Issue #22489: Remove "Fennec to Fenix" migration code
parent
b6932dd590
commit
834d0ee12d
@ -1,5 +0,0 @@
|
||||
History
|
||||
Bookmarks
|
||||
Logins
|
||||
Open Tabs
|
||||
Settings
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
sharedUserId: This release type is meant to replace Firefox (Beta channel) and therefore needs to inherit
|
||||
its sharedUserId for all eternity. Shipping an app update without sharedUserId can have
|
||||
fatal consequences. For example see:
|
||||
- https://issuetracker.google.com/issues/36924841
|
||||
- https://issuetracker.google.com/issues/36905922
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:sharedUserId="${sharedUserId}">
|
||||
</manifest>
|
@ -1,105 +0,0 @@
|
||||
/* 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.migration
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.migration.AbstractMigrationProgressActivity
|
||||
import mozilla.components.support.migration.AbstractMigrationService
|
||||
import mozilla.components.support.migration.MigrationResults
|
||||
import mozilla.components.support.migration.state.MigrationAction
|
||||
import mozilla.components.support.migration.state.MigrationProgress
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.ActivityMigrationBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
class MigrationProgressActivity : AbstractMigrationProgressActivity() {
|
||||
private val logger = Logger("MigrationProgressActivity")
|
||||
private val statusAdapter = MigrationStatusAdapter()
|
||||
override val store: MigrationStore by lazy { components.migrationStore }
|
||||
|
||||
private lateinit var binding: ActivityMigrationBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMigrationBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
fun init() {
|
||||
window.navigationBarColor = getColorFromAttr(R.attr.layer1)
|
||||
|
||||
val appName = binding.migrationDescription.context.getString(R.string.app_name)
|
||||
|
||||
binding.migrationDescription.apply {
|
||||
text = context.getString(R.string.migration_description, appName)
|
||||
}
|
||||
|
||||
binding.migrationStatusList.apply {
|
||||
val margin = resources.getDimensionPixelSize(R.dimen.migration_margin)
|
||||
addItemDecoration(MigrationStatusItemDecoration(margin))
|
||||
layoutManager = LinearLayoutManager(this@MigrationProgressActivity)
|
||||
adapter = statusAdapter
|
||||
}
|
||||
|
||||
binding.migrationWelcomeTitle.apply {
|
||||
text = context.getString(R.string.migration_title, appName)
|
||||
}
|
||||
|
||||
binding.migrationButtonTextView.text = getString(R.string.migration_updating_app_button_text, appName)
|
||||
}
|
||||
|
||||
override fun onMigrationCompleted(results: MigrationResults) {
|
||||
// Enable clicking the finish button
|
||||
binding.migrationButton.apply {
|
||||
setOnClickListener {
|
||||
AbstractMigrationService.dismissNotification(context)
|
||||
|
||||
finish()
|
||||
overridePendingTransition(0, 0)
|
||||
|
||||
store.dispatch(MigrationAction.Clear)
|
||||
|
||||
// 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)
|
||||
} else {
|
||||
// Fallback: Just launch the browser
|
||||
logger.warn("Intent does not contain OPEN_TO_BROWSER extra, launching HomeActivity")
|
||||
startActivity(Intent(this@MigrationProgressActivity, HomeActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.migrationButtonTextView.apply {
|
||||
text = getString(R.string.migration_update_app_button, getString(R.string.app_name))
|
||||
setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.fx_mobile_text_color_oncolor_primary
|
||||
)
|
||||
)
|
||||
}
|
||||
binding.migrationButton.setBackgroundResource(R.drawable.migration_button_background)
|
||||
binding.migrationButtonProgressBar.visibility = View.INVISIBLE
|
||||
// Keep the results list up-to-date.
|
||||
statusAdapter.updateData(results)
|
||||
}
|
||||
|
||||
override fun onMigrationStateChanged(progress: MigrationProgress, results: MigrationResults) {
|
||||
statusAdapter.updateData(results)
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/* 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.migration
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.support.migration.Migration
|
||||
import mozilla.components.support.migration.MigrationResults
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.MigrationListItemBinding
|
||||
|
||||
internal data class MigrationItem(
|
||||
val migration: Migration,
|
||||
val status: Boolean = false
|
||||
)
|
||||
|
||||
// These are the only items we want to show migrating in the UI.
|
||||
internal val whiteList = linkedMapOf(
|
||||
Migration.Settings to R.string.settings_title,
|
||||
Migration.History to R.string.preferences_sync_history,
|
||||
Migration.Bookmarks to R.string.preferences_sync_bookmarks,
|
||||
Migration.Logins to R.string.migration_text_passwords
|
||||
)
|
||||
|
||||
internal class MigrationStatusAdapter :
|
||||
ListAdapter<MigrationItem, MigrationStatusAdapter.ViewHolder>(DiffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.migration_list_item, parent, false)
|
||||
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the [results] to only include items in [whiteList] and update the adapter.
|
||||
*/
|
||||
fun updateData(results: MigrationResults) {
|
||||
val itemList = whiteList.keys.map {
|
||||
if (results.containsKey(it)) {
|
||||
MigrationItem(it, results.getValue(it).success)
|
||||
} else {
|
||||
MigrationItem(it)
|
||||
}
|
||||
}
|
||||
submitList(itemList)
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val context = view.context
|
||||
private val binding = MigrationListItemBinding.bind(view)
|
||||
private val title = binding.migrationItemName
|
||||
private val status = binding.migrationStatusImage
|
||||
|
||||
fun bind(item: MigrationItem) {
|
||||
// Get the resource ID for the item.
|
||||
val migrationText = whiteList[item.migration]?.let {
|
||||
context.getString(it)
|
||||
}.orEmpty()
|
||||
title.text = migrationText
|
||||
status.isInvisible = !item.status
|
||||
status.contentDescription = context.getString(R.string.migration_icon_description)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
internal class MigrationStatusItemDecoration(
|
||||
@Px private val spacing: Int
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
val position = parent.getChildViewHolder(view).bindingAdapterPosition
|
||||
val itemCount = state.itemCount
|
||||
|
||||
outRect.left = spacing
|
||||
outRect.right = spacing
|
||||
outRect.top = spacing
|
||||
outRect.bottom = if (position == itemCount - 1) spacing else 0
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* 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.migration
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import mozilla.components.lib.state.ext.flowScoped
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import mozilla.components.support.migration.state.MigrationProgress
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class MigrationTelemetryListener(
|
||||
private val metrics: MetricController,
|
||||
private val store: MigrationStore,
|
||||
private val logger: Logger = Logger("MigrationTelemetryListener")
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun start() {
|
||||
// Observe for migration completed.
|
||||
store.flowScoped { flow ->
|
||||
flow.collect { state ->
|
||||
logger.debug("Migration state: ${state.progress}")
|
||||
if (state.progress == MigrationProgress.COMPLETED) {
|
||||
metrics.track(Event.FennecToFenixMigrated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<translate
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="0"
|
||||
android:duration="900" />
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="4dp"/>
|
||||
<solid android:color="@color/fx_mobile_action_color_secondary" />
|
||||
</shape>
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="4dp"/>
|
||||
<solid android:color="#312A65" />
|
||||
</shape>
|
@ -1,132 +0,0 @@
|
||||
<?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:background="@color/photonLightGrey05">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="@dimen/migration_progress_margin"
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintBottom_toTopOf="@+id/migration_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<!-- MozMultipleConstraintLayouts: we're not changing the migration code. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:ignore="MozMultipleConstraintLayouts">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/migration_firefox_logo"
|
||||
android:layout_width="@dimen/migration_firefox_logo_size"
|
||||
android:layout_height="@dimen/migration_firefox_logo_size"
|
||||
android:layout_marginStart="@dimen/migration_margin_horizontal_large"
|
||||
android:fontFamily="@font/metropolis_bold"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/migration_welcome_title"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/migration_welcome_title"
|
||||
app:layout_constraintTop_toTopOf="@+id/migration_welcome_title"
|
||||
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="@dimen/migration_margin"
|
||||
android:layout_marginTop="@dimen/migration_margin"
|
||||
android:layout_marginEnd="@dimen/migration_margin_horizontal_large"
|
||||
android:fontFamily="@font/metropolis_bold"
|
||||
android:maxLines="2"
|
||||
android:lineHeight="24sp"
|
||||
android:text="@string/migration_title"
|
||||
android:textColor="@color/fx_mobile_text_color_action_secondary"
|
||||
android:textSize="@dimen/migration_welcome_title_text_size"
|
||||
android:textAppearance="@style/Header16TextStyle"
|
||||
app:layout_constraintBottom_toTopOf="@+id/migration_description"
|
||||
app:layout_constraintLeft_toRightOf="@id/migration_firefox_logo"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
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_marginLeft="@dimen/migration_margin_horizontal_large"
|
||||
android:layout_marginTop="@dimen/migration_margin"
|
||||
android:layout_marginRight="@dimen/migration_margin_horizontal_large"
|
||||
android:lineHeight="24sp"
|
||||
android:text="@string/migration_description"
|
||||
android:textColor="@color/primary_text_light_theme"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintBottom_toTopOf="@+id/migration_status_list"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/migration_welcome_title" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/migration_status_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/migration_margin_horizontal_large"
|
||||
android:layout_marginTop="@dimen/migration_margin"
|
||||
android:layout_marginRight="@dimen/migration_margin_horizontal_large"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.45"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/migration_description"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/migration_list_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/migration_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/migration_margin"
|
||||
android:background="@drawable/button_background_grey"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/migration_button_progress_bar"
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
android:layout_width="@dimen/migration_progress_size"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/migration_margin"
|
||||
android:layout_marginEnd="@dimen/migration_progress_margin_start" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/migration_button_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/migration_progress_margin_compound"
|
||||
android:background="@android:color/transparent"
|
||||
android:text="@string/migration_updating_app_button_text"
|
||||
android:textSize="@dimen/migration_button_text_size"
|
||||
android:textAppearance="@style/NeutralButton"
|
||||
tools:text="Updating Firefox…" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,36 +0,0 @@
|
||||
<?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"
|
||||
app:tint="@color/photonViolet60"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/mozac_ic_check"
|
||||
tools:tint="@color/photonDarkGrey50" />
|
||||
|
||||
<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:textColor="@color/primary_text_light_theme"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
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>
|
@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
sharedUserId: This release type is meant to replace Firefox (Release channel) and therefore needs to inherit
|
||||
its sharedUserId for all eternity. Shipping an app update without sharedUserId can have
|
||||
fatal consequences. For example see:
|
||||
- https://issuetracker.google.com/issues/36924841
|
||||
- https://issuetracker.google.com/issues/36905922
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:sharedUserId="${sharedUserId}">
|
||||
<application
|
||||
android:name="org.mozilla.fenix.MigratingFenixApplication"
|
||||
tools:replace="android:name">
|
||||
|
||||
<!-- Overriding the alias of the main manifest to route app launches through our
|
||||
MigrationDecisionActivity which will show the migration screen before launching
|
||||
into the app if needed. -->
|
||||
<activity-alias
|
||||
android:name="${applicationId}.App"
|
||||
android:targetActivity="org.mozilla.fenix.MigrationDecisionActivity"
|
||||
tools:replace="android:targetActivity" />
|
||||
|
||||
<activity
|
||||
android:name="org.mozilla.fenix.MigrationDecisionActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name="org.mozilla.fenix.MigrationService" />
|
||||
</application>
|
||||
</manifest>
|
@ -1,93 +0,0 @@
|
||||
/* 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.content.Context
|
||||
import mozilla.components.support.migration.FennecMigrator
|
||||
import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
|
||||
import org.mozilla.fenix.migration.MigrationTelemetryListener
|
||||
import org.mozilla.fenix.perf.runBlockingIncrement
|
||||
|
||||
/**
|
||||
* An application class which knows how to migrate Fennec data.
|
||||
*/
|
||||
class MigratingFenixApplication : FenixApplication() {
|
||||
init {
|
||||
recordOnInit() // DO NOT MOVE ANYTHING ABOVE HERE: the timing of this measurement is critical.
|
||||
|
||||
PerformanceActivityLifecycleCallbacks.isTransientActivityInMigrationVariant = {
|
||||
if (it is MigrationDecisionActivity) true else false
|
||||
}
|
||||
}
|
||||
|
||||
val fxaExpectChinaServers = Config.channel.isMozillaOnline
|
||||
|
||||
val migrator by lazy {
|
||||
FennecMigrator.Builder(this, this.components.analytics.crashReporter)
|
||||
.migrateOpenTabs(this.components.useCases.tabsUseCases)
|
||||
.migrateHistory(this.components.core.lazyHistoryStorage)
|
||||
.migrateBookmarks(
|
||||
this.components.core.lazyBookmarksStorage,
|
||||
this.components.core.pinnedSiteStorage
|
||||
)
|
||||
.migrateLogins(this.components.core.lazyPasswordsStorage)
|
||||
.migrateFxa(lazy { this.components.backgroundServices.accountManager }, fxaExpectChinaServers)
|
||||
.migrateAddons(
|
||||
this.components.core.engine,
|
||||
this.components.addonCollectionProvider,
|
||||
this.components.addonUpdater
|
||||
)
|
||||
.migrateTelemetryIdentifiers()
|
||||
.build()
|
||||
}
|
||||
|
||||
val migrationPushSubscriber by lazy {
|
||||
MigrationPushRenewer(
|
||||
components.push.feature,
|
||||
components.migrationStore
|
||||
)
|
||||
}
|
||||
|
||||
val migrationTelemetryListener by lazy {
|
||||
MigrationTelemetryListener(
|
||||
components.analytics.metrics,
|
||||
components.migrationStore
|
||||
)
|
||||
}
|
||||
|
||||
override fun setupInMainProcessOnly() {
|
||||
// These migrations need to run before regular initialization happens.
|
||||
migrateBlocking()
|
||||
|
||||
// Now that we have migrated from Fennec whether the user wants to enable telemetry we can
|
||||
// initialize Glean
|
||||
initializeGlean()
|
||||
|
||||
// Fenix application initialization can happen now.
|
||||
super.setupInMainProcessOnly()
|
||||
|
||||
// The rest of the migrations can happen now.
|
||||
migrationPushSubscriber.start()
|
||||
migrationTelemetryListener.start()
|
||||
migrator.startMigrationIfNeeded(components.migrationStore, MigrationService::class.java)
|
||||
}
|
||||
|
||||
private fun migrateBlocking() {
|
||||
val migrator = FennecMigrator.Builder(this, this.components.analytics.crashReporter)
|
||||
.migrateGecko()
|
||||
// Telemetry may have been disabled in Fennec, so we need to migrate Settings first
|
||||
// to correctly initialize telemetry.
|
||||
.migrateSettings()
|
||||
.build()
|
||||
|
||||
runBlockingIncrement {
|
||||
migrator.migrateAsync(components.migrationStore).await()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getMigratorFromApplication(): FennecMigrator {
|
||||
return (applicationContext as MigratingFenixApplication).migrator
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/* 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.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import mozilla.components.support.migration.state.MigrationProgress
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.migration.MigrationProgressActivity
|
||||
|
||||
/**
|
||||
* The purpose of this activity, when launched, is to decide whether we want to show the migration
|
||||
* screen ([MigrationProgressActivity]) or launch the browser normally ([HomeActivity]).
|
||||
*/
|
||||
class MigrationDecisionActivity : Activity() {
|
||||
private val store by lazy { components.migrationStore }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val intent = if (intent != null) intent else Intent()
|
||||
|
||||
val activity = when (store.state.progress) {
|
||||
MigrationProgress.NONE, MigrationProgress.COMPLETED -> HomeActivity::class.java
|
||||
MigrationProgress.MIGRATING -> MigrationProgressActivity::class.java
|
||||
}
|
||||
|
||||
intent.setClass(applicationContext, activity)
|
||||
intent.putExtra(HomeActivity.OPEN_TO_BROWSER, false)
|
||||
|
||||
startActivity(intent)
|
||||
finish()
|
||||
|
||||
// We are disabling animations here when switching activities because this results in a
|
||||
// perceived faster launch. This activity will start immediately with a solid background
|
||||
// and then we switch to the actual activity without an animation. This visually looks like
|
||||
// a faster start than launching this activity invisibly and switching to the actual
|
||||
// activity after that.
|
||||
overridePendingTransition(0, R.anim.placeholder_animation)
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/* 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 kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import mozilla.components.concept.push.PushProcessor
|
||||
import mozilla.components.lib.state.ext.flowScoped
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import mozilla.components.support.migration.state.MigrationProgress
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
|
||||
/**
|
||||
* Force-renews push subscription after migration was complete.
|
||||
*/
|
||||
class MigrationPushRenewer(
|
||||
private val service: PushProcessor?,
|
||||
private val store: MigrationStore
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun start() {
|
||||
// Observe for migration completed.
|
||||
store.flowScoped { flow ->
|
||||
flow.collect { state ->
|
||||
Logger("MigrationPushRenewer").debug("Migration state: ${state.progress}")
|
||||
if (state.progress == MigrationProgress.COMPLETED) {
|
||||
Logger("MigrationPushRenewer").debug("Renewing registration....")
|
||||
|
||||
// This should force a recreation of firebase device token, re-registration with
|
||||
// the autopush service, and subsequent update of the FxA device record with
|
||||
// new push subscription information.
|
||||
service?.renewRegistration()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
/* 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 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 { components.migrationStore }
|
||||
override val migrationDecisionActivity = MigrationDecisionActivity::class.java
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
sharedUserId: This release type is meant to replace Firefox (Release channel) and therefore needs to inherit
|
||||
its sharedUserId for all eternity. Shipping an app update without sharedUserId can have
|
||||
fatal consequences. For example see:
|
||||
- https://issuetracker.google.com/issues/36924841
|
||||
- https://issuetracker.google.com/issues/36905922
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:sharedUserId="${sharedUserId}">
|
||||
</manifest>
|
@ -1,65 +0,0 @@
|
||||
/* 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.migration
|
||||
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import mozilla.components.support.migration.Migration
|
||||
import mozilla.components.support.migration.MigrationRun
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.databinding.MigrationListItemBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class MigrationStatusAdapterTest {
|
||||
|
||||
private lateinit var adapter: MigrationStatusAdapter
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
adapter = MigrationStatusAdapter()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getItemCount should return the number of items in whitelist`() {
|
||||
assertEquals(0, adapter.itemCount)
|
||||
|
||||
adapter.updateData(
|
||||
mapOf(
|
||||
Migration.Addons to MigrationRun(0, success = true),
|
||||
Migration.Settings to MigrationRun(0, success = true),
|
||||
Migration.Bookmarks to MigrationRun(0, success = false)
|
||||
)
|
||||
)
|
||||
assertEquals(4, adapter.itemCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `creates and binds viewholder`() {
|
||||
adapter.updateData(
|
||||
mapOf(
|
||||
Migration.History to MigrationRun(0, success = true)
|
||||
)
|
||||
)
|
||||
|
||||
val holder1 = adapter.createViewHolder(FrameLayout(testContext), 0)
|
||||
val holder2 = adapter.createViewHolder(FrameLayout(testContext), 0)
|
||||
val binding1 = MigrationListItemBinding.bind(holder1.itemView)
|
||||
val binding2 = MigrationListItemBinding.bind(holder2.itemView)
|
||||
adapter.bindViewHolder(holder1, 0)
|
||||
adapter.bindViewHolder(holder2, 1)
|
||||
|
||||
assertEquals("Settings", binding1.migrationItemName.text)
|
||||
assertEquals(View.INVISIBLE, binding1.migrationStatusImage.visibility)
|
||||
|
||||
assertEquals("History", binding2.migrationItemName.text)
|
||||
assertEquals(View.VISIBLE, binding2.migrationStatusImage.visibility)
|
||||
assertEquals("Migration completed", binding2.migrationStatusImage.contentDescription)
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/* 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.migration
|
||||
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import mozilla.components.support.migration.state.MigrationAction
|
||||
import mozilla.components.support.migration.state.MigrationStore
|
||||
import mozilla.components.support.test.ext.joinBlocking
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class MigrationTelemetryListenerTest {
|
||||
|
||||
private val testDispatcher = TestCoroutineDispatcher()
|
||||
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
|
||||
|
||||
@MockK(relaxed = true) private lateinit var metrics: MetricController
|
||||
@MockK(relaxed = true) private lateinit var logger: Logger
|
||||
private lateinit var store: MigrationStore
|
||||
private lateinit var listener: MigrationTelemetryListener
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
store = MigrationStore()
|
||||
listener = MigrationTelemetryListener(
|
||||
metrics = metrics,
|
||||
store = store,
|
||||
logger = logger
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
testDispatcher.cleanupTestCoroutines()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `progress state is logged`() = testDispatcher.runBlockingTest {
|
||||
listener.start()
|
||||
store.dispatch(MigrationAction.Started).joinBlocking()
|
||||
store.dispatch(MigrationAction.Completed).joinBlocking()
|
||||
store.dispatch(MigrationAction.Clear).joinBlocking()
|
||||
|
||||
verifyOrder {
|
||||
logger.debug("Migration state: MIGRATING")
|
||||
logger.debug("Migration state: COMPLETED")
|
||||
logger.debug("Migration state: NONE")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `metrics are logged when migration is completed`() = testDispatcher.runBlockingTest {
|
||||
listener.start()
|
||||
store.dispatch(MigrationAction.Completed).joinBlocking()
|
||||
|
||||
verify { metrics.track(Event.FennecToFenixMigrated) }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue