initial pwa + custom tabs

This commit is contained in:
Fabian Freund
2026-02-10 16:20:54 +01:00
parent 592c6afdc5
commit 188dfdd003
47 changed files with 3974 additions and 130 deletions
@@ -0,0 +1,10 @@
<?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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?attr/colorSurfaceContainer" />
<corners android:radius="16dp" />
</shape>
@@ -0,0 +1,10 @@
<?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/. -->
<!-- Pulsing ripple effect for PWA loading animation -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="?attr/colorPrimary" />
</shape>
@@ -0,0 +1,10 @@
<?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/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" />
@@ -0,0 +1,54 @@
<?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/. -->
<!-- Minimal loading screen for Custom Tabs - keeps focus on content -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/custom_tab_loading_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<!-- Small domain favicon or generic icon -->
<ImageView
android:id="@+id/custom_tab_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="16dp"
android:contentDescription="@string/website_icon_description"
android:alpha="0.6"
android:src="@drawable/ic_launcher_foreground" />
<!-- Domain/URL being loaded -->
<TextView
android:id="@+id/custom_tab_domain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="?android:attr/textColorSecondary"
android:gravity="center"
android:maxLines="1"
android:ellipsize="end"
android:layout_marginBottom="16dp"
android:paddingHorizontal="32dp" />
<!-- Simple linear progress indicator -->
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/custom_tab_progress"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:indeterminate="true"
app:trackThickness="2dp" />
</LinearLayout>
</FrameLayout>
@@ -0,0 +1,157 @@
<?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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/custom_tab_menu_bg"
android:paddingVertical="4dp"
android:minWidth="192dp">
<!-- Navigation row: Back / Forward -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center"
android:paddingHorizontal="12dp">
<ImageButton
android:id="@+id/menuBack"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/custom_tab_navigate_back" />
<ImageButton
android:id="@+id/menuForward"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/custom_tab_navigate_forward" />
</LinearLayout>
<!-- Divider -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="4dp"
android:background="?attr/colorOutlineVariant" />
<!-- Refresh -->
<LinearLayout
android:id="@+id/menuRefresh"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/menuRefreshIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="12dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/custom_tab_refresh"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurface" />
</LinearLayout>
<!-- Share -->
<LinearLayout
android:id="@+id/menuShare"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/menuShareIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="12dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/custom_tab_share"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurface" />
</LinearLayout>
<!-- Desktop site -->
<LinearLayout
android:id="@+id/menuDesktopSite"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/menuDesktopIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="12dp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/custom_tab_desktop_site"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/menuDesktopSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- Open in browser -->
<LinearLayout
android:id="@+id/menuOpenInBrowser"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/menuOpenInBrowserIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="12dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/custom_tab_open_in_browser"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurface" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,94 @@
<?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/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/toolbarCard"
android:layout_width="match_parent"
android:layout_height="56dp"
app:cardCornerRadius="28dp"
app:cardElevation="3dp"
app:strokeWidth="0dp"
app:cardBackgroundColor="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical">
<!-- Close Button -->
<ImageButton
android:id="@+id/closeButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/mozac_feature_customtabs_exit_button" />
<!-- URL Section with Security Indicator -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="8dp">
<ImageView
android:id="@+id/securityIcon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginEnd="6dp"
android:contentDescription="@string/mozac_feature_customtabs_security_indicator" />
<TextView
android:id="@+id/urlText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurface"
android:textSize="14sp" />
</LinearLayout>
<!-- Action Buttons -->
<ImageButton
android:id="@+id/shareButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/custom_tab_share" />
<ImageButton
android:id="@+id/openInBrowserButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/custom_tab_open_in_browser" />
<ImageButton
android:id="@+id/menuButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="center"
android:contentDescription="@string/mozac_feature_customtabs_menu_button" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>
@@ -3,12 +3,22 @@
- 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/. -->
<!-- Main browser fragment layout -->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/customTabAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<!-- Placeholder for custom tab toolbar - ExternalAppBrowserFragment provides its own -->
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -32,4 +42,4 @@
android:visibility="gone" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,88 @@
<?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/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/pwa_loading_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:padding="32dp">
<!-- App Icon Container with subtle shadow/elevation -->
<FrameLayout
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginBottom="24dp">
<!-- Icon background for maskable icons -->
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/pwa_icon_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
app:shapeAppearanceOverlay="@style/RoundedIconShape" />
<!-- App Icon -->
<ImageView
android:id="@+id/pwa_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:contentDescription="@string/pwa_icon_description"
android:src="@drawable/ic_launcher_foreground" />
<!-- Pulsing animation overlay -->
<View
android:id="@+id/pwa_icon_pulse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/pulse_ripple"
android:alpha="0.0" />
</FrameLayout>
<!-- App Name -->
<TextView
android:id="@+id/pwa_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end"
android:layout_marginBottom="8dp" />
<!-- Subtitle/Status -->
<TextView
android:id="@+id/pwa_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="?android:attr/textColorSecondary"
android:gravity="center"
android:text="@string/pwa_loading" />
<!-- Progress indicator at bottom -->
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/pwa_progress"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="32dp"
android:indeterminate="true"
app:indicatorSize="32dp"
app:trackThickness="3dp" />
</LinearLayout>
</FrameLayout>
@@ -16,4 +16,19 @@
<string name="mozac_feature_addons_failed_to_uninstall">Failed to uninstall %1$s</string>
<!-- Error shown when something unexpected happened while trying to get the extension list from the server -->
<string name="mozac_feature_addons_failed_to_query_extensions">Failed to query extensions!</string>
<!-- Custom tab menu items -->
<string name="custom_tab_share">Share</string>
<string name="custom_tab_desktop_site">Desktop site</string>
<string name="custom_tab_open_in_browser">Open in browser</string>
<string name="custom_tab_navigate_back">Back</string>
<string name="custom_tab_navigate_forward">Forward</string>
<string name="custom_tab_refresh">Refresh</string>
<string name="mozac_feature_customtabs_exit_button">Close</string>
<string name="mozac_feature_customtabs_menu_button">Menu</string>
<string name="mozac_feature_customtabs_security_indicator">Security</string>
<!-- Loading screen strings -->
<string name="pwa_loading">Opening…</string>
<string name="pwa_icon_description">App icon</string>
<string name="website_icon_description">Website icon</string>
</resources>
@@ -7,4 +7,10 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>
<!-- Shape style for rounded icon backgrounds (PWA loading) -->
<style name="RoundedIconShape">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">28%</item>
</style>
</resources>