1
0
Fork 0

For #7092: Add initial Migration UI

master
Jonathan Almeida 2020-01-15 00:59:08 -05:00 committed by Grisha Kruglov
parent 29d9ba472c
commit b89afe7b7c
14 changed files with 717 additions and 10 deletions

View File

@ -429,10 +429,7 @@ dependencies {
implementation Deps.mozilla_support_utils
implementation Deps.mozilla_support_locale
// We only care about support-migration in builds that will be overwriting Fennec.
fennecProductionImplementation Deps.mozilla_support_migration
fennecBetaImplementation Deps.mozilla_support_migration
fennecNightlyImplementation Deps.mozilla_support_migration
implementation Deps.mozilla_support_migration
implementation Deps.mozilla_ui_colors
implementation Deps.mozilla_ui_icons

View File

@ -0,0 +1,5 @@
History
Bookmarks
Logins
Open Tabs
Settings

View File

@ -52,7 +52,8 @@ class IntentReceiverActivity : Activity() {
)
}
val intentProcessors = components.intentProcessors.externalAppIntentProcessors +
val intentProcessors = listOf(components.intentProcessors.migrationIntentProcessor) +
components.intentProcessors.externalAppIntentProcessors +
modeDependentProcessors +
NewTabShortcutIntentProcessor()

View File

@ -6,6 +6,7 @@ package org.mozilla.fenix.components
import android.content.Context
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.test.Mockable
import org.mozilla.fenix.utils.ClipboardHandler
@ -44,10 +45,12 @@ class Components(private val context: Context) {
useCases.sessionUseCases,
useCases.searchUseCases,
core.client,
core.customTabsStore
core.customTabsStore,
migrationStore
)
}
val analytics by lazy { Analytics(context) }
val publicSuffixList by lazy { PublicSuffixList(context) }
val clipboardHandler by lazy { ClipboardHandler(context) }
val migrationStore by lazy { MigrationStore() }
}

View File

@ -7,10 +7,11 @@ package org.mozilla.fenix.components
import android.content.Intent
import mozilla.components.feature.intent.processing.IntentProcessor
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.MigrationProgressActivity
import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity
enum class IntentProcessorType {
EXTERNAL_APP, NEW_TAB, OTHER;
EXTERNAL_APP, NEW_TAB, MIGRATION, OTHER;
/**
* The destination activity based on this intent
@ -19,6 +20,7 @@ enum class IntentProcessorType {
get() = when (this) {
EXTERNAL_APP -> ExternalAppBrowserActivity::class.java.name
NEW_TAB, OTHER -> HomeActivity::class.java.name
MIGRATION -> MigrationProgressActivity::class.java.name
}
/**
@ -27,7 +29,7 @@ enum class IntentProcessorType {
fun shouldOpenToBrowser(intent: Intent): Boolean = when (this) {
EXTERNAL_APP -> true
NEW_TAB -> intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0
OTHER -> false
MIGRATION, OTHER -> false
}
}
@ -35,6 +37,7 @@ enum class IntentProcessorType {
* Classifies the [IntentProcessorType] based on the [IntentProcessor] that handled the [Intent].
*/
fun IntentProcessors.getType(processor: IntentProcessor?) = when {
migrationIntentProcessor == processor -> IntentProcessorType.MIGRATION
externalAppIntentProcessors.contains(processor) ||
customTabIntentProcessor == processor ||
privateCustomTabIntentProcessor == processor -> IntentProcessorType.EXTERNAL_APP

View File

@ -15,6 +15,8 @@ import mozilla.components.feature.pwa.intent.WebAppIntentProcessor
import mozilla.components.feature.pwa.intent.TrustedWebActivityIntentProcessor
import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.migration.MigrationIntentProcessor
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.home.intent.FennecBookmarkShortcutsIntentProcessor
import org.mozilla.fenix.test.Mockable
@ -29,7 +31,8 @@ class IntentProcessors(
private val sessionUseCases: SessionUseCases,
private val searchUseCases: SearchUseCases,
private val httpClient: Client,
private val customTabsStore: CustomTabsServiceStore
private val customTabsStore: CustomTabsServiceStore,
private val migrationStore: MigrationStore
) {
/**
* Provides intent processing functionality for ACTION_VIEW and ACTION_SEND intents.
@ -67,4 +70,8 @@ class IntentProcessors(
FennecBookmarkShortcutsIntentProcessor(sessionManager, sessionUseCases.loadUrl)
)
}
val migrationIntentProcessor by lazy {
MigrationIntentProcessor(migrationStore)
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="20dp"
android:paddingTop="55dp"
android:paddingEnd="20dp"
android:paddingBottom="67dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/migration_firefox_logo"
android:layout_width="67dp"
android:layout_height="67dp"
android:contentDescription="@string/firefox_logo_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_firefox" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/migration_welcome_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:maxLines="2"
android:text="@string/migration_title"
android:textAppearance="@style/HeaderTextStyle"
android:fontFamily="sans-serif-medium"
android:textColor="@color/button_text_color"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/migration_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/migration_firefox_logo"
app:layout_constraintTop_toTopOf="parent"
tools:text="Welcome to the all-new Firefox Preview" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/migration_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/migration_description"
android:textAppearance="@style/Header16TextStyle"
android:textColor="@color/text_scale_example_text_color"
android:fontFamily="sans-serif-light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/migration_firefox_logo" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/migration_status_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/migration_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/migration_description"
tools:itemCount="5"
tools:listitem="@layout/migration_list_item" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/migration_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:minWidth="200dp"
android:text="@string/migration_updating_app_button_text"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Updating Firefox…" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/migration_status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/migration_icon_description"
android:tint="@color/collection_icon_color_violet"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/mozac_ic_check"
tools:tint="@color/above_dark_theme" />
<TextView
android:id="@+id/migration_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="32dp"
android:fontFamily="sans-serif-medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/migration_status_image"
app:layout_constraintTop_toTopOf="parent"
tools:text="@sample/migration_items" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1117,4 +1117,17 @@
<string name="search_edit_custom_engine_success_message">Saved %s</string>
<!-- Text shown when a user successfully deletes a custom search engine -->
<string name="search_delete_search_engine_success_message">Deleted %s</string>
<!-- Description text for the Firefox brand logo -->
<string name="firefox_logo_description">Firefox logo</string>
<!-- Title text shown for the migration screen to the new browser -->
<string name="migration_title">Welcome to an all-new Firefox</string>
<!-- Description text followed by a list of things migrating (e.g. Bookmarks, History) -->
<string name="migration_description">A completely redesigned browser awaits, with improved performance and features to help you do more online.\n\nPlease wait while we update Firefox with your</string>
<!-- Text on the disabled button while in progress -->
<string name="migration_updating_app_button_text">Updating %s…</string>
<!-- Text on the enabled button -->
<string name="migration_update_app_button">Start %s</string>
<!-- Accessibility description text for completed migration item (e.g. Bookmarks, History) -->
<string name="migration_icon_description">Migration status: %s</string>
</resources>

View File

@ -13,6 +13,11 @@
android:name="org.mozilla.fenix.MigratingFenixApplication"
tools:replace="android:name">
<service android:name="org.mozilla.fenix.MigrationService" />
<activity
android:name="org.mozilla.fenix.MigrationProgressActivity"
android:exported="false">
</activity>
</application>
</manifest>

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.runBlocking
import mozilla.components.support.migration.FennecMigrator
import mozilla.components.support.migration.state.MigrationStore
@ -47,6 +48,10 @@ class MigratingFenixApplication : FenixApplication() {
// The rest of the migrations can happen now.
migrationPushSubscriber.start()
migrator.startMigrationIfNeeded(migrationStore, MigrationService::class.java)
// Start migration UI
val intent = Intent(this, MigrationProgressActivity::class.java)
startActivity(intent)
}
private fun migrateBlocking() {

View File

@ -0,0 +1,148 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_migration.*
import kotlinx.android.synthetic.main.migration_list_item.view.*
import mozilla.components.support.migration.AbstractMigrationProgressActivity
import mozilla.components.support.migration.Migration
import mozilla.components.support.migration.Migration.Bookmarks
import mozilla.components.support.migration.Migration.History
import mozilla.components.support.migration.Migration.Logins
import mozilla.components.support.migration.Migration.Settings
import mozilla.components.support.migration.MigrationResults
import mozilla.components.support.migration.state.MigrationProgress
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.ext.components
class MigrationProgressActivity : AbstractMigrationProgressActivity() {
private val statusAdapter = MigrationStatusAdapter()
override val store: MigrationStore by lazy { components.migrationStore }
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_migration)
init()
super.onCreate(savedInstanceState)
}
fun init() {
migration_status_list.apply {
layoutManager = LinearLayoutManager(this@MigrationProgressActivity)
adapter = statusAdapter
}
migration_button.apply {
setOnClickListener {
finish()
overridePendingTransition(0, 0)
// If we received a user-initiated intent, throw this back to the intent receiver.
if (intent.hasExtra(HomeActivity.OPEN_TO_BROWSER)) {
intent.setClassName(applicationContext, IntentReceiverActivity::class.java.name)
startActivity(intent)
}
}
text = getString(
R.string.migration_updating_app_button_text,
getString(R.string.app_name)
)
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
// Enables sticky immersive mode.
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
}
override fun onMigrationCompleted() {
// Enable clicking the finish button
migration_button.apply {
isEnabled = true
text = getString(R.string.migration_update_app_button, getString(R.string.app_name))
setBackgroundColor(ContextCompat.getColor(context, R.color.button_text_color))
setTextColor(ContextCompat.getColor(context, R.color.white_color))
}
}
override fun onMigrationStateChanged(progress: MigrationProgress, results: MigrationResults) {
statusAdapter.submitList(results.toItemList())
}
}
// These are the only items we want to show migrating in the UI.
internal val whiteList = mapOf(
Bookmarks to R.string.preferences_sync_bookmarks,
History to R.string.preferences_sync_history,
Logins to R.string.preferences_sync_logins,
Settings to R.string.settings_title
)
internal fun MigrationResults.toItemList() = filterKeys {
whiteList.keys.contains(it)
}.map { (type, status) ->
MigrationItem(
type,
status.success
)
}
internal data class MigrationItem(val migration: Migration, val status: Boolean)
internal class MigrationStatusAdapter :
ListAdapter<MigrationItem, MigrationStatusAdapter.ViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
override fun getItemViewType(position: Int): Int = R.layout.migration_list_item
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val context = view.context
private val title = view.migration_item_name
private val status = view.migration_status_image
fun bind(item: MigrationItem) {
// Get the resource ID for the item.
val migrationText = whiteList[item.migration]?.run {
context.getString(this)
} ?: ""
title.text = migrationText
status.visibility = if (item.status) View.VISIBLE else View.INVISIBLE
status.contentDescription =
context.getString(R.string.migration_icon_description, migrationText)
}
}
private object DiffCallback : DiffUtil.ItemCallback<MigrationItem>() {
override fun areItemsTheSame(oldItem: MigrationItem, newItem: MigrationItem) =
oldItem.migration.javaClass.simpleName == newItem.migration.javaClass.simpleName
override fun areContentsTheSame(oldItem: MigrationItem, newItem: MigrationItem) =
oldItem.migration.javaClass.simpleName == newItem.migration.javaClass.simpleName &&
oldItem.status == newItem.status
}
}

View File

@ -6,11 +6,12 @@ package org.mozilla.fenix
import mozilla.components.support.migration.AbstractMigrationService
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.ext.components
/**
* Background service for running the migration from legacy Firefox for Android (Fennec).
*/
class MigrationService : AbstractMigrationService() {
override val migrator by lazy { getMigratorFromApplication() }
override val store: MigrationStore by lazy { getMigrationStoreFromApplication() }
override val store: MigrationStore by lazy { components.migrationStore }
}