2019-01-09 22:22:58 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2019-07-12 18:38:15 +00:00
|
|
|
* 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/. */
|
2019-01-09 22:22:58 +00:00
|
|
|
|
2018-12-07 20:11:50 +00:00
|
|
|
package org.mozilla.fenix
|
|
|
|
|
2019-01-18 23:33:40 +00:00
|
|
|
import android.content.Context
|
2019-03-04 22:32:10 +00:00
|
|
|
import android.content.Intent
|
2020-08-26 11:43:20 +00:00
|
|
|
import android.content.res.Configuration
|
2020-07-14 23:20:42 +00:00
|
|
|
import android.os.Build
|
2018-12-07 20:11:50 +00:00
|
|
|
import android.os.Bundle
|
2020-06-08 17:45:33 +00:00
|
|
|
import android.os.StrictMode
|
2020-08-28 05:57:39 +00:00
|
|
|
import android.os.SystemClock
|
2020-06-26 00:35:52 +00:00
|
|
|
import android.text.format.DateUtils
|
2019-01-18 23:33:40 +00:00
|
|
|
import android.util.AttributeSet
|
2020-07-14 23:20:42 +00:00
|
|
|
import android.view.KeyEvent
|
16373 Count the # of inflations done on startup (#16778)
* For #16373: Added performance Inflater to counter # of inflations
This class is quite straight forward. The only thing that I have to point out is the onCreateView method. It usually
calls its super if you don't override it. The problem with that is that the super.onCreateView actually uses
android.view. as a prefix for the XML element it tries to inflate. So if we have an element that isn't part
of that package, it'll crash. As I said in the code, a good example is ImageButton. Calling android.view.ImageButton
will make the app crash. The method is implemented the same way that PhoneLayoutInflater does (Another example
is the AsyncLayoutInflater)
* For #16373: Added test for PerformanceInflater
This test got quite awkward / complicated fast. I wanted to test the to make sure we don't break *any* of our layouts
and to do so, I decided to just retrieve all our XML in our /res/layout folder. However, this gets quite a bit outside of a unit test scope.
The point was to get every layouts and get their LayoutID through the resources using the testContext we have. It gets even weirder, since some
of the XML tags have special implementation in android. One of them is the <fragment> tag. That tag actually is inflated by the OS using the Factory2
that the Activity.java implements. In order to get around the fragment issue, we just return a basic FrameLayout since the system LayoutInflater doesn't deal
won't ever get a <fragment> tag to inflate. Another issue was the <merge> tag. In order to inflate those, you need 1) a root view and 2) attach your view to it.
In order to be able to test those layouts file, I had to create an empty FrameLayout and use it as the root view for testing. Again, I know this is beyond the spirit of a unit test but if we use this inflater, I think it should make sure that no layouts are broken by it.
* For #16373: Overrode getSystemService to return PerformanceInflater
This allows PerformanceInflater to be called in every inflation to keep track of the number of inflations we do.
* For #16373: Added UI test for # of inflations
* For #16373: Lint fix
* For #167373: Changed the LayoutInflater cloneInContext to take this instead of inflater
The inflater parameter is set on the first call from the OS from the Window object. However, the activity itself sets multiple factories on the inflater
during its creation (usually through AppCompatDelegateImpl.java). This means that, once we initially set the inflater with a null check, we pass an inflater
that has no factory initially. However, since we keep a reference to it, when cloneInContext was called, it cloned the inflater with the original inflater
which didn't have any factories set up. This meant that the app would crash on either browserFragment creation or any thing that required appCompat (such as
ImageView and ImageButton). Now, passing itself with a cloneInContext means we keep all the factories initially set by the activity or the fragment.
* For #16373: Fixed code issues for PR. No behavior change
* For #16373: fixed some code nits
2021-01-04 16:00:34 +00:00
|
|
|
import android.view.LayoutInflater
|
2019-01-18 23:33:40 +00:00
|
|
|
import android.view.View
|
2021-03-23 09:46:10 +00:00
|
|
|
import android.view.ActionMode
|
2020-07-14 23:20:42 +00:00
|
|
|
import android.view.ViewConfiguration
|
2021-02-04 23:42:05 +00:00
|
|
|
import android.view.WindowManager.LayoutParams.FLAG_SECURE
|
2019-08-20 17:00:49 +00:00
|
|
|
import androidx.annotation.CallSuper
|
2019-06-06 19:40:10 +00:00
|
|
|
import androidx.annotation.IdRes
|
2019-08-20 17:00:49 +00:00
|
|
|
import androidx.annotation.VisibleForTesting
|
|
|
|
import androidx.annotation.VisibleForTesting.PROTECTED
|
2020-01-16 18:03:18 +00:00
|
|
|
import androidx.appcompat.app.ActionBar
|
2019-02-07 23:37:52 +00:00
|
|
|
import androidx.appcompat.widget.Toolbar
|
2019-06-13 00:13:24 +00:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2019-08-20 17:00:49 +00:00
|
|
|
import androidx.navigation.NavDestination
|
2019-10-25 21:25:09 +00:00
|
|
|
import androidx.navigation.NavDirections
|
2019-02-06 22:31:39 +00:00
|
|
|
import androidx.navigation.fragment.NavHostFragment
|
2019-02-07 23:37:52 +00:00
|
|
|
import androidx.navigation.ui.AppBarConfiguration
|
|
|
|
import androidx.navigation.ui.NavigationUI
|
2020-01-23 08:24:48 +00:00
|
|
|
import kotlinx.android.synthetic.main.activity_home.*
|
2020-02-04 06:41:52 +00:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
2021-04-14 00:48:45 +00:00
|
|
|
import kotlinx.coroutines.delay
|
2020-06-26 00:35:52 +00:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
2020-07-16 01:02:58 +00:00
|
|
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
2020-07-14 23:20:42 +00:00
|
|
|
import kotlinx.coroutines.Job
|
2019-05-28 22:26:36 +00:00
|
|
|
import kotlinx.coroutines.launch
|
2021-04-14 00:48:45 +00:00
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
2020-12-15 16:30:24 +00:00
|
|
|
import mozilla.appservices.places.BookmarkRoot
|
2021-03-30 20:12:46 +00:00
|
|
|
import mozilla.components.browser.state.action.ContentAction
|
2020-11-09 11:16:48 +00:00
|
|
|
import mozilla.components.browser.state.search.SearchEngine
|
2020-10-02 22:39:03 +00:00
|
|
|
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
|
2020-07-16 20:34:50 +00:00
|
|
|
import mozilla.components.browser.state.state.SessionState
|
2020-02-04 06:41:52 +00:00
|
|
|
import mozilla.components.browser.state.state.WebExtensionState
|
2020-08-05 08:53:45 +00:00
|
|
|
import mozilla.components.concept.engine.EngineSession
|
2019-01-18 23:33:40 +00:00
|
|
|
import mozilla.components.concept.engine.EngineView
|
2020-12-15 16:30:24 +00:00
|
|
|
import mozilla.components.concept.storage.BookmarkNode
|
|
|
|
import mozilla.components.concept.storage.BookmarkNodeType
|
2020-06-10 17:25:53 +00:00
|
|
|
import mozilla.components.feature.contextmenu.DefaultSelectionActionDelegate
|
2020-08-14 23:08:41 +00:00
|
|
|
import mozilla.components.feature.privatemode.notification.PrivateNotificationFeature
|
2020-06-10 17:25:53 +00:00
|
|
|
import mozilla.components.feature.search.BrowserStoreSearchAdapter
|
2019-08-22 23:56:13 +00:00
|
|
|
import mozilla.components.service.fxa.sync.SyncReason
|
2021-02-01 10:52:56 +00:00
|
|
|
import mozilla.components.support.base.feature.ActivityResultHandler
|
2019-11-27 23:02:47 +00:00
|
|
|
import mozilla.components.support.base.feature.UserInteractionHandler
|
2020-04-07 21:41:57 +00:00
|
|
|
import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
|
2020-07-10 18:27:28 +00:00
|
|
|
import mozilla.components.support.ktx.android.content.call
|
|
|
|
import mozilla.components.support.ktx.android.content.email
|
2020-02-25 18:22:50 +00:00
|
|
|
import mozilla.components.support.ktx.android.content.share
|
2019-02-28 17:25:37 +00:00
|
|
|
import mozilla.components.support.ktx.kotlin.isUrl
|
|
|
|
import mozilla.components.support.ktx.kotlin.toNormalizedUrl
|
2020-01-04 02:15:35 +00:00
|
|
|
import mozilla.components.support.locale.LocaleAwareAppCompatActivity
|
2019-02-06 22:31:39 +00:00
|
|
|
import mozilla.components.support.utils.SafeIntent
|
2019-08-20 17:00:49 +00:00
|
|
|
import mozilla.components.support.utils.toSafeIntent
|
2020-02-04 06:41:52 +00:00
|
|
|
import mozilla.components.support.webextensions.WebExtensionPopupFeature
|
2020-06-26 00:35:52 +00:00
|
|
|
import org.mozilla.fenix.GleanMetrics.Metrics
|
2021-02-11 23:19:26 +00:00
|
|
|
import org.mozilla.fenix.GleanMetrics.PerfStartup
|
2020-07-07 17:31:05 +00:00
|
|
|
import org.mozilla.fenix.addons.AddonDetailsFragmentDirections
|
2020-07-13 13:05:28 +00:00
|
|
|
import org.mozilla.fenix.addons.AddonPermissionsDetailsFragmentDirections
|
2019-08-21 15:33:59 +00:00
|
|
|
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
2020-02-23 05:46:50 +00:00
|
|
|
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
|
|
|
import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager
|
2019-09-03 21:47:43 +00:00
|
|
|
import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder
|
2019-09-18 16:47:30 +00:00
|
|
|
import org.mozilla.fenix.components.metrics.Event
|
2020-07-31 20:24:14 +00:00
|
|
|
import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExceptionsFragmentDirections
|
2020-06-04 21:41:50 +00:00
|
|
|
import org.mozilla.fenix.ext.alreadyOnDestination
|
2020-08-26 11:43:20 +00:00
|
|
|
import org.mozilla.fenix.ext.breadcrumb
|
2020-06-04 21:41:50 +00:00
|
|
|
import org.mozilla.fenix.ext.components
|
2021-04-14 00:48:45 +00:00
|
|
|
import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph
|
2021-03-17 17:35:50 +00:00
|
|
|
import org.mozilla.fenix.ext.measureNoInline
|
2020-07-27 21:24:02 +00:00
|
|
|
import org.mozilla.fenix.ext.metrics
|
2019-06-06 19:40:10 +00:00
|
|
|
import org.mozilla.fenix.ext.nav
|
2021-05-10 15:41:51 +00:00
|
|
|
import org.mozilla.fenix.ext.setNavigationIcon
|
2020-07-06 20:04:43 +00:00
|
|
|
import org.mozilla.fenix.ext.settings
|
2019-08-21 23:29:45 +00:00
|
|
|
import org.mozilla.fenix.home.HomeFragmentDirections
|
2020-06-04 21:41:50 +00:00
|
|
|
import org.mozilla.fenix.home.intent.CrashReporterIntentProcessor
|
2019-08-22 18:31:40 +00:00
|
|
|
import org.mozilla.fenix.home.intent.OpenBrowserIntentProcessor
|
2020-06-10 17:25:53 +00:00
|
|
|
import org.mozilla.fenix.home.intent.OpenSpecificTabIntentProcessor
|
2020-05-26 20:25:52 +00:00
|
|
|
import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor
|
2020-06-04 21:41:50 +00:00
|
|
|
import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
|
2019-08-21 23:29:45 +00:00
|
|
|
import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
|
2020-12-15 16:30:24 +00:00
|
|
|
import org.mozilla.fenix.library.bookmarks.DesktopFolders
|
2019-08-21 23:29:45 +00:00
|
|
|
import org.mozilla.fenix.library.history.HistoryFragmentDirections
|
2020-08-29 19:24:45 +00:00
|
|
|
import org.mozilla.fenix.library.recentlyclosed.RecentlyClosedFragmentDirections
|
2021-04-14 00:48:45 +00:00
|
|
|
import org.mozilla.fenix.perf.NavGraphProvider
|
2020-01-17 00:58:12 +00:00
|
|
|
import org.mozilla.fenix.perf.Performance
|
16373 Count the # of inflations done on startup (#16778)
* For #16373: Added performance Inflater to counter # of inflations
This class is quite straight forward. The only thing that I have to point out is the onCreateView method. It usually
calls its super if you don't override it. The problem with that is that the super.onCreateView actually uses
android.view. as a prefix for the XML element it tries to inflate. So if we have an element that isn't part
of that package, it'll crash. As I said in the code, a good example is ImageButton. Calling android.view.ImageButton
will make the app crash. The method is implemented the same way that PhoneLayoutInflater does (Another example
is the AsyncLayoutInflater)
* For #16373: Added test for PerformanceInflater
This test got quite awkward / complicated fast. I wanted to test the to make sure we don't break *any* of our layouts
and to do so, I decided to just retrieve all our XML in our /res/layout folder. However, this gets quite a bit outside of a unit test scope.
The point was to get every layouts and get their LayoutID through the resources using the testContext we have. It gets even weirder, since some
of the XML tags have special implementation in android. One of them is the <fragment> tag. That tag actually is inflated by the OS using the Factory2
that the Activity.java implements. In order to get around the fragment issue, we just return a basic FrameLayout since the system LayoutInflater doesn't deal
won't ever get a <fragment> tag to inflate. Another issue was the <merge> tag. In order to inflate those, you need 1) a root view and 2) attach your view to it.
In order to be able to test those layouts file, I had to create an empty FrameLayout and use it as the root view for testing. Again, I know this is beyond the spirit of a unit test but if we use this inflater, I think it should make sure that no layouts are broken by it.
* For #16373: Overrode getSystemService to return PerformanceInflater
This allows PerformanceInflater to be called in every inflation to keep track of the number of inflations we do.
* For #16373: Added UI test for # of inflations
* For #16373: Lint fix
* For #167373: Changed the LayoutInflater cloneInContext to take this instead of inflater
The inflater parameter is set on the first call from the OS from the Window object. However, the activity itself sets multiple factories on the inflater
during its creation (usually through AppCompatDelegateImpl.java). This means that, once we initially set the inflater with a null check, we pass an inflater
that has no factory initially. However, since we keep a reference to it, when cloneInContext was called, it cloned the inflater with the original inflater
which didn't have any factories set up. This meant that the app would crash on either browserFragment creation or any thing that required appCompat (such as
ImageView and ImageButton). Now, passing itself with a cloneInContext means we keep all the factories initially set by the activity or the fragment.
* For #16373: Fixed code issues for PR. No behavior change
* For #16373: fixed some code nits
2021-01-04 16:00:34 +00:00
|
|
|
import org.mozilla.fenix.perf.PerformanceInflater
|
2021-02-02 01:58:46 +00:00
|
|
|
import org.mozilla.fenix.perf.ProfilerMarkers
|
2021-04-09 21:51:29 +00:00
|
|
|
import org.mozilla.fenix.perf.StartupPathProvider
|
2020-02-20 23:15:24 +00:00
|
|
|
import org.mozilla.fenix.perf.StartupTimeline
|
2021-04-12 22:42:11 +00:00
|
|
|
import org.mozilla.fenix.perf.StartupTypeTelemetry
|
2020-09-24 20:46:42 +00:00
|
|
|
import org.mozilla.fenix.search.SearchDialogFragmentDirections
|
2020-08-14 23:08:41 +00:00
|
|
|
import org.mozilla.fenix.session.PrivateNotificationService
|
2019-08-21 23:29:45 +00:00
|
|
|
import org.mozilla.fenix.settings.SettingsFragmentDirections
|
2019-09-10 20:29:21 +00:00
|
|
|
import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections
|
2020-01-09 19:28:21 +00:00
|
|
|
import org.mozilla.fenix.settings.about.AboutFragmentDirections
|
2020-07-16 22:08:04 +00:00
|
|
|
import org.mozilla.fenix.settings.logins.fragment.LoginDetailFragmentDirections
|
2020-07-21 17:47:10 +00:00
|
|
|
import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections
|
2020-04-21 11:44:59 +00:00
|
|
|
import org.mozilla.fenix.settings.search.AddSearchEngineFragmentDirections
|
|
|
|
import org.mozilla.fenix.settings.search.EditCustomSearchEngineFragmentDirections
|
|
|
|
import org.mozilla.fenix.share.AddNewDeviceFragmentDirections
|
2020-05-26 20:25:52 +00:00
|
|
|
import org.mozilla.fenix.sync.SyncedTabsFragmentDirections
|
2020-06-18 17:32:14 +00:00
|
|
|
import org.mozilla.fenix.tabtray.TabTrayDialogFragment
|
2020-08-18 04:09:27 +00:00
|
|
|
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentDirections
|
2019-08-21 15:33:59 +00:00
|
|
|
import org.mozilla.fenix.theme.DefaultThemeManager
|
|
|
|
import org.mozilla.fenix.theme.ThemeManager
|
2020-01-04 09:35:46 +00:00
|
|
|
import org.mozilla.fenix.utils.BrowsersCache
|
2020-08-17 22:05:06 +00:00
|
|
|
import java.lang.ref.WeakReference
|
2018-12-07 20:11:50 +00:00
|
|
|
|
2020-04-02 22:49:26 +00:00
|
|
|
/**
|
|
|
|
* The main activity of the application. The application is primarily a single Activity (this one)
|
|
|
|
* with fragments switching out to display different views. The most important views shown here are the:
|
|
|
|
* - home screen
|
|
|
|
* - browser screen
|
|
|
|
*/
|
2020-07-16 01:02:58 +00:00
|
|
|
@OptIn(ExperimentalCoroutinesApi::class)
|
2021-03-30 20:12:46 +00:00
|
|
|
@SuppressWarnings("TooManyFunctions", "LargeClass", "LongParameterList")
|
2020-07-06 20:04:43 +00:00
|
|
|
open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
2020-08-28 05:57:39 +00:00
|
|
|
// DO NOT MOVE ANYTHING ABOVE THIS, GETTING INIT TIME IS CRITICAL
|
|
|
|
// we need to store startup timestamp for warm startup. we cant directly store
|
|
|
|
// inside AppStartupTelemetry since that class lives inside components and
|
|
|
|
// components requires context to access.
|
|
|
|
protected val homeActivityInitTimeStampNanoSeconds = SystemClock.elapsedRealtimeNanos()
|
2019-03-18 21:32:03 +00:00
|
|
|
|
2019-05-30 22:49:58 +00:00
|
|
|
lateinit var themeManager: ThemeManager
|
2020-02-23 05:46:50 +00:00
|
|
|
lateinit var browsingModeManager: BrowsingModeManager
|
2019-08-20 17:00:49 +00:00
|
|
|
|
2020-01-23 08:24:48 +00:00
|
|
|
private var isVisuallyComplete = false
|
|
|
|
|
2020-10-02 22:39:03 +00:00
|
|
|
private var privateNotificationObserver: PrivateNotificationFeature<PrivateNotificationService>? =
|
|
|
|
null
|
2020-01-23 08:24:48 +00:00
|
|
|
|
2020-01-16 18:03:18 +00:00
|
|
|
private var isToolbarInflated = false
|
|
|
|
|
2020-02-04 06:41:52 +00:00
|
|
|
private val webExtensionPopupFeature by lazy {
|
|
|
|
WebExtensionPopupFeature(components.core.store, ::openPopup)
|
|
|
|
}
|
|
|
|
|
16373 Count the # of inflations done on startup (#16778)
* For #16373: Added performance Inflater to counter # of inflations
This class is quite straight forward. The only thing that I have to point out is the onCreateView method. It usually
calls its super if you don't override it. The problem with that is that the super.onCreateView actually uses
android.view. as a prefix for the XML element it tries to inflate. So if we have an element that isn't part
of that package, it'll crash. As I said in the code, a good example is ImageButton. Calling android.view.ImageButton
will make the app crash. The method is implemented the same way that PhoneLayoutInflater does (Another example
is the AsyncLayoutInflater)
* For #16373: Added test for PerformanceInflater
This test got quite awkward / complicated fast. I wanted to test the to make sure we don't break *any* of our layouts
and to do so, I decided to just retrieve all our XML in our /res/layout folder. However, this gets quite a bit outside of a unit test scope.
The point was to get every layouts and get their LayoutID through the resources using the testContext we have. It gets even weirder, since some
of the XML tags have special implementation in android. One of them is the <fragment> tag. That tag actually is inflated by the OS using the Factory2
that the Activity.java implements. In order to get around the fragment issue, we just return a basic FrameLayout since the system LayoutInflater doesn't deal
won't ever get a <fragment> tag to inflate. Another issue was the <merge> tag. In order to inflate those, you need 1) a root view and 2) attach your view to it.
In order to be able to test those layouts file, I had to create an empty FrameLayout and use it as the root view for testing. Again, I know this is beyond the spirit of a unit test but if we use this inflater, I think it should make sure that no layouts are broken by it.
* For #16373: Overrode getSystemService to return PerformanceInflater
This allows PerformanceInflater to be called in every inflation to keep track of the number of inflations we do.
* For #16373: Added UI test for # of inflations
* For #16373: Lint fix
* For #167373: Changed the LayoutInflater cloneInContext to take this instead of inflater
The inflater parameter is set on the first call from the OS from the Window object. However, the activity itself sets multiple factories on the inflater
during its creation (usually through AppCompatDelegateImpl.java). This means that, once we initially set the inflater with a null check, we pass an inflater
that has no factory initially. However, since we keep a reference to it, when cloneInContext was called, it cloned the inflater with the original inflater
which didn't have any factories set up. This meant that the app would crash on either browserFragment creation or any thing that required appCompat (such as
ImageView and ImageButton). Now, passing itself with a cloneInContext means we keep all the factories initially set by the activity or the fragment.
* For #16373: Fixed code issues for PR. No behavior change
* For #16373: fixed some code nits
2021-01-04 16:00:34 +00:00
|
|
|
private var inflater: LayoutInflater? = null
|
|
|
|
|
2019-03-20 17:47:22 +00:00
|
|
|
private val navHost by lazy {
|
|
|
|
supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment
|
|
|
|
}
|
|
|
|
|
2019-08-22 18:31:40 +00:00
|
|
|
private val externalSourceIntentProcessors by lazy {
|
|
|
|
listOf(
|
2020-11-09 11:16:48 +00:00
|
|
|
SpeechProcessingIntentProcessor(this, components.core.store, components.analytics.metrics),
|
2019-08-22 18:31:40 +00:00
|
|
|
StartSearchIntentProcessor(components.analytics.metrics),
|
2020-05-26 06:21:57 +00:00
|
|
|
OpenBrowserIntentProcessor(this, ::getIntentSessionId),
|
|
|
|
OpenSpecificTabIntentProcessor(this)
|
2019-08-22 18:31:40 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-07-14 23:20:42 +00:00
|
|
|
// See onKeyDown for why this is necessary
|
|
|
|
private var backLongPressJob: Job? = null
|
|
|
|
|
2020-06-22 17:15:33 +00:00
|
|
|
private lateinit var navigationToolbar: Toolbar
|
|
|
|
|
2021-03-23 09:46:10 +00:00
|
|
|
// Tracker for contextual menu (Copy|Search|Select all|etc...)
|
|
|
|
private var actionMode: ActionMode? = null
|
|
|
|
|
2021-04-09 21:51:29 +00:00
|
|
|
private val startupPathProvider = StartupPathProvider()
|
2021-04-12 22:42:11 +00:00
|
|
|
private lateinit var startupTypeTelemetry: StartupTypeTelemetry
|
2021-04-09 21:51:29 +00:00
|
|
|
|
2021-03-17 17:35:50 +00:00
|
|
|
final override fun onCreate(savedInstanceState: Bundle?): Unit = PerfStartup.homeActivityOnCreate.measureNoInline {
|
2021-02-02 01:51:09 +00:00
|
|
|
// DO NOT MOVE ANYTHING ABOVE THIS addMarker CALL.
|
|
|
|
components.core.engine.profiler?.addMarker("Activity.onCreate", "HomeActivity")
|
|
|
|
|
2020-09-24 22:21:15 +00:00
|
|
|
components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
|
2020-06-08 17:45:33 +00:00
|
|
|
// There is disk read violations on some devices such as samsung and pixel for android 9/10
|
2020-09-24 23:35:36 +00:00
|
|
|
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
|
2021-03-26 14:04:23 +00:00
|
|
|
// Theme setup should always be called before super.onCreate
|
|
|
|
setupThemeAndBrowsingMode(getModeFromIntentOrLastKnown(intent))
|
2020-06-08 17:45:33 +00:00
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
}
|
2019-06-10 20:46:55 +00:00
|
|
|
|
2020-08-26 11:43:20 +00:00
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onCreate()",
|
|
|
|
data = mapOf(
|
|
|
|
"recreated" to (savedInstanceState != null).toString(),
|
|
|
|
"intent" to (intent?.action ?: "null")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2019-07-31 23:20:34 +00:00
|
|
|
components.publicSuffixList.prefetch()
|
2019-06-14 17:08:46 +00:00
|
|
|
|
2021-04-14 00:48:45 +00:00
|
|
|
setContentView(R.layout.activity_home).run {
|
|
|
|
// Do not call anything between setContentView and inflateNavGraphAsync.
|
|
|
|
// It needs to start its job as early as possible.
|
|
|
|
NavGraphProvider.inflateNavGraphAsync(navHost.navController, lifecycleScope)
|
|
|
|
}
|
2020-01-23 08:24:48 +00:00
|
|
|
|
|
|
|
// Must be after we set the content view
|
|
|
|
if (isVisuallyComplete) {
|
2020-08-17 22:05:06 +00:00
|
|
|
components.performance.visualCompletenessQueue
|
|
|
|
.attachViewToRunVisualCompletenessQueueLater(WeakReference(rootContainer))
|
2020-01-23 08:24:48 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 23:08:41 +00:00
|
|
|
privateNotificationObserver = PrivateNotificationFeature(
|
|
|
|
applicationContext,
|
|
|
|
components.core.store,
|
|
|
|
PrivateNotificationService::class
|
|
|
|
).also {
|
2020-06-25 17:41:35 +00:00
|
|
|
it.start()
|
|
|
|
}
|
2020-06-25 01:35:11 +00:00
|
|
|
|
2021-04-14 16:00:05 +00:00
|
|
|
if (isActivityColdStarted(intent, savedInstanceState) &&
|
|
|
|
!externalSourceIntentProcessors.any { it.process(intent, navHost.navController, this.intent) }) {
|
2020-10-02 22:39:03 +00:00
|
|
|
navigateToBrowserOnColdStart()
|
2020-06-19 18:33:16 +00:00
|
|
|
}
|
2019-06-14 17:08:46 +00:00
|
|
|
|
2020-02-11 16:12:39 +00:00
|
|
|
Performance.processIntentIfPerformanceTest(intent, this)
|
2019-12-12 21:23:02 +00:00
|
|
|
|
2019-09-24 17:33:46 +00:00
|
|
|
if (settings().isTelemetryEnabled) {
|
2020-07-07 17:31:05 +00:00
|
|
|
lifecycle.addObserver(
|
|
|
|
BreadcrumbsRecorder(
|
|
|
|
components.analytics.crashReporter,
|
|
|
|
navHost.navController, ::getBreadcrumbMessage
|
|
|
|
)
|
|
|
|
)
|
2019-06-14 17:08:46 +00:00
|
|
|
|
2020-06-27 00:38:08 +00:00
|
|
|
val safeIntent = intent?.toSafeIntent()
|
|
|
|
safeIntent
|
2019-08-20 17:00:49 +00:00
|
|
|
?.let(::getIntentSource)
|
|
|
|
?.also { components.analytics.metrics.track(Event.OpenedApp(it)) }
|
2020-06-27 00:38:08 +00:00
|
|
|
// record on cold startup
|
|
|
|
safeIntent
|
|
|
|
?.let(::getIntentAllSource)
|
2020-06-29 22:15:12 +00:00
|
|
|
?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) }
|
2019-06-04 15:08:06 +00:00
|
|
|
}
|
2019-05-23 20:05:30 +00:00
|
|
|
supportActionBar?.hide()
|
2020-02-04 06:41:52 +00:00
|
|
|
|
2020-04-07 21:41:57 +00:00
|
|
|
lifecycle.addObservers(
|
|
|
|
webExtensionPopupFeature,
|
|
|
|
StartupTimeline.homeActivityLifecycleObserver
|
|
|
|
)
|
2020-06-25 01:35:11 +00:00
|
|
|
|
|
|
|
if (shouldAddToRecentsScreen(intent)) {
|
|
|
|
intent.removeExtra(START_IN_RECENTS_SCREEN)
|
|
|
|
moveTaskToBack(true)
|
|
|
|
}
|
2020-06-26 00:35:52 +00:00
|
|
|
|
|
|
|
captureSnapshotTelemetryMetrics()
|
2020-07-08 23:39:07 +00:00
|
|
|
|
2020-08-27 06:14:13 +00:00
|
|
|
startupTelemetryOnCreateCalled(intent.toSafeIntent(), savedInstanceState != null)
|
2021-04-09 21:51:29 +00:00
|
|
|
startupPathProvider.attachOnActivityOnCreate(lifecycle, intent)
|
2021-04-12 22:42:11 +00:00
|
|
|
startupTypeTelemetry = StartupTypeTelemetry(components.startupStateProvider, startupPathProvider).apply {
|
|
|
|
attachOnHomeActivityOnCreate(lifecycle)
|
|
|
|
}
|
2020-06-29 22:15:12 +00:00
|
|
|
|
2020-11-05 21:25:45 +00:00
|
|
|
components.core.requestInterceptor.setNavigationController(navHost.navController)
|
|
|
|
|
2020-07-08 23:39:07 +00:00
|
|
|
StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE.
|
2019-03-04 22:32:10 +00:00
|
|
|
}
|
2019-03-22 06:05:28 +00:00
|
|
|
|
2020-10-02 22:39:03 +00:00
|
|
|
protected open fun startupTelemetryOnCreateCalled(
|
|
|
|
safeIntent: SafeIntent,
|
|
|
|
hasSavedInstanceState: Boolean
|
|
|
|
) {
|
2021-04-09 21:51:29 +00:00
|
|
|
// This function gets overridden by subclasses.
|
2020-08-28 05:57:39 +00:00
|
|
|
components.appStartupTelemetry.onHomeActivityOnCreate(
|
|
|
|
safeIntent,
|
|
|
|
hasSavedInstanceState,
|
|
|
|
homeActivityInitTimeStampNanoSeconds, rootContainer
|
|
|
|
)
|
2021-03-24 16:25:50 +00:00
|
|
|
|
|
|
|
components.performance.coldStartupDurationTelemetry.onHomeActivityOnCreate(
|
|
|
|
components.performance.visualCompletenessQueue,
|
2021-04-08 00:16:31 +00:00
|
|
|
components.startupStateProvider,
|
2021-03-24 16:25:50 +00:00
|
|
|
safeIntent,
|
|
|
|
rootContainer
|
|
|
|
)
|
2020-08-27 06:14:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRestart() {
|
2020-08-28 05:57:39 +00:00
|
|
|
// DO NOT MOVE ANYTHING ABOVE THIS..
|
|
|
|
// we are measuring startup time for hot startup type
|
|
|
|
startupTelemetryOnRestartCalled()
|
2020-08-27 06:14:13 +00:00
|
|
|
super.onRestart()
|
2020-08-28 05:57:39 +00:00
|
|
|
}
|
2020-08-27 06:14:13 +00:00
|
|
|
|
2020-08-28 05:57:39 +00:00
|
|
|
private fun startupTelemetryOnRestartCalled() {
|
|
|
|
components.appStartupTelemetry.onHomeActivityOnRestart(rootContainer)
|
2020-06-29 22:15:12 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 17:00:49 +00:00
|
|
|
@CallSuper
|
2019-05-28 22:26:36 +00:00
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
2019-10-03 19:43:33 +00:00
|
|
|
|
2021-02-04 23:42:05 +00:00
|
|
|
// Even if screenshots are allowed, we hide private content in the recents screen in onPause
|
2021-05-19 16:07:33 +00:00
|
|
|
// only when we are in private mode, so in onResume we should go back to setting these flags
|
|
|
|
// with the user screenshot setting only when we are in private mode.
|
2021-02-04 23:42:05 +00:00
|
|
|
// See https://github.com/mozilla-mobile/fenix/issues/11153
|
2021-05-19 16:07:33 +00:00
|
|
|
if (settings().lastKnownMode == BrowsingMode.Private) {
|
|
|
|
updateSecureWindowFlags(settings().lastKnownMode)
|
|
|
|
}
|
2021-02-04 23:42:05 +00:00
|
|
|
|
2020-08-26 11:43:20 +00:00
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onResume()"
|
|
|
|
)
|
|
|
|
|
2020-03-27 06:18:32 +00:00
|
|
|
components.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
|
|
|
|
lifecycleScope.launch {
|
2019-06-13 00:13:24 +00:00
|
|
|
// Make sure accountManager is initialized.
|
2020-07-28 02:46:36 +00:00
|
|
|
components.backgroundServices.accountManager.start()
|
2019-06-13 00:13:24 +00:00
|
|
|
// If we're authenticated, kick-off a sync and a device state refresh.
|
2020-03-27 06:18:32 +00:00
|
|
|
components.backgroundServices.accountManager.authenticatedAccount()?.let {
|
2020-07-28 02:46:36 +00:00
|
|
|
components.backgroundServices.accountManager.syncNow(
|
2020-07-07 17:31:05 +00:00
|
|
|
SyncReason.Startup,
|
|
|
|
debounce = true
|
|
|
|
)
|
2019-06-13 00:13:24 +00:00
|
|
|
}
|
2019-05-28 22:26:36 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-27 21:24:02 +00:00
|
|
|
|
|
|
|
// Launch this on a background thread so as not to affect startup performance
|
|
|
|
lifecycleScope.launch(IO) {
|
2020-07-27 21:36:48 +00:00
|
|
|
if (
|
|
|
|
settings().isDefaultBrowser() &&
|
2020-08-12 19:00:09 +00:00
|
|
|
settings().wasDefaultBrowserOnLastResume != settings().isDefaultBrowser()
|
2020-07-27 21:36:48 +00:00
|
|
|
) {
|
2020-07-27 21:24:02 +00:00
|
|
|
metrics.track(Event.ChangedToDefaultBrowser)
|
|
|
|
}
|
2020-08-12 19:00:09 +00:00
|
|
|
|
|
|
|
settings().wasDefaultBrowserOnLastResume = settings().isDefaultBrowser()
|
2020-07-27 21:24:02 +00:00
|
|
|
}
|
2019-05-28 22:26:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-17 23:36:20 +00:00
|
|
|
override fun onStart() = PerfStartup.homeActivityOnStart.measureNoInline {
|
2020-08-26 11:43:20 +00:00
|
|
|
super.onStart()
|
|
|
|
|
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onStart()"
|
|
|
|
)
|
2021-02-02 01:58:46 +00:00
|
|
|
|
|
|
|
ProfilerMarkers.homeActivityOnStart(rootContainer, components.core.engine.profiler)
|
2020-08-26 11:43:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onStop() {
|
|
|
|
super.onStop()
|
|
|
|
|
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onStop()",
|
|
|
|
data = mapOf(
|
|
|
|
"finishing" to isFinishing.toString()
|
|
|
|
)
|
|
|
|
)
|
2020-08-28 05:57:39 +00:00
|
|
|
|
|
|
|
components.appStartupTelemetry.onStop()
|
2020-08-26 11:43:20 +00:00
|
|
|
}
|
|
|
|
|
2020-01-04 09:35:46 +00:00
|
|
|
final override fun onPause() {
|
2020-10-02 22:39:03 +00:00
|
|
|
// We should return to the browser if there were normal tabs when we left the app
|
|
|
|
settings().shouldReturnToBrowser =
|
|
|
|
components.core.store.state.getNormalOrPrivateTabs(private = false).isNotEmpty()
|
|
|
|
|
2021-02-04 23:42:05 +00:00
|
|
|
// Even if screenshots are allowed, we want to hide private content in the recents screen
|
2021-05-19 16:07:33 +00:00
|
|
|
// only when we are in private mode
|
2021-02-04 23:42:05 +00:00
|
|
|
// See https://github.com/mozilla-mobile/fenix/issues/11153
|
2020-06-03 05:32:43 +00:00
|
|
|
if (settings().lastKnownMode.isPrivate) {
|
2021-02-04 23:42:05 +00:00
|
|
|
window.addFlags(FLAG_SECURE)
|
2020-06-03 05:32:43 +00:00
|
|
|
}
|
2020-07-27 21:24:02 +00:00
|
|
|
|
2020-12-15 16:30:24 +00:00
|
|
|
lifecycleScope.launch(IO) {
|
|
|
|
components.core.bookmarksStorage.getTree(BookmarkRoot.Root.id, true)?.let {
|
|
|
|
val desktopRootNode = DesktopFolders(
|
|
|
|
applicationContext,
|
|
|
|
showMobileRoot = false
|
|
|
|
).withOptionalDesktopFolders(it)
|
|
|
|
settings().desktopBookmarksSize = getBookmarkCount(desktopRootNode)
|
|
|
|
}
|
|
|
|
|
|
|
|
components.core.bookmarksStorage.getTree(BookmarkRoot.Mobile.id, true)?.let {
|
|
|
|
settings().mobileBookmarksSize = getBookmarkCount(it)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 09:35:46 +00:00
|
|
|
super.onPause()
|
|
|
|
|
2020-08-26 11:43:20 +00:00
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onPause()",
|
|
|
|
data = mapOf(
|
|
|
|
"finishing" to isFinishing.toString()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-01-04 09:35:46 +00:00
|
|
|
// Every time the application goes into the background, it is possible that the user
|
|
|
|
// is about to change the browsers installed on their system. Therefore, we reset the cache of
|
|
|
|
// all the installed browsers.
|
|
|
|
//
|
|
|
|
// NB: There are ways for the user to install new products without leaving the browser.
|
|
|
|
BrowsersCache.resetAll()
|
|
|
|
}
|
2020-01-22 20:53:26 +00:00
|
|
|
|
2020-12-15 16:30:24 +00:00
|
|
|
private fun getBookmarkCount(node: BookmarkNode): Int {
|
|
|
|
val children = node.children
|
|
|
|
return if (children == null) {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
var count = 0
|
|
|
|
|
|
|
|
for (child in children) {
|
|
|
|
if (child.type == BookmarkNodeType.FOLDER) {
|
|
|
|
count += getBookmarkCount(child)
|
|
|
|
} else if (child.type == BookmarkNodeType.ITEM) {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 17:41:35 +00:00
|
|
|
override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
2020-08-26 11:43:20 +00:00
|
|
|
|
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onDestroy()",
|
|
|
|
data = mapOf(
|
|
|
|
"finishing" to isFinishing.toString()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-06-25 17:41:35 +00:00
|
|
|
privateNotificationObserver?.stop()
|
|
|
|
}
|
|
|
|
|
2020-08-26 11:43:20 +00:00
|
|
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
super.onConfigurationChanged(newConfig)
|
|
|
|
|
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onConfigurationChanged()"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun recreate() {
|
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "recreate()"
|
|
|
|
)
|
|
|
|
|
|
|
|
super.recreate()
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:00:49 +00:00
|
|
|
/**
|
|
|
|
* Handles intents received when the activity is open.
|
|
|
|
*/
|
|
|
|
final override fun onNewIntent(intent: Intent?) {
|
|
|
|
super.onNewIntent(intent)
|
2020-11-06 16:44:02 +00:00
|
|
|
intent?.let {
|
|
|
|
handleNewIntent(it)
|
|
|
|
}
|
2021-04-09 21:51:29 +00:00
|
|
|
startupPathProvider.onIntentReceived(intent)
|
2020-11-06 16:44:02 +00:00
|
|
|
}
|
2019-08-22 18:31:40 +00:00
|
|
|
|
2020-11-06 16:44:02 +00:00
|
|
|
open fun handleNewIntent(intent: Intent) {
|
2020-08-26 11:43:20 +00:00
|
|
|
// Diagnostic breadcrumb for "Display already aquired" crash:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/issues/7960
|
|
|
|
breadcrumb(
|
|
|
|
message = "onNewIntent()",
|
|
|
|
data = mapOf(
|
|
|
|
"intent" to intent.action.toString()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-07-07 17:31:05 +00:00
|
|
|
val intentProcessors =
|
|
|
|
listOf(CrashReporterIntentProcessor()) + externalSourceIntentProcessors
|
|
|
|
val intentHandled =
|
|
|
|
intentProcessors.any { it.process(intent, navHost.navController, this.intent) }
|
2020-01-09 19:28:21 +00:00
|
|
|
browsingModeManager.mode = getModeFromIntentOrLastKnown(intent)
|
2020-06-18 17:32:14 +00:00
|
|
|
|
|
|
|
if (intentHandled) {
|
|
|
|
supportFragmentManager
|
|
|
|
.primaryNavigationFragment
|
|
|
|
?.childFragmentManager
|
|
|
|
?.fragments
|
|
|
|
?.lastOrNull()
|
|
|
|
?.let { it as? TabTrayDialogFragment }
|
|
|
|
?.also { it.dismissAllowingStateLoss() }
|
|
|
|
}
|
2020-06-27 00:38:08 +00:00
|
|
|
|
|
|
|
// Note: This does not work in case of an user sending an intent with ACTION_VIEW
|
|
|
|
// for example, launch the application, and than use adb to send an intent with
|
|
|
|
// ACTION_VIEW to open a link. In this case, we will get multiple telemetry events.
|
|
|
|
intent
|
|
|
|
.toSafeIntent()
|
|
|
|
.let(::getIntentAllSource)
|
2020-06-29 22:15:12 +00:00
|
|
|
?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) }
|
|
|
|
|
2020-08-27 06:14:13 +00:00
|
|
|
components.appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
|
2019-08-20 17:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overrides view inflation to inject a custom [EngineView] from [components].
|
|
|
|
*/
|
|
|
|
final override fun onCreateView(
|
2019-01-31 19:33:56 +00:00
|
|
|
parent: View?,
|
|
|
|
name: String,
|
|
|
|
context: Context,
|
|
|
|
attrs: AttributeSet
|
2019-08-20 17:00:49 +00:00
|
|
|
): View? = when (name) {
|
2020-02-18 03:20:35 +00:00
|
|
|
EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply {
|
|
|
|
selectionActionDelegate = DefaultSelectionActionDelegate(
|
2020-08-18 18:25:56 +00:00
|
|
|
BrowserStoreSearchAdapter(
|
|
|
|
components.core.store,
|
|
|
|
tabId = getIntentSessionId(intent.toSafeIntent())
|
|
|
|
),
|
2020-07-10 18:27:28 +00:00
|
|
|
resources = context.resources,
|
|
|
|
shareTextClicked = { share(it) },
|
|
|
|
emailTextClicked = { email(it) },
|
|
|
|
callTextClicked = { call(it) },
|
|
|
|
actionSorter = ::actionSorter
|
|
|
|
)
|
2020-02-18 03:20:35 +00:00
|
|
|
}.asView()
|
2019-08-20 17:00:49 +00:00
|
|
|
else -> super.onCreateView(parent, name, context, attrs)
|
|
|
|
}
|
2019-02-01 05:52:26 +00:00
|
|
|
|
2021-03-23 09:46:10 +00:00
|
|
|
override fun onActionModeStarted(mode: ActionMode?) {
|
|
|
|
actionMode = mode
|
|
|
|
super.onActionModeStarted(mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onActionModeFinished(mode: ActionMode?) {
|
|
|
|
actionMode = null
|
|
|
|
super.onActionModeFinished(mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun finishActionMode() {
|
|
|
|
actionMode?.finish().also { actionMode = null }
|
|
|
|
}
|
|
|
|
|
2020-07-10 21:00:15 +00:00
|
|
|
@Suppress("MagicNumber")
|
|
|
|
// Defining the positions as constants doesn't seem super useful here.
|
|
|
|
private fun actionSorter(actions: Array<String>): Array<String> {
|
2020-07-10 18:27:28 +00:00
|
|
|
val order = hashMapOf<String, Int>()
|
|
|
|
|
2020-07-13 16:56:58 +00:00
|
|
|
order["CUSTOM_CONTEXT_MENU_EMAIL"] = 0
|
|
|
|
order["CUSTOM_CONTEXT_MENU_CALL"] = 1
|
|
|
|
order["org.mozilla.geckoview.COPY"] = 2
|
|
|
|
order["CUSTOM_CONTEXT_MENU_SEARCH"] = 3
|
2020-07-20 20:25:36 +00:00
|
|
|
order["CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY"] = 4
|
2020-09-09 15:56:21 +00:00
|
|
|
order["org.mozilla.geckoview.PASTE"] = 5
|
|
|
|
order["org.mozilla.geckoview.SELECT_ALL"] = 6
|
|
|
|
order["CUSTOM_CONTEXT_MENU_SHARE"] = 7
|
2020-07-10 18:27:28 +00:00
|
|
|
|
|
|
|
return actions.sortedBy { actionName ->
|
|
|
|
// Sort the actions in our preferred order, putting "other" actions unsorted at the end
|
|
|
|
order[actionName] ?: actions.size
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:00:49 +00:00
|
|
|
final override fun onBackPressed() {
|
2019-02-01 05:52:26 +00:00
|
|
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
2019-11-27 23:02:47 +00:00
|
|
|
if (it is UserInteractionHandler && it.onBackPressed()) {
|
2019-02-01 05:52:26 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
super.onBackPressed()
|
|
|
|
}
|
2019-02-06 22:31:39 +00:00
|
|
|
|
2021-02-01 10:52:56 +00:00
|
|
|
final override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
|
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
|
|
|
if (it is ActivityResultHandler && it.onActivityResult(requestCode, data, resultCode)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
|
|
}
|
|
|
|
|
2020-09-01 01:47:16 +00:00
|
|
|
private fun shouldUseCustomBackLongPress(): Boolean {
|
|
|
|
val isAndroidN =
|
|
|
|
Build.VERSION.SDK_INT == Build.VERSION_CODES.N || Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1
|
|
|
|
// Huawei devices seem to have problems with onKeyLongPress
|
|
|
|
// See https://github.com/mozilla-mobile/fenix/issues/13498
|
|
|
|
val isHuawei = Build.MANUFACTURER.equals("huawei", ignoreCase = true)
|
|
|
|
return isAndroidN || isHuawei
|
|
|
|
}
|
2020-07-14 23:20:42 +00:00
|
|
|
|
|
|
|
private fun handleBackLongPress(): Boolean {
|
|
|
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
|
|
|
if (it is OnBackLongPressedListener && it.onBackLongPressed()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
final override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
|
|
|
// Inspired by https://searchfox.org/mozilla-esr68/source/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java#584-613
|
2020-09-01 01:47:16 +00:00
|
|
|
// Android N and Huawei devices have broken onKeyLongPress events for the back button, so we
|
2020-07-14 23:20:42 +00:00
|
|
|
// instead implement the long press behavior ourselves
|
|
|
|
// - For short presses, we cancel the callback in onKeyUp
|
|
|
|
// - For long presses, the normal keypress is marked as cancelled, hence won't be handled elsewhere
|
|
|
|
// (but Android still provides the haptic feedback), and the long press action is run
|
2020-09-01 01:47:16 +00:00
|
|
|
if (shouldUseCustomBackLongPress() && keyCode == KeyEvent.KEYCODE_BACK) {
|
2020-07-14 23:20:42 +00:00
|
|
|
backLongPressJob = lifecycleScope.launch {
|
|
|
|
delay(ViewConfiguration.getLongPressTimeout().toLong())
|
|
|
|
handleBackLongPress()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return super.onKeyDown(keyCode, event)
|
|
|
|
}
|
|
|
|
|
|
|
|
final override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
2020-09-01 01:47:16 +00:00
|
|
|
if (shouldUseCustomBackLongPress() && keyCode == KeyEvent.KEYCODE_BACK) {
|
2020-07-14 23:20:42 +00:00
|
|
|
backLongPressJob?.cancel()
|
|
|
|
}
|
|
|
|
return super.onKeyUp(keyCode, event)
|
|
|
|
}
|
|
|
|
|
|
|
|
final override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
|
|
|
|
// onKeyLongPress is broken in Android N so we don't handle back button long presses here
|
|
|
|
// for N. The version check ensures we don't handle back button long presses twice.
|
2020-09-01 01:47:16 +00:00
|
|
|
if (!shouldUseCustomBackLongPress() && keyCode == KeyEvent.KEYCODE_BACK) {
|
2020-07-14 23:20:42 +00:00
|
|
|
return handleBackLongPress()
|
|
|
|
}
|
|
|
|
return super.onKeyLongPress(keyCode, event)
|
|
|
|
}
|
|
|
|
|
2020-04-07 05:15:04 +00:00
|
|
|
final override fun onUserLeaveHint() {
|
|
|
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
|
|
|
if (it is UserInteractionHandler && it.onHomePressed()) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
super.onUserLeaveHint()
|
|
|
|
}
|
|
|
|
|
2019-09-03 21:47:43 +00:00
|
|
|
protected open fun getBreadcrumbMessage(destination: NavDestination): String {
|
2019-08-20 17:00:49 +00:00
|
|
|
val fragmentName = resources.getResourceEntryName(destination.id)
|
|
|
|
return "Changing to fragment $fragmentName, isCustomTab: false"
|
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting(otherwise = PROTECTED)
|
|
|
|
internal open fun getIntentSource(intent: SafeIntent): Event.OpenedApp.Source? {
|
|
|
|
return when {
|
|
|
|
intent.isLauncherIntent -> Event.OpenedApp.Source.APP_ICON
|
|
|
|
intent.action == Intent.ACTION_VIEW -> Event.OpenedApp.Source.LINK
|
|
|
|
else -> null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 22:15:12 +00:00
|
|
|
protected open fun getIntentAllSource(intent: SafeIntent): Event.AppReceivedIntent.Source? {
|
2020-06-27 00:38:08 +00:00
|
|
|
return when {
|
2020-06-29 22:15:12 +00:00
|
|
|
intent.isLauncherIntent -> Event.AppReceivedIntent.Source.APP_ICON
|
|
|
|
intent.action == Intent.ACTION_VIEW -> Event.AppReceivedIntent.Source.LINK
|
|
|
|
else -> Event.AppReceivedIntent.Source.UNKNOWN
|
2020-06-27 00:38:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 17:52:33 +00:00
|
|
|
/**
|
|
|
|
* External sources such as 3rd party links and shortcuts use this function to enter
|
2020-01-09 19:28:21 +00:00
|
|
|
* private mode directly before the content view is created. Returns the mode set by the intent
|
|
|
|
* otherwise falls back to the last known mode.
|
2019-09-11 17:52:33 +00:00
|
|
|
*/
|
2020-01-09 19:28:21 +00:00
|
|
|
internal fun getModeFromIntentOrLastKnown(intent: Intent?): BrowsingMode {
|
2019-09-11 17:52:33 +00:00
|
|
|
intent?.toSafeIntent()?.let {
|
|
|
|
if (it.hasExtra(PRIVATE_BROWSING_MODE)) {
|
|
|
|
val startPrivateMode = it.getBooleanExtra(PRIVATE_BROWSING_MODE, false)
|
2019-10-07 17:51:09 +00:00
|
|
|
return BrowsingMode.fromBoolean(isPrivate = startPrivateMode)
|
2019-09-11 17:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-09 19:28:21 +00:00
|
|
|
return settings().lastKnownMode
|
2019-09-11 17:52:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 01:35:11 +00:00
|
|
|
/**
|
|
|
|
* Determines whether the activity should be pushed to be backstack (i.e., 'minimized' to the recents
|
|
|
|
* screen) upon starting.
|
|
|
|
* @param intent - The intent that started this activity. Is checked for having the 'START_IN_RECENTS_SCREEN'-extra.
|
|
|
|
* @return true if the activity should be started and pushed to the recents screen, false otherwise.
|
|
|
|
*/
|
|
|
|
private fun shouldAddToRecentsScreen(intent: Intent?): Boolean {
|
|
|
|
intent?.toSafeIntent()?.let {
|
|
|
|
return it.getBooleanExtra(START_IN_RECENTS_SCREEN, false)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:51:09 +00:00
|
|
|
private fun setupThemeAndBrowsingMode(mode: BrowsingMode) {
|
2020-01-09 19:28:21 +00:00
|
|
|
settings().lastKnownMode = mode
|
2020-02-23 05:46:50 +00:00
|
|
|
browsingModeManager = createBrowsingModeManager(mode)
|
2019-08-20 17:00:49 +00:00
|
|
|
themeManager = createThemeManager()
|
|
|
|
themeManager.setActivityTheme(this)
|
|
|
|
themeManager.applyStatusBarTheme(this)
|
|
|
|
}
|
|
|
|
|
2020-01-16 18:03:18 +00:00
|
|
|
/**
|
|
|
|
* Returns the [supportActionBar], inflating it if necessary.
|
|
|
|
* Everyone should call this instead of supportActionBar.
|
|
|
|
*/
|
2020-07-06 20:04:43 +00:00
|
|
|
override fun getSupportActionBarAndInflateIfNecessary(): ActionBar {
|
2020-01-16 18:03:18 +00:00
|
|
|
if (!isToolbarInflated) {
|
2020-06-22 17:15:33 +00:00
|
|
|
navigationToolbar = navigationToolbarStub.inflate() as Toolbar
|
2020-01-16 18:03:18 +00:00
|
|
|
|
|
|
|
setSupportActionBar(navigationToolbar)
|
2020-06-26 11:08:50 +00:00
|
|
|
// Add ids to this that we don't want to have a toolbar back button
|
2020-06-22 17:15:33 +00:00
|
|
|
setupNavigationToolbar()
|
2021-05-10 15:41:51 +00:00
|
|
|
setNavigationIcon(R.drawable.ic_back_button)
|
2019-08-20 17:00:49 +00:00
|
|
|
|
2020-01-16 18:03:18 +00:00
|
|
|
isToolbarInflated = true
|
|
|
|
}
|
|
|
|
return supportActionBar!!
|
2019-08-14 20:48:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-22 17:15:33 +00:00
|
|
|
@Suppress("SpreadOperator")
|
|
|
|
fun setupNavigationToolbar(vararg topLevelDestinationIds: Int) {
|
|
|
|
NavigationUI.setupWithNavController(
|
|
|
|
navigationToolbar,
|
|
|
|
navHost.navController,
|
|
|
|
AppBarConfiguration.Builder(*topLevelDestinationIds).build()
|
|
|
|
)
|
|
|
|
|
|
|
|
navigationToolbar.setNavigationOnClickListener {
|
|
|
|
onBackPressed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:00:49 +00:00
|
|
|
protected open fun getIntentSessionId(intent: SafeIntent): String? = null
|
|
|
|
|
2020-08-05 08:53:45 +00:00
|
|
|
/**
|
|
|
|
* Navigates to the browser fragment and loads a URL or performs a search (depending on the
|
|
|
|
* value of [searchTermOrURL]).
|
|
|
|
*
|
|
|
|
* @param flags Flags that will be used when loading the URL (not applied to searches).
|
|
|
|
*/
|
2019-05-09 21:21:33 +00:00
|
|
|
@Suppress("LongParameterList")
|
2019-03-29 20:49:50 +00:00
|
|
|
fun openToBrowserAndLoad(
|
2019-04-25 20:00:09 +00:00
|
|
|
searchTermOrURL: String,
|
2019-04-29 19:32:30 +00:00
|
|
|
newTab: Boolean,
|
|
|
|
from: BrowserDirection,
|
2019-04-29 15:30:32 +00:00
|
|
|
customTabSessionId: String? = null,
|
2019-05-09 21:21:33 +00:00
|
|
|
engine: SearchEngine? = null,
|
2020-08-05 08:53:45 +00:00
|
|
|
forceSearch: Boolean = false,
|
2021-03-30 20:12:46 +00:00
|
|
|
flags: EngineSession.LoadUrlFlags = EngineSession.LoadUrlFlags.none(),
|
|
|
|
requestDesktopMode: Boolean = false
|
2019-03-29 20:49:50 +00:00
|
|
|
) {
|
2019-04-29 15:30:32 +00:00
|
|
|
openToBrowser(from, customTabSessionId)
|
2021-03-30 20:12:46 +00:00
|
|
|
load(searchTermOrURL, newTab, engine, forceSearch, flags, requestDesktopMode)
|
2019-02-28 17:25:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 15:30:32 +00:00
|
|
|
fun openToBrowser(from: BrowserDirection, customTabSessionId: String? = null) {
|
2019-08-21 23:29:45 +00:00
|
|
|
if (navHost.navController.alreadyOnDestination(R.id.browserFragment)) return
|
|
|
|
@IdRes val fragmentId = if (from.fragmentId != 0) from.fragmentId else null
|
2019-08-12 16:31:59 +00:00
|
|
|
val directions = getNavDirections(from, customTabSessionId)
|
2019-10-25 21:25:09 +00:00
|
|
|
if (directions != null) {
|
|
|
|
navHost.navController.nav(fragmentId, directions)
|
|
|
|
}
|
2019-02-06 22:31:39 +00:00
|
|
|
}
|
|
|
|
|
2019-08-12 16:31:59 +00:00
|
|
|
protected open fun getNavDirections(
|
|
|
|
from: BrowserDirection,
|
|
|
|
customTabSessionId: String?
|
2019-10-25 21:25:09 +00:00
|
|
|
): NavDirections? = when (from) {
|
2019-08-12 16:31:59 +00:00
|
|
|
BrowserDirection.FromGlobal ->
|
|
|
|
NavGraphDirections.actionGlobalBrowser(customTabSessionId)
|
|
|
|
BrowserDirection.FromHome ->
|
2020-08-05 22:33:43 +00:00
|
|
|
HomeFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-08-05 23:22:07 +00:00
|
|
|
BrowserDirection.FromSearchDialog ->
|
|
|
|
SearchDialogFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-08-12 16:31:59 +00:00
|
|
|
BrowserDirection.FromSettings ->
|
2020-04-14 04:43:45 +00:00
|
|
|
SettingsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-05-26 20:25:52 +00:00
|
|
|
BrowserDirection.FromSyncedTabs ->
|
|
|
|
SyncedTabsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-08-12 16:31:59 +00:00
|
|
|
BrowserDirection.FromBookmarks ->
|
2020-04-14 04:43:45 +00:00
|
|
|
BookmarkFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-08-12 16:31:59 +00:00
|
|
|
BrowserDirection.FromHistory ->
|
2020-04-14 04:43:45 +00:00
|
|
|
HistoryFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-07-02 02:37:03 +00:00
|
|
|
BrowserDirection.FromTrackingProtectionExceptions ->
|
|
|
|
TrackingProtectionExceptionsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-09-03 20:16:29 +00:00
|
|
|
BrowserDirection.FromAbout ->
|
2020-04-14 04:43:45 +00:00
|
|
|
AboutFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-09-10 20:29:21 +00:00
|
|
|
BrowserDirection.FromTrackingProtection ->
|
2020-04-14 04:43:45 +00:00
|
|
|
TrackingProtectionFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-01-25 02:13:44 +00:00
|
|
|
BrowserDirection.FromSavedLoginsFragment ->
|
2020-05-12 22:32:01 +00:00
|
|
|
SavedLoginsAuthFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-04-21 11:44:59 +00:00
|
|
|
BrowserDirection.FromAddNewDeviceFragment ->
|
|
|
|
AddNewDeviceFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
|
|
|
BrowserDirection.FromAddSearchEngineFragment ->
|
|
|
|
AddSearchEngineFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
|
|
|
BrowserDirection.FromEditCustomSearchEngineFragment ->
|
|
|
|
EditCustomSearchEngineFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-07-02 02:13:23 +00:00
|
|
|
BrowserDirection.FromAddonDetailsFragment ->
|
2020-07-07 17:31:05 +00:00
|
|
|
AddonDetailsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-07-13 13:05:28 +00:00
|
|
|
BrowserDirection.FromAddonPermissionsDetailsFragment ->
|
|
|
|
AddonPermissionsDetailsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-07-07 17:31:05 +00:00
|
|
|
BrowserDirection.FromLoginDetailFragment ->
|
|
|
|
LoginDetailFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-08-18 04:09:27 +00:00
|
|
|
BrowserDirection.FromTabTray ->
|
|
|
|
TabTrayDialogFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2020-08-29 19:24:45 +00:00
|
|
|
BrowserDirection.FromRecentlyClosed ->
|
|
|
|
RecentlyClosedFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
2019-08-12 16:31:59 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 08:53:45 +00:00
|
|
|
/**
|
|
|
|
* Loads a URL or performs a search (depending on the value of [searchTermOrURL]).
|
|
|
|
*
|
|
|
|
* @param flags Flags that will be used when loading the URL (not applied to searches).
|
|
|
|
*/
|
2019-07-18 22:15:24 +00:00
|
|
|
private fun load(
|
|
|
|
searchTermOrURL: String,
|
|
|
|
newTab: Boolean,
|
|
|
|
engine: SearchEngine?,
|
2020-08-05 08:53:45 +00:00
|
|
|
forceSearch: Boolean,
|
2021-03-30 20:12:46 +00:00
|
|
|
flags: EngineSession.LoadUrlFlags = EngineSession.LoadUrlFlags.none(),
|
|
|
|
requestDesktopMode: Boolean = false
|
2019-07-18 22:15:24 +00:00
|
|
|
) {
|
2020-07-03 14:47:18 +00:00
|
|
|
val startTime = components.core.engine.profiler?.getProfilerTime()
|
2019-08-07 20:02:08 +00:00
|
|
|
val mode = browsingModeManager.mode
|
2019-02-28 17:25:37 +00:00
|
|
|
|
2019-04-29 19:32:30 +00:00
|
|
|
val loadUrlUseCase = if (newTab) {
|
2019-08-07 20:02:08 +00:00
|
|
|
when (mode) {
|
|
|
|
BrowsingMode.Private -> components.useCases.tabsUseCases.addPrivateTab
|
|
|
|
BrowsingMode.Normal -> components.useCases.tabsUseCases.addTab
|
2019-02-28 17:25:37 +00:00
|
|
|
}
|
|
|
|
} else components.useCases.sessionUseCases.loadUrl
|
|
|
|
|
2020-11-09 11:16:48 +00:00
|
|
|
// In situations where we want to perform a search but have no search engine (e.g. the user
|
|
|
|
// has removed all of them, or we couldn't load any) we will pass searchTermOrURL to Gecko
|
|
|
|
// and let it try to load whatever was entered.
|
|
|
|
if ((!forceSearch && searchTermOrURL.isUrl()) || engine == null) {
|
|
|
|
loadUrlUseCase.invoke(searchTermOrURL.toNormalizedUrl(), flags)
|
2021-03-30 20:12:46 +00:00
|
|
|
|
|
|
|
if (requestDesktopMode) {
|
|
|
|
handleRequestDesktopMode()
|
|
|
|
}
|
2020-11-09 11:16:48 +00:00
|
|
|
} else {
|
2019-04-29 19:32:30 +00:00
|
|
|
if (newTab) {
|
2019-02-28 17:25:37 +00:00
|
|
|
components.useCases.searchUseCases.newTabSearch
|
2019-07-18 22:15:24 +00:00
|
|
|
.invoke(
|
2020-11-09 11:16:48 +00:00
|
|
|
searchTermOrURL,
|
2020-07-16 20:34:50 +00:00
|
|
|
SessionState.Source.USER_ENTERED,
|
2019-07-18 22:15:24 +00:00
|
|
|
true,
|
2019-08-07 20:02:08 +00:00
|
|
|
mode.isPrivate,
|
2021-04-21 13:14:50 +00:00
|
|
|
searchEngine = engine
|
2019-07-18 22:15:24 +00:00
|
|
|
)
|
2020-11-09 11:16:48 +00:00
|
|
|
} else {
|
2021-04-21 13:14:50 +00:00
|
|
|
components.useCases.searchUseCases.defaultSearch.invoke(searchTermOrURL, engine)
|
2020-11-09 11:16:48 +00:00
|
|
|
}
|
2019-02-28 17:25:37 +00:00
|
|
|
}
|
2020-07-03 14:47:18 +00:00
|
|
|
|
|
|
|
if (components.core.engine.profiler?.isProfilerActive() == true) {
|
|
|
|
// Wrapping the `addMarker` method with `isProfilerActive` even though it's no-op when
|
|
|
|
// profiler is not active. That way, `text` argument will not create a string builder all the time.
|
2020-08-28 05:57:39 +00:00
|
|
|
components.core.engine.profiler?.addMarker(
|
|
|
|
"HomeActivity.load",
|
|
|
|
startTime,
|
|
|
|
"newTab: $newTab"
|
|
|
|
)
|
2020-07-03 14:47:18 +00:00
|
|
|
}
|
2019-02-28 17:25:37 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 20:12:46 +00:00
|
|
|
internal fun handleRequestDesktopMode() {
|
|
|
|
val requestDesktopSiteUseCase =
|
|
|
|
components.useCases.sessionUseCases.requestDesktopSite
|
|
|
|
requestDesktopSiteUseCase.invoke(true)
|
|
|
|
components.core.store.dispatch(
|
|
|
|
ContentAction.UpdateDesktopModeAction(
|
|
|
|
components.core.store.state.selectedTabId.toString(), true
|
|
|
|
)
|
|
|
|
)
|
|
|
|
// Reset preference value after opening the tab in desktop mode
|
|
|
|
settings().openNextTabInDesktopMode = false
|
|
|
|
}
|
|
|
|
|
2020-10-02 22:39:03 +00:00
|
|
|
open fun navigateToBrowserOnColdStart() {
|
|
|
|
// Normal tabs + cold start -> Should go back to browser if we had any tabs open when we left last
|
|
|
|
// except for PBM + Cold Start there won't be any tabs since they're evicted so we never will navigate
|
2020-11-17 20:28:59 +00:00
|
|
|
if (settings().shouldReturnToBrowser &&
|
2020-10-21 20:29:06 +00:00
|
|
|
!browsingModeManager.mode.isPrivate
|
|
|
|
) {
|
2020-10-02 22:39:03 +00:00
|
|
|
openToBrowser(BrowserDirection.FromGlobal, null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 17:45:33 +00:00
|
|
|
override fun attachBaseContext(base: Context) {
|
2020-09-28 22:42:14 +00:00
|
|
|
base.components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
|
2020-06-08 17:45:33 +00:00
|
|
|
super.attachBaseContext(base)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
16373 Count the # of inflations done on startup (#16778)
* For #16373: Added performance Inflater to counter # of inflations
This class is quite straight forward. The only thing that I have to point out is the onCreateView method. It usually
calls its super if you don't override it. The problem with that is that the super.onCreateView actually uses
android.view. as a prefix for the XML element it tries to inflate. So if we have an element that isn't part
of that package, it'll crash. As I said in the code, a good example is ImageButton. Calling android.view.ImageButton
will make the app crash. The method is implemented the same way that PhoneLayoutInflater does (Another example
is the AsyncLayoutInflater)
* For #16373: Added test for PerformanceInflater
This test got quite awkward / complicated fast. I wanted to test the to make sure we don't break *any* of our layouts
and to do so, I decided to just retrieve all our XML in our /res/layout folder. However, this gets quite a bit outside of a unit test scope.
The point was to get every layouts and get their LayoutID through the resources using the testContext we have. It gets even weirder, since some
of the XML tags have special implementation in android. One of them is the <fragment> tag. That tag actually is inflated by the OS using the Factory2
that the Activity.java implements. In order to get around the fragment issue, we just return a basic FrameLayout since the system LayoutInflater doesn't deal
won't ever get a <fragment> tag to inflate. Another issue was the <merge> tag. In order to inflate those, you need 1) a root view and 2) attach your view to it.
In order to be able to test those layouts file, I had to create an empty FrameLayout and use it as the root view for testing. Again, I know this is beyond the spirit of a unit test but if we use this inflater, I think it should make sure that no layouts are broken by it.
* For #16373: Overrode getSystemService to return PerformanceInflater
This allows PerformanceInflater to be called in every inflation to keep track of the number of inflations we do.
* For #16373: Added UI test for # of inflations
* For #16373: Lint fix
* For #167373: Changed the LayoutInflater cloneInContext to take this instead of inflater
The inflater parameter is set on the first call from the OS from the Window object. However, the activity itself sets multiple factories on the inflater
during its creation (usually through AppCompatDelegateImpl.java). This means that, once we initially set the inflater with a null check, we pass an inflater
that has no factory initially. However, since we keep a reference to it, when cloneInContext was called, it cloned the inflater with the original inflater
which didn't have any factories set up. This meant that the app would crash on either browserFragment creation or any thing that required appCompat (such as
ImageView and ImageButton). Now, passing itself with a cloneInContext means we keep all the factories initially set by the activity or the fragment.
* For #16373: Fixed code issues for PR. No behavior change
* For #16373: fixed some code nits
2021-01-04 16:00:34 +00:00
|
|
|
override fun getSystemService(name: String): Any? {
|
2021-04-08 19:20:18 +00:00
|
|
|
// Issue #17759 had a crash with the PerformanceInflater.kt on Android 5.0 and 5.1
|
|
|
|
// when using the TimePicker. Since the inflater was created for performance monitoring
|
|
|
|
// purposes and that we test on new android versions, this means that any difference in
|
|
|
|
// inflation will be caught on those devices.
|
|
|
|
if (LAYOUT_INFLATER_SERVICE == name && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
16373 Count the # of inflations done on startup (#16778)
* For #16373: Added performance Inflater to counter # of inflations
This class is quite straight forward. The only thing that I have to point out is the onCreateView method. It usually
calls its super if you don't override it. The problem with that is that the super.onCreateView actually uses
android.view. as a prefix for the XML element it tries to inflate. So if we have an element that isn't part
of that package, it'll crash. As I said in the code, a good example is ImageButton. Calling android.view.ImageButton
will make the app crash. The method is implemented the same way that PhoneLayoutInflater does (Another example
is the AsyncLayoutInflater)
* For #16373: Added test for PerformanceInflater
This test got quite awkward / complicated fast. I wanted to test the to make sure we don't break *any* of our layouts
and to do so, I decided to just retrieve all our XML in our /res/layout folder. However, this gets quite a bit outside of a unit test scope.
The point was to get every layouts and get their LayoutID through the resources using the testContext we have. It gets even weirder, since some
of the XML tags have special implementation in android. One of them is the <fragment> tag. That tag actually is inflated by the OS using the Factory2
that the Activity.java implements. In order to get around the fragment issue, we just return a basic FrameLayout since the system LayoutInflater doesn't deal
won't ever get a <fragment> tag to inflate. Another issue was the <merge> tag. In order to inflate those, you need 1) a root view and 2) attach your view to it.
In order to be able to test those layouts file, I had to create an empty FrameLayout and use it as the root view for testing. Again, I know this is beyond the spirit of a unit test but if we use this inflater, I think it should make sure that no layouts are broken by it.
* For #16373: Overrode getSystemService to return PerformanceInflater
This allows PerformanceInflater to be called in every inflation to keep track of the number of inflations we do.
* For #16373: Added UI test for # of inflations
* For #16373: Lint fix
* For #167373: Changed the LayoutInflater cloneInContext to take this instead of inflater
The inflater parameter is set on the first call from the OS from the Window object. However, the activity itself sets multiple factories on the inflater
during its creation (usually through AppCompatDelegateImpl.java). This means that, once we initially set the inflater with a null check, we pass an inflater
that has no factory initially. However, since we keep a reference to it, when cloneInContext was called, it cloned the inflater with the original inflater
which didn't have any factories set up. This meant that the app would crash on either browserFragment creation or any thing that required appCompat (such as
ImageView and ImageButton). Now, passing itself with a cloneInContext means we keep all the factories initially set by the activity or the fragment.
* For #16373: Fixed code issues for PR. No behavior change
* For #16373: fixed some code nits
2021-01-04 16:00:34 +00:00
|
|
|
if (inflater == null) {
|
|
|
|
inflater = PerformanceInflater(LayoutInflater.from(baseContext), this)
|
|
|
|
}
|
|
|
|
return inflater
|
|
|
|
}
|
|
|
|
return super.getSystemService(name)
|
|
|
|
}
|
|
|
|
|
2020-02-23 05:46:50 +00:00
|
|
|
protected open fun createBrowsingModeManager(initialMode: BrowsingMode): BrowsingModeManager {
|
2020-07-21 17:47:10 +00:00
|
|
|
return DefaultBrowsingModeManager(initialMode, components.settings) { newMode ->
|
2021-02-04 23:42:05 +00:00
|
|
|
updateSecureWindowFlags(newMode)
|
2020-02-23 05:46:50 +00:00
|
|
|
themeManager.currentTheme = newMode
|
2021-02-04 23:42:05 +00:00
|
|
|
}.also {
|
|
|
|
updateSecureWindowFlags(initialMode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun updateSecureWindowFlags(mode: BrowsingMode = browsingModeManager.mode) {
|
|
|
|
if (mode == BrowsingMode.Private && !settings().allowScreenshotsInPrivateMode) {
|
|
|
|
window.addFlags(FLAG_SECURE)
|
|
|
|
} else {
|
|
|
|
window.clearFlags(FLAG_SECURE)
|
2020-02-23 05:46:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:00:49 +00:00
|
|
|
protected open fun createThemeManager(): ThemeManager {
|
|
|
|
return DefaultThemeManager(browsingModeManager.mode, this)
|
2019-05-30 22:49:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 06:41:52 +00:00
|
|
|
private fun openPopup(webExtensionState: WebExtensionState) {
|
|
|
|
val action = NavGraphDirections.actionGlobalWebExtensionActionPopupFragment(
|
|
|
|
webExtensionId = webExtensionState.id,
|
|
|
|
webExtensionTitle = webExtensionState.name
|
|
|
|
)
|
2021-04-14 00:48:45 +00:00
|
|
|
navHost.navController.navigateBlockingForAsyncNavGraph(action)
|
2020-02-04 06:41:52 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 08:24:48 +00:00
|
|
|
/**
|
|
|
|
* The root container is null at this point, so let the HomeActivity know that
|
|
|
|
* we are visually complete.
|
|
|
|
*/
|
2020-08-17 22:05:06 +00:00
|
|
|
fun setVisualCompletenessQueueReady() {
|
2020-01-23 08:24:48 +00:00
|
|
|
isVisuallyComplete = true
|
|
|
|
}
|
|
|
|
|
2020-06-26 00:35:52 +00:00
|
|
|
private fun captureSnapshotTelemetryMetrics() = CoroutineScope(Dispatchers.IO).launch {
|
|
|
|
// PWA
|
|
|
|
val recentlyUsedPwaCount = components.core.webAppShortcutManager.recentlyUsedWebAppsCount(
|
|
|
|
activeThresholdMs = PWA_RECENTLY_USED_THRESHOLD
|
|
|
|
)
|
|
|
|
if (recentlyUsedPwaCount == 0) {
|
|
|
|
Metrics.hasRecentPwas.set(false)
|
|
|
|
} else {
|
|
|
|
Metrics.hasRecentPwas.set(true)
|
|
|
|
// This metric's lifecycle is set to 'application', meaning that it gets reset upon
|
|
|
|
// application restart. Combined with the behaviour of the metric type itself (a growing counter),
|
|
|
|
// it's important that this metric is only set once per application's lifetime.
|
|
|
|
// Otherwise, we're going to over-count.
|
|
|
|
Metrics.recentlyUsedPwaCount.add(recentlyUsedPwaCount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 18:33:16 +00:00
|
|
|
@VisibleForTesting
|
2020-07-07 17:31:05 +00:00
|
|
|
internal fun isActivityColdStarted(startingIntent: Intent, activityIcicle: Bundle?): Boolean {
|
2020-06-19 18:33:16 +00:00
|
|
|
// First time opening this activity in the task.
|
|
|
|
// Cold start / start from Recents after back press.
|
2020-07-07 17:31:05 +00:00
|
|
|
return activityIcicle == null &&
|
|
|
|
// Activity was restarted from Recents after it was destroyed by Android while in background
|
|
|
|
// in cases of memory pressure / "Don't keep activities".
|
|
|
|
startingIntent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0
|
|
|
|
}
|
2020-06-19 18:33:16 +00:00
|
|
|
|
2019-02-06 22:31:39 +00:00
|
|
|
companion object {
|
|
|
|
const val OPEN_TO_BROWSER = "open_to_browser"
|
2019-07-11 17:24:58 +00:00
|
|
|
const val OPEN_TO_BROWSER_AND_LOAD = "open_to_browser_and_load"
|
|
|
|
const val OPEN_TO_SEARCH = "open_to_search"
|
2019-09-11 17:52:33 +00:00
|
|
|
const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
|
2019-08-28 21:41:37 +00:00
|
|
|
const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open"
|
|
|
|
const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open"
|
2020-06-25 01:35:11 +00:00
|
|
|
const val START_IN_RECENTS_SCREEN = "start_in_recents_screen"
|
2020-06-26 00:35:52 +00:00
|
|
|
|
|
|
|
// PWA must have been used within last 30 days to be considered "recently used" for the
|
|
|
|
// telemetry purposes.
|
|
|
|
const val PWA_RECENTLY_USED_THRESHOLD = DateUtils.DAY_IN_MILLIS * 30L
|
2019-02-06 22:31:39 +00:00
|
|
|
}
|
2018-12-07 20:11:50 +00:00
|
|
|
}
|