support private mode for custom tabs
This commit is contained in:
@@ -12,3 +12,4 @@
|
|||||||
build/
|
build/
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
AGENTS.md
|
||||||
|
|||||||
+5
-1
@@ -235,7 +235,9 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||||||
private fun openInBrowser(sessionId: String) {
|
private fun openInBrowser(sessionId: String) {
|
||||||
val activity = requireActivity()
|
val activity = requireActivity()
|
||||||
val store = components.core.store
|
val store = components.core.store
|
||||||
val url = store.state.findCustomTab(sessionId)?.content?.url ?: return
|
val customTab = store.state.findCustomTab(sessionId) ?: return
|
||||||
|
val url = customTab.content.url
|
||||||
|
val isPrivateCustomTab = customTab.content.private
|
||||||
|
|
||||||
sessionFeature?.get()?.release()
|
sessionFeature?.get()?.release()
|
||||||
|
|
||||||
@@ -243,6 +245,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||||||
val mainIntent = Intent(Intent.ACTION_VIEW, android.net.Uri.parse(url)).apply {
|
val mainIntent = Intent(Intent.ACTION_VIEW, android.net.Uri.parse(url)).apply {
|
||||||
setClassName(activity, "eu.weblibre.gecko.MainActivity")
|
setClassName(activity, "eu.weblibre.gecko.MainActivity")
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
putExtra(PRIVATE_BROWSING_MODE, isPrivateCustomTab)
|
||||||
}
|
}
|
||||||
activity.startActivity(mainIntent)
|
activity.startActivity(mainIntent)
|
||||||
|
|
||||||
@@ -354,6 +357,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||||||
companion object {
|
companion object {
|
||||||
private const val CUSTOM_TAB_SESSION_ID_KEY = "custom_tab_session_id"
|
private const val CUSTOM_TAB_SESSION_ID_KEY = "custom_tab_session_id"
|
||||||
private const val WEB_APP_MANIFEST_URL_KEY = "web_app_manifest_url"
|
private const val WEB_APP_MANIFEST_URL_KEY = "web_app_manifest_url"
|
||||||
|
private const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
|
||||||
|
|
||||||
fun create(
|
fun create(
|
||||||
customTabSessionId: String,
|
customTabSessionId: String,
|
||||||
|
|||||||
+2
-1
@@ -11,6 +11,7 @@ import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
|||||||
class AuthIntentReceiverActivity : Activity() {
|
class AuthIntentReceiverActivity : Activity() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "AuthIntentReceiver"
|
private const val TAG = "AuthIntentReceiver"
|
||||||
|
private const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -32,7 +33,7 @@ class AuthIntentReceiverActivity : Activity() {
|
|||||||
val processed = CustomTabIntentProcessor(
|
val processed = CustomTabIntentProcessor(
|
||||||
components.useCases.customTabsUseCases.add,
|
components.useCases.customTabsUseCases.add,
|
||||||
resources,
|
resources,
|
||||||
isPrivate = false,
|
isPrivate = sourceIntent.getBooleanExtra(PRIVATE_BROWSING_MODE, false),
|
||||||
).process(sourceIntent)
|
).process(sourceIntent)
|
||||||
|
|
||||||
if (processed) {
|
if (processed) {
|
||||||
|
|||||||
+5
-1
@@ -42,6 +42,7 @@ import java.io.File
|
|||||||
class IntentReceiverActivity : Activity() {
|
class IntentReceiverActivity : Activity() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "IntentReceiverActivity"
|
private const val TAG = "IntentReceiverActivity"
|
||||||
|
private const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
@@ -81,6 +82,9 @@ class IntentReceiverActivity : Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun routeIntent(intent: Intent) {
|
private fun routeIntent(intent: Intent) {
|
||||||
|
val privateBrowsingMode = intent.getBooleanExtra(PRIVATE_BROWSING_MODE, false)
|
||||||
|
intent.putExtra(PRIVATE_BROWSING_MODE, privateBrowsingMode)
|
||||||
|
|
||||||
// Check if this is our custom PWA intent with profile metadata
|
// Check if this is our custom PWA intent with profile metadata
|
||||||
val profileUuid = intent.getStringExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID)
|
val profileUuid = intent.getStringExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID)
|
||||||
val contextId = intent.getStringExtra(PwaConstants.EXTRA_PWA_CONTEXT_ID)
|
val contextId = intent.getStringExtra(PwaConstants.EXTRA_PWA_CONTEXT_ID)
|
||||||
@@ -107,7 +111,7 @@ class IntentReceiverActivity : Activity() {
|
|||||||
"CustomTab" to CustomTabIntentProcessor(
|
"CustomTab" to CustomTabIntentProcessor(
|
||||||
components.useCases.customTabsUseCases.add,
|
components.useCases.customTabsUseCases.add,
|
||||||
resources,
|
resources,
|
||||||
isPrivate = false,
|
isPrivate = privateBrowsingMode,
|
||||||
),
|
),
|
||||||
"PWA" to WebAppIntentProcessor(
|
"PWA" to WebAppIntentProcessor(
|
||||||
components.core.store,
|
components.core.store,
|
||||||
|
|||||||
+36
-9
@@ -16,6 +16,7 @@ import android.widget.ImageButton
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
@@ -46,6 +47,7 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
|
|
||||||
private val toolbarCard: MaterialCardView
|
private val toolbarCard: MaterialCardView
|
||||||
private val closeButton: ImageButton
|
private val closeButton: ImageButton
|
||||||
|
private val privateMaskIcon: ImageView
|
||||||
private val securityIcon: ImageView
|
private val securityIcon: ImageView
|
||||||
private val titleText: TextView
|
private val titleText: TextView
|
||||||
private val urlText: TextView
|
private val urlText: TextView
|
||||||
@@ -58,6 +60,7 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
private var urlScope: CoroutineScope? = null
|
private var urlScope: CoroutineScope? = null
|
||||||
private var titleScope: CoroutineScope? = null
|
private var titleScope: CoroutineScope? = null
|
||||||
private var securityScope: CoroutineScope? = null
|
private var securityScope: CoroutineScope? = null
|
||||||
|
private var isPrivateSession: Boolean = false
|
||||||
|
|
||||||
var onCloseListener: (() -> Unit)? = null
|
var onCloseListener: (() -> Unit)? = null
|
||||||
var onShareListener: (() -> Unit)? = null
|
var onShareListener: (() -> Unit)? = null
|
||||||
@@ -69,6 +72,7 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
|
|
||||||
toolbarCard = findViewById(R.id.toolbarCard)
|
toolbarCard = findViewById(R.id.toolbarCard)
|
||||||
closeButton = findViewById(R.id.closeButton)
|
closeButton = findViewById(R.id.closeButton)
|
||||||
|
privateMaskIcon = findViewById(R.id.privateMaskIcon)
|
||||||
securityIcon = findViewById(R.id.securityIcon)
|
securityIcon = findViewById(R.id.securityIcon)
|
||||||
titleText = findViewById(R.id.titleText)
|
titleText = findViewById(R.id.titleText)
|
||||||
urlText = findViewById(R.id.urlText)
|
urlText = findViewById(R.id.urlText)
|
||||||
@@ -89,9 +93,17 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
this.sessionId = sessionId
|
this.sessionId = sessionId
|
||||||
this.store = store
|
this.store = store
|
||||||
|
|
||||||
toolbarColor?.let { applyCustomColors(it) }
|
val tab = store.state.findCustomTab(sessionId)
|
||||||
|
isPrivateSession = tab?.content?.private == true
|
||||||
|
|
||||||
store.state.findCustomTab(sessionId)?.let { tab ->
|
if (toolbarColor != null) {
|
||||||
|
applyCustomColors(toolbarColor)
|
||||||
|
} else {
|
||||||
|
applyMaterial3Colors()
|
||||||
|
applyIcons()
|
||||||
|
}
|
||||||
|
|
||||||
|
tab?.let {
|
||||||
updateTitle(tab)
|
updateTitle(tab)
|
||||||
updateUrl(tab)
|
updateUrl(tab)
|
||||||
updateSecurityIcon(tab)
|
updateSecurityIcon(tab)
|
||||||
@@ -118,14 +130,16 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
private fun applyMaterial3Colors() {
|
private fun applyMaterial3Colors() {
|
||||||
val surfaceColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurface)
|
val surfaceColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurface)
|
||||||
val onSurfaceColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurface)
|
val onSurfaceColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurface)
|
||||||
|
val onSurfaceVariantColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
||||||
toolbarCard.setCardBackgroundColor(surfaceColor)
|
toolbarCard.setCardBackgroundColor(surfaceColor)
|
||||||
titleText.setTextColor(onSurfaceColor)
|
titleText.setTextColor(onSurfaceColor)
|
||||||
urlText.setTextColor(onSurfaceColor)
|
urlText.setTextColor(onSurfaceVariantColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyIcons() {
|
private fun applyIcons() {
|
||||||
val iconColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
val iconColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
||||||
|
|
||||||
|
updatePrivateMaskIcon()
|
||||||
closeButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon.cmd_close, 20, iconColor))
|
closeButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon.cmd_close, 20, iconColor))
|
||||||
shareButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon3.cmd_share_variant, 18, iconColor))
|
shareButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon3.cmd_share_variant, 18, iconColor))
|
||||||
openInBrowserButton.setImageDrawable(
|
openInBrowserButton.setImageDrawable(
|
||||||
@@ -145,6 +159,7 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
titleText.setTextColor(textColor)
|
titleText.setTextColor(textColor)
|
||||||
urlText.setTextColor(textColor)
|
urlText.setTextColor(textColor)
|
||||||
|
|
||||||
|
updatePrivateMaskIcon()
|
||||||
closeButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon.cmd_close, 20, textColor))
|
closeButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon.cmd_close, 20, textColor))
|
||||||
shareButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon3.cmd_share_variant, 18, textColor))
|
shareButton.setImageDrawable(mdiIcon(CommunityMaterial.Icon3.cmd_share_variant, 18, textColor))
|
||||||
openInBrowserButton.setImageDrawable(
|
openInBrowserButton.setImageDrawable(
|
||||||
@@ -242,17 +257,29 @@ class CustomTabToolbar @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSecurityIcon(tab: CustomTabSessionState) {
|
private fun updateSecurityIcon(tab: CustomTabSessionState) {
|
||||||
|
val neutralIconColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
||||||
|
val errorIconColor = MaterialColors.getColor(this, android.R.attr.colorError)
|
||||||
|
|
||||||
val securityInfoKnown = tab.content.securityInfo.host.isNotBlank()
|
val securityInfoKnown = tab.content.securityInfo.host.isNotBlank()
|
||||||
|
|
||||||
if (tab.content.loading || !securityInfoKnown) {
|
if (tab.content.loading || !securityInfoKnown) {
|
||||||
val color = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock_open_outline, 16, neutralIconColor))
|
||||||
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock_open_outline, 16, color))
|
|
||||||
} else if (tab.content.securityInfo.isSecure) {
|
} else if (tab.content.securityInfo.isSecure) {
|
||||||
val color = MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurfaceVariant)
|
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock, 16, neutralIconColor))
|
||||||
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock, 16, color))
|
|
||||||
} else {
|
} else {
|
||||||
val color = MaterialColors.getColor(this, android.R.attr.colorError)
|
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock_open_outline, 16, errorIconColor))
|
||||||
securityIcon.setImageDrawable(mdiIcon(CommunityMaterial.Icon2.cmd_lock_open_outline, 16, color))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePrivateMaskIcon() {
|
||||||
|
privateMaskIcon.visibility = if (isPrivateSession) View.VISIBLE else View.GONE
|
||||||
|
if (isPrivateSession) {
|
||||||
|
val privateMaskColor = ContextCompat.getColor(context, R.color.private_tab_mask_accent)
|
||||||
|
privateMaskIcon.setImageDrawable(
|
||||||
|
mdiIcon(CommunityMaterial.Icon.cmd_domino_mask, 16, privateMaskColor)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
privateMaskIcon.setImageDrawable(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-1
@@ -27,7 +27,6 @@ class CustomTabToolbarFeature(
|
|||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
val tab = store.state.findCustomTab(sessionId) ?: return
|
val tab = store.state.findCustomTab(sessionId) ?: return
|
||||||
|
|
||||||
val toolbarColor = tab.config.colorSchemes?.defaultColorSchemeParams?.toolbarColor
|
val toolbarColor = tab.config.colorSchemes?.defaultColorSchemeParams?.toolbarColor
|
||||||
|
|
||||||
toolbar.bind(sessionId, store, toolbarColor)
|
toolbar.bind(sessionId, store, toolbarColor)
|
||||||
|
|||||||
@@ -45,6 +45,15 @@
|
|||||||
android:paddingHorizontal="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
android:paddingVertical="6dp">
|
android:paddingVertical="6dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privateMaskIcon"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/securityIcon"
|
android:id="@+id/securityIcon"
|
||||||
android:layout_width="18dp"
|
android:layout_width="18dp"
|
||||||
|
|||||||
@@ -4,4 +4,6 @@
|
|||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<color name="icons">#ffffffff</color>
|
<color name="icons">#ffffffff</color>
|
||||||
</resources>
|
<!-- Keep in sync with app/lib/core/design/app_colors.dart privateTabPurple -->
|
||||||
|
<color name="private_tab_mask_accent">#FF8000D7</color>
|
||||||
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user