Unverified Commit 49b617c9 authored by Mihai Branescu's avatar Mihai Branescu Committed by GitHub
Browse files

For #9100 - Private browsing notification fixes

Co-authored-by: Seef <Saif Dara>
parent 632b6497
......@@ -43,7 +43,6 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.push.PushFxaIntegration
import org.mozilla.fenix.push.WebPushEngineIntegration
import org.mozilla.fenix.session.NotificationSessionObserver
import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
import org.mozilla.fenix.session.VisibilityLifecycleCallback
import org.mozilla.fenix.utils.BrowsersCache
......@@ -157,9 +156,6 @@ open class FenixApplication : LocaleAwareApplication() {
visibilityLifecycleCallback = VisibilityLifecycleCallback(getSystemService())
registerActivityLifecycleCallbacks(visibilityLifecycleCallback)
val privateNotificationObserver = NotificationSessionObserver(this)
privateNotificationObserver.start()
// Storage maintenance disabled, for now, as it was interfering with background migrations.
// See https://github.com/mozilla-mobile/fenix/issues/7227 for context.
// if ((System.currentTimeMillis() - settings().lastPlacesStorageMaintenance) > ONE_DAY_MILLIS) {
......
......@@ -74,6 +74,7 @@ import org.mozilla.fenix.library.history.HistoryFragmentDirections
import org.mozilla.fenix.perf.Performance
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.search.SearchFragmentDirections
import org.mozilla.fenix.session.NotificationSessionObserver
import org.mozilla.fenix.settings.SettingsFragmentDirections
import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections
import org.mozilla.fenix.settings.about.AboutFragmentDirections
......@@ -154,6 +155,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
sessionObserver = UriOpenedObserver(this)
checkPrivateShortcutEntryPoint(intent)
val privateNotificationObserver = NotificationSessionObserver(this)
privateNotificationObserver.start()
if (isActivityColdStarted(intent, savedInstanceState)) {
externalSourceIntentProcessors.any { it.process(intent, navHost.navController, this.intent) }
}
......@@ -176,6 +181,11 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
StartupTimeline.homeActivityLifecycleObserver
)
StartupTimeline.onActivityCreateEndHome(this)
if (shouldAddToRecentsScreen(intent)) {
intent.removeExtra(START_IN_RECENTS_SCREEN)
moveTaskToBack(true)
}
}
@CallSuper
......@@ -313,6 +323,30 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
return settings().lastKnownMode
}
/**
* 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
}
private fun checkPrivateShortcutEntryPoint(intent: Intent) {
if (intent.hasExtra(OPEN_TO_SEARCH) &&
(intent.getStringExtra(OPEN_TO_SEARCH) ==
StartSearchIntentProcessor.STATIC_SHORTCUT_NEW_PRIVATE_TAB ||
intent.getStringExtra(OPEN_TO_SEARCH) ==
StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT)
) {
NotificationSessionObserver.isStartedFromPrivateShortcut = true
}
}
private fun setupThemeAndBrowsingMode(mode: BrowsingMode) {
settings().lastKnownMode = mode
browsingModeManager = createBrowsingModeManager(mode)
......@@ -499,5 +533,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open"
const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open"
const val delay = 5000L
const val START_IN_RECENTS_SCREEN = "start_in_recents_screen"
}
}
......@@ -34,7 +34,7 @@ class NotificationSessionObserver(
.ifChanged()
.collect { hasPrivateTabs ->
if (hasPrivateTabs) {
notificationService.start(context)
notificationService.start(context, isStartedFromPrivateShortcut)
started = true
} else if (started) {
notificationService.stop(context)
......@@ -47,4 +47,8 @@ class NotificationSessionObserver(
fun stop() {
scope?.cancel()
}
companion object {
var isStartedFromPrivateShortcut = false
}
}
......@@ -37,32 +37,41 @@ import org.mozilla.fenix.ext.sessionsOfType
*/
class SessionNotificationService : Service() {
private var isStartedFromPrivateShortcut: Boolean = false
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val action = intent.action ?: return Service.START_NOT_STICKY
val action = intent.action ?: return START_NOT_STICKY
when (action) {
ACTION_START -> {
isStartedFromPrivateShortcut = intent.getBooleanExtra(STARTED_FROM_PRIVATE_SHORTCUT, false)
createNotificationChannelIfNeeded()
startForeground(NOTIFICATION_ID, buildNotification())
}
ACTION_ERASE -> {
metrics.track(Event.PrivateBrowsingNotificationTapped)
components.core.sessionManager.removeAndCloseAllPrivateSessions()
if (!VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) {
startActivity(
Intent(this, HomeActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
)
val homeScreenIntent = Intent(this, HomeActivity::class.java)
val intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
homeScreenIntent.apply {
setFlags(intentFlags)
putExtra(HomeActivity.PRIVATE_BROWSING_MODE, isStartedFromPrivateShortcut)
}
if (VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) {
// Set start mode to be in background (recents screen)
homeScreenIntent.apply {
putExtra(HomeActivity.START_IN_RECENTS_SCREEN, true)
}
}
startActivity(homeScreenIntent)
components.core.sessionManager.removeAndCloseAllPrivateSessions()
}
else -> throw IllegalStateException("Unknown intent: $intent")
}
return Service.START_NOT_STICKY
return START_NOT_STICKY
}
override fun onTaskRemoved(rootIntent: Intent) {
......@@ -125,13 +134,18 @@ class SessionNotificationService : Service() {
companion object {
private const val NOTIFICATION_ID = 83
private const val NOTIFICATION_CHANNEL_ID = "browsing-session"
private const val STARTED_FROM_PRIVATE_SHORTCUT = "STARTED_FROM_PRIVATE_SHORTCUT"
private const val ACTION_START = "start"
private const val ACTION_ERASE = "erase"
internal fun start(context: Context) {
internal fun start(
context: Context,
startedFromPrivateShortcut: Boolean
) {
val intent = Intent(context, SessionNotificationService::class.java)
intent.action = ACTION_START
intent.putExtra(STARTED_FROM_PRIVATE_SHORTCUT, startedFromPrivateShortcut)
// From Focus #2901: The application is crashing due to the service not calling `startForeground`
// before it times out. This is a speculative fix to decrease the time between these two
......
......@@ -25,6 +25,13 @@ class VisibilityLifecycleCallback(private val activityManager: ActivityManager?)
*/
private var activitiesInStartedState: Int = 0
/**
* Finishes and removes the list of AppTasks only if the application is in the background.
* The application is considered to be in the background if it has at least 1 Activity in the
* started state
* @return True if application is in background (also finishes and removes all AppTasks),
* false otherwise
*/
private fun finishAndRemoveTaskIfInBackground(): Boolean {
if (activitiesInStartedState == 0) {
activityManager?.let {
......@@ -59,6 +66,9 @@ class VisibilityLifecycleCallback(private val activityManager: ActivityManager?)
/**
* If all activities of this app are in the background then finish and remove all tasks. After
* that the app won't show up in "recent apps" anymore.
*
* @return True if application is in background (and consequently, finishes and removes all tasks),
* false otherwise.
*/
internal fun finishAndRemoveTaskIfInBackground(context: Context): Boolean {
return (context.applicationContext as FenixApplication)
......
......@@ -35,6 +35,7 @@ class NotificationSessionObserverTest {
store = BrowserStore()
every { context.components.core.store } returns store
observer = NotificationSessionObserver(context, notificationService)
NotificationSessionObserver.isStartedFromPrivateShortcut = false
}
@Test
......@@ -44,7 +45,7 @@ class NotificationSessionObserverTest {
store.dispatch(TabListAction.AddTabAction(privateSession)).join()
observer.start()
verify(exactly = 1) { notificationService.start(context) }
verify(exactly = 1) { notificationService.start(context, false) }
confirmVerified(notificationService)
}
......@@ -57,10 +58,10 @@ class NotificationSessionObserverTest {
verify { notificationService wasNot Called }
store.dispatch(TabListAction.AddTabAction(normalSession)).join()
verify(exactly = 0) { notificationService.start(context) }
verify(exactly = 0) { notificationService.start(context, false) }
store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join()
verify(exactly = 0) { notificationService.start(context) }
verify(exactly = 0) { notificationService.start(context, false) }
}
@Test
......@@ -74,9 +75,9 @@ class NotificationSessionObserverTest {
verify { notificationService wasNot Called }
store.dispatch(CustomTabListAction.AddCustomTabAction(privateCustomSession)).join()
verify(exactly = 0) { notificationService.start(context) }
verify(exactly = 0) { notificationService.start(context, false) }
store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join()
verify(exactly = 0) { notificationService.start(context) }
verify(exactly = 0) { notificationService.start(context, false) }
}
}
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