diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml
index 45846ce14..922467614 100644
--- a/app/src/debug/AndroidManifest.xml
+++ b/app/src/debug/AndroidManifest.xml
@@ -22,6 +22,15 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug/res/xml/shortcuts_debug.xml b/app/src/debug/res/xml/shortcuts_debug.xml
new file mode 100644
index 000000000..0ac003ac7
--- /dev/null
+++ b/app/src/debug/res/xml/shortcuts_debug.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1373ba7e3..07df0726c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
+
@@ -58,6 +59,10 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
+ intent.setClassName(applicationContext, HomeActivity::class.java.name)
+ val startPrivateMode = (intent.action == ACTION_OPEN_PRIVATE_TAB)
+ if (startPrivateMode) {
+ intent.putExtra(
+ HomeActivity.OPEN_TO_SEARCH,
+ StartSearchIntentProcessor.STATIC_SHORTCUT_NEW_PRIVATE_TAB
+ )
+ } else {
+ intent.putExtra(
+ HomeActivity.OPEN_TO_SEARCH,
+ StartSearchIntentProcessor.STATIC_SHORTCUT_NEW_TAB
+ )
+ }
+ intent.putExtra(HomeActivity.PRIVATE_BROWSING_MODE, startPrivateMode)
+ intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ true
+ }
else -> {
intent.setClassName(applicationContext, HomeActivity::class.java.name)
false
@@ -124,5 +147,8 @@ class IntentReceiverActivity : Activity() {
private const val SPEECH_REQUEST_CODE = 0
const val SPEECH_PROCESSING = "speech_processing"
const val PREVIOUS_INTENT = "previous_intent"
+ const val PRIVATE_MODE = "private"
+ const val ACTION_OPEN_TAB = "org.mozilla.fenix.OPEN_TAB"
+ const val ACTION_OPEN_PRIVATE_TAB = "org.mozilla.fenix.OPEN_PRIVATE_TAB"
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt b/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt
new file mode 100644
index 000000000..d2e0316d6
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.components
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.graphics.drawable.IconCompat
+import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.R
+import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
+import java.util.UUID
+
+/**
+ * Handles the creation of pinned shortcuts.
+ */
+object PrivateShortcutCreateManager {
+
+ fun createPrivateShortcut(context: Context) {
+ if (!ShortcutManagerCompat.isRequestPinShortcutSupported(context)) return
+
+ val icon = IconCompat.createWithResource(context, R.mipmap.ic_launcher_private_round)
+ val shortcut = ShortcutInfoCompat.Builder(context, UUID.randomUUID().toString())
+ .setShortLabel(context.getString(R.string.app_name_private))
+ .setLongLabel(context.getString(R.string.app_name_private))
+ .setIcon(icon)
+ .setIntent(Intent(context, HomeActivity::class.java).apply {
+ action = Intent.ACTION_VIEW
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ putExtra(HomeActivity.PRIVATE_BROWSING_MODE, true)
+ putExtra(
+ HomeActivity.OPEN_TO_SEARCH,
+ StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT
+ )
+ })
+ .build()
+ val homeScreenIntent = Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ val intentSender = PendingIntent
+ .getActivity(context, 0, homeScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
+ .intentSender
+ ShortcutManagerCompat.requestPinShortcut(context, shortcut, intentSender)
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
index 6f1ed818b..4cd71f06c 100644
--- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
@@ -9,9 +9,13 @@ import android.content.Context
import android.content.DialogInterface
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.PopupWindow
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
@@ -54,15 +58,18 @@ import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.FenixViewModelProvider
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
+import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.collections.CreateCollectionViewModel
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.FenixSnackbar
+import org.mozilla.fenix.components.PrivateShortcutCreateManager
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.sessionsOfType
+import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.toTab
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
import org.mozilla.fenix.home.sessioncontrol.Mode
@@ -84,7 +91,6 @@ import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.share.ShareTab
import org.mozilla.fenix.utils.FragmentPreDrawManager
-import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.utils.allowUndo
import org.mozilla.fenix.whatsnew.WhatsNew
@@ -212,7 +218,7 @@ class HomeFragment : Fragment(), AccountObserver {
val searchEngine = requireComponents.search.searchEngineManager.getDefaultSearchEngineAsync(
requireContext(),
- Settings.getInstance(requireContext()).defaultSearchEngineName
+ requireContext().settings.defaultSearchEngineName
)
val searchIcon = BitmapDrawable(resources, searchEngine.icon)
searchIcon.setBounds(0, 0, iconSize, iconSize)
@@ -251,6 +257,10 @@ class HomeFragment : Fragment(), AccountObserver {
) { newMode ->
invokePendingDeleteJobs()
+ if (newMode == BrowsingMode.Private) {
+ requireContext().settings.incrementNumTimesPrivateModeOpened()
+ }
+
if (onboarding.userHasBeenOnboarded()) {
getManagedEmitter().onNext(
SessionControlChange.ModeChange(Mode.fromBrowsingMode(newMode))
@@ -290,6 +300,11 @@ class HomeFragment : Fragment(), AccountObserver {
(activity as AppCompatActivity).supportActionBar?.hide()
requireComponents.backgroundServices.accountManager.register(this, owner = this)
+
+ if (requireContext().settings.showPrivateModeContextualFeatureRecommender &&
+ browsingModeManager.mode.isPrivate) {
+ recommendPrivateBrowsingShortcut()
+ }
}
override fun onStart() {
@@ -543,6 +558,34 @@ class HomeFragment : Fragment(), AccountObserver {
homeViewModel.motionLayoutProgress = homeLayout?.progress ?: 0F
}
+ private fun recommendPrivateBrowsingShortcut() {
+ context?.let {
+ val layout = LayoutInflater.from(it)
+ .inflate(R.layout.pbm_shortcut_popup, null)
+ val trackingOnboarding =
+ PopupWindow(
+ layout,
+ (resources.displayMetrics.widthPixels / CFR_WIDTH_DIVIDER).toInt(),
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ true
+ )
+ layout.findViewById