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