Commit f39de0b6 authored by Matthew Finkel's avatar Matthew Finkel
Browse files

Bug 40028: Integrate Tor Controller into HomeFragment

parent 5bf121ef
......@@ -51,6 +51,7 @@ import org.mozilla.fenix.push.WebPushEngineIntegration
import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
import org.mozilla.fenix.session.VisibilityLifecycleCallback
import org.mozilla.fenix.utils.BrowsersCache
import org.torproject.android.service.util.Prefs
/**
*The main application class for Fenix. Records data to measure initialization performance.
......@@ -64,6 +65,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
private val logger = Logger("FenixApplication")
var terminating = false
open val components by lazy { Components(this) }
var visibilityLifecycleCallback: VisibilityLifecycleCallback? = null
......@@ -93,6 +96,21 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
setupInMainProcessOnly()
}
fun isTerminating() = terminating
fun terminate() {
onTerminate()
System.exit(0)
}
override fun onTerminate() {
terminating = true
super.onTerminate()
components.torController.stop()
components.torController.stopTor()
}
protected open fun initializeGlean() {
val telemetryEnabled = settings().isTelemetryEnabled
......@@ -141,6 +159,9 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
if (!megazordSetup.isCompleted) {
runBlocking { megazordSetup.await(); }
}
// Give TAS the base Context
Prefs.setContext(applicationContext)
}
setupLeakCanary()
......@@ -159,6 +180,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
initVisualCompletenessQueueAndQueueTasks()
components.appStartupTelemetry.onFenixApplicationOnCreate()
components.torController.start()
}
private fun restoreDownloads() {
......
......@@ -4,10 +4,9 @@
package org.mozilla.fenix
import android.content.BroadcastReceiver
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
......@@ -25,7 +24,6 @@ import androidx.annotation.VisibleForTesting.PROTECTED
import androidx.appcompat.app.ActionBar
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.NavDestination
import androidx.navigation.NavDirections
import androidx.navigation.fragment.NavHostFragment
......@@ -107,9 +105,6 @@ import org.mozilla.fenix.tabtray.TabTrayDialogFragmentDirections
import org.mozilla.fenix.theme.DefaultThemeManager
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.utils.BrowsersCache
import org.torproject.android.service.TorService
import org.torproject.android.service.TorServiceConstants
import org.torproject.android.service.util.Prefs
import java.lang.ref.WeakReference
/**
......@@ -133,6 +128,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
private var isToolbarInflated = false
private var isBeingRecreated = false
private val webExtensionPopupFeature by lazy {
WebExtensionPopupFeature(components.core.store, ::openPopup)
}
......@@ -157,9 +154,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
private lateinit var navigationToolbar: Toolbar
final override fun onCreate(savedInstanceState: Bundle?) {
// Give Orbot the base Context
Prefs.setContext(applicationContext)
StrictModeManager.changeStrictModePolicies(supportFragmentManager)
// There is disk read violations on some devices such as samsung and pixel for android 9/10
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
......@@ -365,6 +359,14 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
)
privateNotificationObserver?.stop()
if (!isBeingRecreated && !(application as FenixApplication).isTerminating()) {
// We assume the Activity is being destroyed because the user
// swiped away the app on the Recent screen. When this happens,
// we assume the user expects the entire Application is destroyed
// and not only the top Activity/Task. Therefore we kill the
// underlying Application, as well.
(application as FenixApplication).terminate()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
......@@ -384,6 +386,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
message = "recreate()"
)
isBeingRecreated = true
super.recreate()
}
......
......@@ -19,6 +19,7 @@ import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.components.metrics.AppStartupTelemetry
import org.mozilla.fenix.tor.TorController
import org.mozilla.fenix.utils.ClipboardHandler
import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.Settings
......@@ -121,4 +122,6 @@ class Components(private val context: Context) {
FenixReviewSettings(settings)
)
}
val torController by lazy { TorController(context) }
}
......@@ -73,6 +73,7 @@ import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.android.content.res.resolveAttribute
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
......@@ -105,6 +106,8 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.DefaultTopSite
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
import org.mozilla.fenix.tor.TorEvents
import org.mozilla.fenix.tor.bootstrap.TorQuickStart
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.utils.FragmentPreDrawManager
import org.mozilla.fenix.utils.ToolbarPopupWindow
......@@ -165,6 +168,7 @@ class HomeFragment : Fragment() {
private lateinit var currentMode: CurrentMode
private val topSitesFeature = ViewBoundFeatureWrapper<TopSitesFeature>()
private val torQuickStart by lazy { TorQuickStart(requireContext()) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -190,6 +194,9 @@ class HomeFragment : Fragment() {
currentMode = CurrentMode(
view.context,
onboarding,
torQuickStart,
!BuildConfig.DISABLE_TOR,
components.torController,
browsingModeManager,
::dispatchModeChanges
)
......@@ -243,7 +250,10 @@ class HomeFragment : Fragment() {
registerCollectionStorageObserver = ::registerCollectionStorageObserver,
showDeleteCollectionPrompt = ::showDeleteCollectionPrompt,
showTabTray = ::openTabTray,
handleSwipedItemDeletionCancel = ::handleSwipedItemDeletionCancel
handleSwipedItemDeletionCancel = ::handleSwipedItemDeletionCancel,
handleTorBootstrapConnect = ::handleTorBootstrapConnect,
cancelTorBootstrap = ::cancelTorBootstrap,
initiateTorBootstrap = ::initiateTorBootstrap
)
)
......@@ -258,6 +268,9 @@ class HomeFragment : Fragment() {
updateSessionControlView(view)
activity.themeManager.applyStatusBarTheme(activity)
adjustHomeFragmentView(currentMode.getCurrentMode(), view)
return view
}
......@@ -329,6 +342,44 @@ class HomeFragment : Fragment() {
}
}
private fun adjustHomeFragmentView(mode: Mode, view: View?) {
if (mode == Mode.Bootstrap) {
view?.sessionControlRecyclerView?.apply {
setPadding(0, 0, 0, 0)
(getLayoutParams() as ViewGroup.MarginLayoutParams).setMargins(0, 0, 0, 0)
}
view?.homeAppBar?.apply {
visibility = View.GONE
}
view?.toolbarLayout?.apply {
visibility = View.GONE
}
} else {
// Keep synchronized with xml layout (somehow).
view?.sessionControlRecyclerView?.apply {
setPadding(
SESSION_CONTROL_VIEW_PADDING,
SESSION_CONTROL_VIEW_PADDING,
SESSION_CONTROL_VIEW_PADDING,
SESSION_CONTROL_VIEW_PADDING
)
// Default margin until it is re-set below (either set immediately or after Layout)
(getLayoutParams() as ViewGroup.MarginLayoutParams).setMargins(
0,
0,
0,
DEFAULT_ONBOARDING_FINISH_MARGIN
)
}
view?.homeAppBar?.apply {
visibility = View.VISIBLE
}
view?.toolbarLayout?.apply {
visibility = View.VISIBLE
}
}
}
@Suppress("LongMethod", "ComplexMethod")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......@@ -620,8 +671,12 @@ class HomeFragment : Fragment() {
}
private fun dispatchModeChanges(mode: Mode) {
if (mode != Mode.fromBrowsingMode(browsingModeManager.mode)) {
homeFragmentStore.dispatch(HomeFragmentAction.ModeChange(mode))
homeFragmentStore.dispatch(HomeFragmentAction.ModeChange(mode))
val localView = view
adjustHomeFragmentView(mode, view)
if (localView != null) {
updateSessionControlView(localView)
}
}
......@@ -661,6 +716,8 @@ class HomeFragment : Fragment() {
}
homeViewModel.layoutManagerState =
sessionControlView!!.view.layoutManager?.onSaveInstanceState()
currentMode.unregisterTorListener()
}
override fun onResume() {
......@@ -993,6 +1050,18 @@ class HomeFragment : Fragment() {
view?.sessionControlRecyclerView?.adapter?.notifyDataSetChanged()
}
private fun handleTorBootstrapConnect() {
requireComponents.torController.onTorConnecting()
}
private fun cancelTorBootstrap() {
requireComponents.torController.stopTor()
}
private fun initiateTorBootstrap(withDebugLogging: Boolean = false) {
requireComponents.torController.initiateTorBootstrap(lifecycleScope, withDebugLogging)
}
companion object {
const val ALL_NORMAL_TABS = "all_normal"
const val ALL_PRIVATE_TABS = "all_private"
......@@ -1008,5 +1077,9 @@ class HomeFragment : Fragment() {
private const val ANIM_SNACKBAR_DELAY = 100L
private const val CFR_WIDTH_DIVIDER = 1.7
private const val CFR_Y_OFFSET = -20
// Layout
private const val DEFAULT_ONBOARDING_FINISH_MARGIN = 60
private const val SESSION_CONTROL_VIEW_PADDING = 16
}
}
......@@ -10,6 +10,9 @@ import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
import mozilla.components.service.fxa.sharing.ShareableAccount
import org.mozilla.fenix.tor.TorController
import org.mozilla.fenix.tor.TorEvents
import org.mozilla.fenix.tor.bootstrap.TorQuickStart
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.ext.components
......@@ -22,6 +25,7 @@ sealed class Mode {
object Normal : Mode()
object Private : Mode()
data class Onboarding(val state: OnboardingState) : Mode()
object Bootstrap : Mode()
companion object {
fun fromBrowsingMode(browsingMode: BrowsingMode) = when (browsingMode) {
......@@ -43,16 +47,26 @@ sealed class OnboardingState {
object SignedIn : OnboardingState()
}
@SuppressWarnings("LongParameterList", "TooManyFunctions")
class CurrentMode(
private val context: Context,
private val onboarding: FenixOnboarding,
private val torQuickStart: TorQuickStart,
private val shouldStartTor: Boolean,
private val torController: TorController,
private val browsingModeManager: BrowsingModeManager,
private val dispatchModeChanges: (mode: Mode) -> Unit
) : AccountObserver {
) : AccountObserver, TorEvents {
private val accountManager by lazy { context.components.backgroundServices.accountManager }
fun getCurrentMode() = if (onboarding.userHasBeenOnboarded()) {
init {
torController.registerTorListener(this)
}
fun getCurrentMode() = if (shouldStartTor && (!torQuickStart.quickStartTor() && !torController.isBootstrapped)) {
Mode.Bootstrap
} else if (onboarding.userHasBeenOnboarded()) {
Mode.fromBrowsingMode(browsingModeManager.mode)
} else {
val account = accountManager.authenticatedAccount()
......@@ -72,6 +86,26 @@ class CurrentMode(
dispatchModeChanges(getCurrentMode())
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorConnecting() {
}
override fun onTorConnected() {
dispatchModeChanges(getCurrentMode())
}
override fun onTorStopped() {
dispatchModeChanges(getCurrentMode())
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorStatusUpdate(entry: String?, status: String?) {
}
fun unregisterTorListener() {
torController.unregisterTorListener(this)
}
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) = emitModeChanges()
override fun onAuthenticationProblems() = emitModeChanges()
override fun onLoggedOut() = emitModeChanges()
......
......@@ -149,6 +149,26 @@ interface SessionControlController {
* @see [CollectionInteractor.onRemoveCollectionsPlaceholder]
*/
fun handleRemoveCollectionsPlaceholder()
/**
* @see [TorBootstrapInteractor.onTorBootstrapConnectClicked]
*/
fun handleTorBootstrapConnectClicked()
/**
* @see [TorBootstrapInteractor.onTorBootstrapConnectingClicked]
*/
fun handleTorBootstrapConnectingClicked()
/**
* @see [TorBootstrapInteractor.onTorStartBootstrapping]
*/
fun handleTorStartBootstrapping()
/**
* @see [TorBootstrapInteractor.onTorStartDebugBootstrapping]
*/
fun handleTorStartDebugBootstrapping()
}
@Suppress("TooManyFunctions", "LargeClass")
......@@ -173,7 +193,10 @@ class DefaultSessionControlController(
handleSwipedItemDeletionCancel: () -> Unit
) -> Unit,
private val showTabTray: () -> Unit,
private val handleSwipedItemDeletionCancel: () -> Unit
private val handleSwipedItemDeletionCancel: () -> Unit,
private val handleTorBootstrapConnect: () -> Unit,
private val initiateTorBootstrap: (Boolean) -> Unit,
private val cancelTorBootstrap: () -> Unit
) : SessionControlController {
override fun handleCollectionAddTabTapped(collection: TabCollection) {
......@@ -434,4 +457,20 @@ class DefaultSessionControlController(
)
navController.nav(R.id.homeFragment, directions)
}
override fun handleTorBootstrapConnectClicked() {
handleTorBootstrapConnect()
}
override fun handleTorBootstrapConnectingClicked() {
cancelTorBootstrap()
}
override fun handleTorStartBootstrapping() {
initiateTorBootstrap(false)
}
override fun handleTorStartDebugBootstrapping() {
initiateTorBootstrap(true)
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment