You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iceraven-browser/app/src/main/java/org/mozilla/fenix/perf/StartupPathProvider.kt

108 lines
4.4 KiB
Kotlin

/* 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.perf
import android.app.Activity
import android.content.Intent
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.NONE
import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
/**
* The "path" that this activity started in. See the
* [Fenix perf glossary](https://wiki.mozilla.org/index.php?title=Performance/Fenix/Glossary)
* for specific definitions.
*
* This should be a member variable of [Activity] because its data is tied to the lifecycle of an
* Activity. Call [attachOnActivityOnCreate] & [onIntentReceived] for this class to work correctly.
*/
class StartupPathProvider {
/**
* The path the application took to
* [Fenix perf glossary](https://wiki.mozilla.org/index.php?title=Performance/Fenix/Glossary)
* for specific definitions.
*/
enum class StartupPath {
MAIN,
VIEW,
/**
* The start up path if we received an Intent but we're unable to categorize it into other buckets.
*/
UNKNOWN,
/**
* The start up path has not been set. This state includes:
* - this API is accessed before it is set
* - if no intent is received before the activity is STARTED (e.g. app switcher)
*/
NOT_SET
}
/**
* Returns the [StartupPath] for the currently started activity. This value will be set
* after an [Intent] is received that causes this activity to move into the STARTED state.
*/
var startupPathForActivity = StartupPath.NOT_SET
private set
private var wasResumedSinceStartedState = false
fun attachOnActivityOnCreate(lifecycle: Lifecycle, intent: Intent?) {
lifecycle.addObserver(StartupPathLifecycleObserver())
onIntentReceived(intent)
}
// N.B.: this method duplicates the actual logic for determining what page to open.
// Unfortunately, it's difficult to re-use that logic because it occurs in many places throughout
// the code so we do the simple thing for now and duplicate it. It's noticeably different from
// what you might expect: e.g. ACTION_MAIN can open a URL and if ACTION_VIEW provides an invalid
// URL, it'll perform a MAIN action. However, it's fairly representative of what users *intended*
// to do when opening the app and shouldn't change much because it's based on Android system-wide
// conventions, so it's probably fine for our purposes.
private fun getStartupPathFromIntent(intent: Intent): StartupPath = when (intent.action) {
Intent.ACTION_MAIN -> StartupPath.MAIN
Intent.ACTION_VIEW -> StartupPath.VIEW
else -> StartupPath.UNKNOWN
}
/**
* Expected to be called when a new [Intent] is received by the [Activity]: i.e.
* [Activity.onCreate] and [Activity.onNewIntent].
*/
fun onIntentReceived(intent: Intent?) {
// We want to set a path only if the intent causes the Activity to move into the STARTED state.
// This means we want to discard any intents that are received when the app is foregrounded.
// However, we can't use the Lifecycle.currentState to determine this because:
// - the app is briefly paused (state becomes STARTED) before receiving the Intent in
// the foreground so we can't say <= STARTED
// - onIntentReceived can be called from the CREATED or STARTED state so we can't say == CREATED
// So we're forced to track this state ourselves.
if (!wasResumedSinceStartedState && intent != null) {
startupPathForActivity = getStartupPathFromIntent(intent)
}
}
@VisibleForTesting(otherwise = NONE)
fun getTestCallbacks() = StartupPathLifecycleObserver()
@VisibleForTesting(otherwise = PRIVATE)
inner class StartupPathLifecycleObserver : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
wasResumedSinceStartedState = true
}
override fun onStop(owner: LifecycleOwner) {
// Clear existing state.
startupPathForActivity = StartupPath.NOT_SET
wasResumedSinceStartedState = false
}
}
}