Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
For #419: Create launch icon for private browsing (#4948)
Browse files Browse the repository at this point in the history
  • Loading branch information
jyeontaek authored Sep 11, 2019
1 parent 9033b8d commit 2e2bac4
Show file tree
Hide file tree
Showing 43 changed files with 595 additions and 12 deletions.
9 changes: 9 additions & 0 deletions app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
<activity android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
android:exported="true"/>
<service android:name="com.facebook.flipper.plugins.leakcanary.RecordLeakService" />

<activity
android:name=".HomeActivity">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts_debug"
tools:node="replace" />
</activity>

</application>

</manifest>
29 changes: 29 additions & 0 deletions app/src/debug/res/xml/shortcuts_debug.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="open_new_tab"
android:enabled="true"
android:icon="@drawable/ic_static_shortcut_tab"
android:shortcutShortLabel="@string/home_screen_shortcut_open_new_tab"
android:shortcutLongLabel="@string/home_screen_shortcut_open_new_tab">
<intent
android:action="org.mozilla.fenix.OPEN_TAB"
android:targetPackage="org.mozilla.fenix.debug"
android:targetClass="org.mozilla.fenix.IntentReceiverActivity" />
</shortcut>
<shortcut
android:shortcutId="open_new_private_tab"
android:enabled="true"
android:icon="@drawable/ic_static_shortcut_private_tab"
android:shortcutShortLabel="@string/home_screen_shortcut_open_new_private_tab"
android:shortcutLongLabel="@string/home_screen_shortcut_open_new_private_tab">
<intent
android:action="org.mozilla.fenix.OPEN_PRIVATE_TAB"
android:targetPackage="org.mozilla.fenix.debug"
android:targetClass="org.mozilla.fenix.IntentReceiverActivity" />
</shortcut>
</shortcuts>
53 changes: 53 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>

<!-- Needed to prompt the user to give permission to install a downloaded apk -->
<uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Expand Down Expand Up @@ -58,6 +59,10 @@
<data android:scheme="fenix"
android:host="make_default_browser"/>
</intent-filter>

<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>

<activity
Expand Down Expand Up @@ -118,6 +123,54 @@
android:resource="@mipmap/ic_launcher" />
</activity>

<activity-alias
android:name="org.mozilla.fenix.alias.IntentReceiverActivity"
android:label="@string/app_name_private"
android:icon="@mipmap/ic_launcher_private"
android:roundIcon="@mipmap/ic_launcher_private_round"
android:targetActivity=".IntentReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />

<data android:scheme="http" />
<data android:scheme="https" />
<data android:mimeType="text/html" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/xhtml+xml" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.ASSIST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<meta-data
android:name="com.android.systemui.action_assist_icon"
android:resource="@mipmap/ic_launcher_private" />
<meta-data
android:name="private"
android:value="true" />
</activity-alias>

<activity
android:name=".browser.BrowserPerformanceTestActivity"
android:enabled="${isRaptorEnabled}"
Expand Down
Binary file added app/src/main/ic_launcher_private-web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions app/src/main/java/org/mozilla/fenix/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setPrivateModeIfNecessary()

components.publicSuffixList.prefetch()
setupThemeAndBrowsingMode()

Expand Down Expand Up @@ -169,6 +171,20 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
}
}

/**
* External sources such as 3rd party links and shortcuts use this function to enter
* private mode directly before the content view is created.
*/
private fun setPrivateModeIfNecessary() {
intent?.toSafeIntent()?.let {
if (it.hasExtra(PRIVATE_BROWSING_MODE)) {
val startPrivateMode = it.getBooleanExtra(PRIVATE_BROWSING_MODE, false)
settings.usePrivateMode = startPrivateMode
intent.removeExtra(PRIVATE_BROWSING_MODE)
}
}
}

private fun setupThemeAndBrowsingMode() {
browsingModeManager = createBrowsingModeManager()
themeManager = createThemeManager()
Expand Down Expand Up @@ -347,6 +363,7 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
const val OPEN_TO_BROWSER = "open_to_browser"
const val OPEN_TO_BROWSER_AND_LOAD = "open_to_browser_and_load"
const val OPEN_TO_SEARCH = "open_to_search"
const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open"
const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open"
}
Expand Down
28 changes: 27 additions & 1 deletion app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package org.mozilla.fenix

import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.speech.RecognizerIntent
import kotlinx.coroutines.MainScope
Expand All @@ -17,6 +18,7 @@ import org.mozilla.fenix.customtabs.CustomTabActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.intent.StartSearchIntentProcessor

class IntentReceiverActivity : Activity() {

Expand All @@ -33,7 +35,10 @@ class IntentReceiverActivity : Activity() {
return
}

val isPrivate = this.settings.usePrivateMode
val isPrivate = packageManager
?.getActivityInfo(componentName, PackageManager.GET_META_DATA)
?.metaData
?.getBoolean(PRIVATE_MODE) ?: this.settings.usePrivateMode

MainScope().launch {
// The intent property is nullable, but the rest of the code below
Expand Down Expand Up @@ -79,6 +84,24 @@ class IntentReceiverActivity : Activity() {
// from a session that the user already "erased".
intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0
}
intent.action == ACTION_OPEN_TAB || intent.action == ACTION_OPEN_PRIVATE_TAB -> {
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
Expand Down Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
49 changes: 47 additions & 2 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -251,6 +257,10 @@ class HomeFragment : Fragment(), AccountObserver {
) { newMode ->
invokePendingDeleteJobs()

if (newMode == BrowsingMode.Private) {
requireContext().settings.incrementNumTimesPrivateModeOpened()
}

if (onboarding.userHasBeenOnboarded()) {
getManagedEmitter<SessionControlChange>().onNext(
SessionControlChange.ModeChange(Mode.fromBrowsingMode(newMode))
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<Button>(R.id.cfr_pos_button).apply {
setOnClickListener {
PrivateShortcutCreateManager.createPrivateShortcut(context)
trackingOnboarding.dismiss()
}
}
layout.findViewById<Button>(R.id.cfr_neg_button).apply {
setOnClickListener { trackingOnboarding.dismiss() }
}
// We want to show the popup only after privateBrowsingButton is available.
// Otherwise, we will encounter an activity token error.
privateBrowsingButton.post {
trackingOnboarding.showAsDropDown(privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END)
}
}
}

private fun hideOnboardingIfNeeded() {
if (!onboarding.userHasBeenOnboarded()) {
onboarding.finish()
Expand Down Expand Up @@ -896,6 +939,8 @@ class HomeFragment : Fragment(), AccountObserver {
private const val TELEMETRY_HOME_IDENITIFIER = "home"
private const val SHARED_TRANSITION_MS = 200L
private const val TAB_ITEM_TRANSITION_NAME = "tab_item"
private const val CFR_WIDTH_DIVIDER = 1.7
private const val CFR_Y_OFFSET = -20
}
}

Expand Down
Loading

0 comments on commit 2e2bac4

Please sign in to comment.