2019-01-24 21:07:52 +00:00
|
|
|
/* 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/. */
|
|
|
|
|
2019-01-23 21:39:53 +00:00
|
|
|
package org.mozilla.fenix
|
|
|
|
|
2019-02-13 15:08:35 +00:00
|
|
|
import android.annotation.SuppressLint
|
2019-01-23 21:39:53 +00:00
|
|
|
import android.app.Application
|
2019-04-02 00:53:37 +00:00
|
|
|
import androidx.appcompat.app.AppCompatDelegate
|
|
|
|
import androidx.preference.PreferenceManager
|
2019-01-29 19:20:29 +00:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.GlobalScope
|
|
|
|
import kotlinx.coroutines.launch
|
2019-04-02 21:41:34 +00:00
|
|
|
import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient
|
2019-01-29 19:20:29 +00:00
|
|
|
import mozilla.components.service.fretboard.Fretboard
|
|
|
|
import mozilla.components.service.fretboard.source.kinto.KintoExperimentSource
|
|
|
|
import mozilla.components.service.fretboard.storage.flatfile.FlatFileExperimentStorage
|
|
|
|
import mozilla.components.support.base.log.Log
|
|
|
|
import mozilla.components.support.base.log.logger.Logger
|
|
|
|
import mozilla.components.support.base.log.sink.AndroidLogSink
|
2019-03-14 13:27:54 +00:00
|
|
|
import mozilla.components.support.ktx.android.content.isMainProcess
|
|
|
|
import mozilla.components.support.ktx.android.content.runOnlyInMainProcess
|
2019-02-15 21:19:14 +00:00
|
|
|
import mozilla.components.support.rustlog.RustLog
|
2019-01-23 21:39:53 +00:00
|
|
|
import org.mozilla.fenix.components.Components
|
2019-04-02 00:53:37 +00:00
|
|
|
import org.mozilla.fenix.utils.Settings
|
2019-01-29 19:20:29 +00:00
|
|
|
import java.io.File
|
2019-01-23 21:39:53 +00:00
|
|
|
|
2019-02-13 15:08:35 +00:00
|
|
|
@SuppressLint("Registered")
|
|
|
|
open class FenixApplication : Application() {
|
2019-01-29 19:20:29 +00:00
|
|
|
lateinit var fretboard: Fretboard
|
|
|
|
|
2019-01-23 21:39:53 +00:00
|
|
|
val components by lazy { Components(this) }
|
2019-01-29 16:42:10 +00:00
|
|
|
|
|
|
|
override fun onCreate() {
|
|
|
|
super.onCreate()
|
2019-04-02 00:53:37 +00:00
|
|
|
setDayNightTheme()
|
2019-02-15 21:19:14 +00:00
|
|
|
val megazordEnabled = setupMegazord()
|
|
|
|
setupLogging(megazordEnabled)
|
2019-02-26 16:24:14 +00:00
|
|
|
setupCrashReporting()
|
2019-01-29 16:42:10 +00:00
|
|
|
|
2019-03-14 13:27:54 +00:00
|
|
|
if (!isMainProcess()) {
|
2019-02-26 16:24:14 +00:00
|
|
|
// If this is not the main process then do not continue with the initialization here. Everything that
|
|
|
|
// follows only needs to be done in our app's main process and should not be done in other processes like
|
|
|
|
// a GeckoView child process or the crash handling process. Most importantly we never want to end up in a
|
|
|
|
// situation where we create a GeckoRuntime from the Gecko child process (
|
|
|
|
return
|
2019-02-13 15:08:35 +00:00
|
|
|
}
|
2019-02-26 16:24:14 +00:00
|
|
|
|
2019-02-13 15:08:35 +00:00
|
|
|
setupLeakCanary()
|
2019-01-29 19:20:29 +00:00
|
|
|
loadExperiments()
|
2019-04-03 18:59:08 +00:00
|
|
|
if (Settings.getInstance(this).isTelemetryEnabled) {
|
|
|
|
components.analytics.metrics.start()
|
|
|
|
}
|
2019-01-29 19:20:29 +00:00
|
|
|
}
|
|
|
|
|
2019-02-13 15:08:35 +00:00
|
|
|
protected open fun setupLeakCanary() {
|
|
|
|
// no-op, LeakCanary is disabled by default
|
|
|
|
}
|
|
|
|
|
|
|
|
open fun toggleLeakCanary(newValue: Boolean) {
|
|
|
|
// no-op, LeakCanary is disabled by default
|
|
|
|
}
|
|
|
|
|
2019-02-15 21:19:14 +00:00
|
|
|
private fun setupLogging(megazordEnabled: Boolean) {
|
|
|
|
// We want the log messages of all builds to go to Android logcat
|
|
|
|
Log.addSink(AndroidLogSink())
|
|
|
|
|
|
|
|
if (megazordEnabled) {
|
|
|
|
// We want rust logging to go through the log sinks.
|
|
|
|
// This has to happen after initializing the megazord, and
|
|
|
|
// it's only worth doing in the case that we are a megazord.
|
|
|
|
RustLog.enable()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 19:20:29 +00:00
|
|
|
private fun loadExperiments() {
|
|
|
|
val experimentsFile = File(filesDir, EXPERIMENTS_JSON_FILENAME)
|
|
|
|
val experimentSource = KintoExperimentSource(
|
2019-02-26 16:24:14 +00:00
|
|
|
EXPERIMENTS_BASE_URL,
|
|
|
|
EXPERIMENTS_BUCKET_NAME,
|
|
|
|
EXPERIMENTS_COLLECTION_NAME,
|
2019-04-02 21:41:34 +00:00
|
|
|
// TODO Switch back to components.core.client (see https://github.com/mozilla-mobile/fenix/issues/1329)
|
|
|
|
HttpURLConnectionClient()
|
2019-01-29 19:20:29 +00:00
|
|
|
)
|
2019-01-31 19:16:04 +00:00
|
|
|
// TODO add ValueProvider to keep clientID in sync with Glean when ready
|
|
|
|
fretboard = Fretboard(experimentSource, FlatFileExperimentStorage(experimentsFile))
|
2019-01-29 19:20:29 +00:00
|
|
|
fretboard.loadExperiments()
|
|
|
|
Logger.debug("Bucket is ${fretboard.getUserBucket(this@FenixApplication)}")
|
|
|
|
Logger.debug("Experiments active: ${fretboard.getExperimentsMap(this@FenixApplication)}")
|
|
|
|
GlobalScope.launch(Dispatchers.IO) {
|
|
|
|
fretboard.updateExperiments()
|
|
|
|
}
|
2019-01-29 16:42:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setupCrashReporting() {
|
|
|
|
components
|
|
|
|
.analytics
|
|
|
|
.crashReporter
|
|
|
|
.install(this)
|
|
|
|
}
|
2019-02-15 21:19:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiate Megazord sequence! Megazord Battle Mode!
|
|
|
|
*
|
|
|
|
* Mozilla Application Services publishes many native (Rust) code libraries that stand alone: each published Android
|
|
|
|
* ARchive (AAR) contains managed code (classes.jar) and multiple .so library files (one for each supported
|
|
|
|
* architecture). That means consuming multiple such libraries entails at least two .so libraries, and each of those
|
|
|
|
* libraries includes the entire Rust standard library as well as (potentially many) duplicated dependencies. To
|
|
|
|
* save space and allow cross-component native-code Link Time Optimization (LTO, i.e., inlining, dead code
|
|
|
|
* elimination, etc).
|
|
|
|
* Application Services also publishes composite libraries -- so called megazord libraries or just megazords -- that
|
|
|
|
* compose multiple Rust components into a single optimized .so library file.
|
|
|
|
*
|
|
|
|
* @return Boolean indicating if we're in a megazord.
|
|
|
|
*/
|
|
|
|
private fun setupMegazord(): Boolean {
|
|
|
|
// mozilla.appservices.FenixMegazord will be missing if we're doing an application-services
|
|
|
|
// dependency substitution locally. That class is supplied dynamically by the org.mozilla.appservices
|
|
|
|
// gradle plugin, and that won't happen if we're not megazording. We won't megazord if we're
|
|
|
|
// locally substituting every module that's part of the megazord's definition, which is what
|
|
|
|
// happens during a local substitution of application-services.
|
|
|
|
// As a workaround, use reflections to conditionally initialize the megazord in case it's present.
|
|
|
|
return try {
|
|
|
|
val megazordClass = Class.forName("mozilla.appservices.FenixMegazord")
|
|
|
|
val megazordInitMethod = megazordClass.getDeclaredMethod("init")
|
|
|
|
megazordInitMethod.invoke(megazordClass)
|
|
|
|
true
|
|
|
|
} catch (e: ClassNotFoundException) {
|
|
|
|
Logger.info("mozilla.appservices.FenixMegazord not found; skipping megazord init.")
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2019-02-26 16:24:14 +00:00
|
|
|
|
2019-03-14 13:27:54 +00:00
|
|
|
override fun onTrimMemory(level: Int) {
|
|
|
|
super.onTrimMemory(level)
|
|
|
|
runOnlyInMainProcess {
|
|
|
|
components.core.sessionManager.onLowMemory()
|
2019-02-26 16:24:14 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-02 00:53:37 +00:00
|
|
|
|
|
|
|
private fun setDayNightTheme() {
|
|
|
|
when {
|
|
|
|
Settings.getInstance(this).shouldUseLightTheme -> {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_NO
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Settings.getInstance(this).shouldUseDarkTheme -> {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_YES
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Settings.getInstance(this).shouldUseAutoBatteryTheme -> {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Settings.getInstance(this).shouldFollowDeviceTheme -> {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
|
|
|
)
|
|
|
|
}
|
|
|
|
// First run of app no default set, set the default to Follow System for 28+ and Normal Mode otherwise
|
|
|
|
else -> {
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
|
|
|
)
|
|
|
|
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
|
|
|
.putBoolean(getString(R.string.pref_key_follow_device_theme), true).apply()
|
|
|
|
} else {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
AppCompatDelegate.MODE_NIGHT_NO
|
|
|
|
)
|
|
|
|
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
|
|
|
.putBoolean(getString(R.string.pref_key_light_theme), true).apply()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-26 16:24:14 +00:00
|
|
|
}
|