diff --git a/.gitignore b/.gitignore
index 694b40c7efc..85bd93af178 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ gradle/
build/
gradlew*
.DS_Store
+.settings
diff --git a/Android.bp b/Android.bp
index 13a926bc9e8..14b74dee75f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -165,6 +165,8 @@ android_library {
"com_android_wm_shell_flags_lib",
"android.appwidget.flags-aconfig-java",
"com.android.window.flags.window-aconfig-java",
+ "libGoogleFeed",
+ "SettingsLib",
],
manifest: "AndroidManifest-common.xml",
sdk_version: "current",
@@ -178,7 +180,7 @@ android_library {
// Build rule for Launcher3 app.
//
android_app {
- name: "Launcher3",
+ name: "DerpLauncher",
static_libs: [
"Launcher3ResLib",
@@ -198,14 +200,19 @@ android_app {
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
target_sdk_version: "current",
+ certificate: "platform",
privileged: true,
system_ext_specific: true,
overrides: [
"Home",
"Launcher2",
+ "QuickSearchBox",
+ ],
+ required: [
+ "privapp_whitelist_com.android.launcher3",
+ "privapp_whitelist_com.android.launcher3-ext",
],
- required: ["privapp_whitelist_com.android.launcher3"],
jacoco: {
include_filter: ["com.android.launcher3.**"],
@@ -218,6 +225,13 @@ android_app {
},
}
+java_import {
+ name: "libGoogleFeed",
+ jars: [
+ "libs/libGoogleFeed.jar",
+ ],
+}
+
// Library with all the dependencies for building quickstep
android_library {
name: "QuickstepResLib",
@@ -298,7 +312,7 @@ android_library {
// Build rule for Quickstep app.
android_app {
- name: "Launcher3QuickStep",
+ name: "DerpLauncherQuickStep",
static_libs: ["Launcher3QuickStepLib"],
optimize: {
@@ -309,14 +323,20 @@ android_app {
min_sdk_version: "current",
target_sdk_version: "current",
+ certificate: "platform",
privileged: true,
system_ext_specific: true,
overrides: [
"Home",
"Launcher2",
"Launcher3",
+ "Launcher3QuickStep",
+ "QuickSearchBox",
+ ],
+ required: [
+ "privapp_whitelist_com.android.launcher3",
+ "privapp_whitelist_com.android.launcher3-ext",
],
- required: ["privapp_whitelist_com.android.launcher3"],
resource_dirs: ["quickstep/res"],
@@ -351,6 +371,7 @@ android_app {
enabled: true,
},
+ certificate: "platform",
privileged: true,
system_ext_specific: true,
overrides: [
@@ -358,8 +379,13 @@ android_app {
"Launcher2",
"Launcher3",
"Launcher3QuickStep",
+ "Launcher3QuickStepGo",
+ "QuickSearchBox",
+ ],
+ required: [
+ "privapp_whitelist_com.android.launcher3",
+ "privapp_whitelist_com.android.launcher3-ext",
],
- required: ["privapp_whitelist_com.android.launcher3"],
additional_manifests: [
"go/AndroidManifest.xml",
@@ -387,6 +413,7 @@ android_app {
enabled: true,
},
+ certificate: "platform",
privileged: true,
system_ext_specific: true,
overrides: [
@@ -395,7 +422,10 @@ android_app {
"Launcher3",
"Launcher3QuickStep",
],
- required: ["privapp_whitelist_com.android.launcher3"],
+ required: [
+ "privapp_whitelist_com.android.launcher3",
+ "privapp_whitelist_com.android.launcher3-ext",
+ ],
additional_manifests: [
"go/AndroidManifest.xml",
@@ -408,3 +438,11 @@ android_app {
include_filter: ["com.android.launcher3.*"],
},
}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.launcher3-ext",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "privapp_whitelist_com.android.launcher3-ext.xml",
+ filename_from_src: true,
+}
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 80d2eaca23c..f3427e71319 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -20,7 +20,9 @@
+ package="com.android.launcher3"
+ coreApp="true"
+ android:sharedUserId="android.uid.system">
+
+
+
+
+
+
+
+
+ android:exported="true"
+ android:writePermission="${packageName}.permission.WRITE_SETTINGS"
+ android:readPermission="${packageName}.permission.READ_SETTINGS" />
@@ -155,6 +166,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 517bd6d70e0..a68ba6b6d4e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,6 +19,7 @@
-->
-
\ No newline at end of file
+
diff --git a/libs/libGoogleFeed.jar b/libs/libGoogleFeed.jar
new file mode 100644
index 00000000000..158b76de405
Binary files /dev/null and b/libs/libGoogleFeed.jar differ
diff --git a/privapp_whitelist_com.android.launcher3-ext.xml b/privapp_whitelist_com.android.launcher3-ext.xml
new file mode 100644
index 00000000000..6186a7d4fbd
--- /dev/null
+++ b/privapp_whitelist_com.android.launcher3-ext.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/proguard.flags b/proguard.flags
index 31edd8d88c5..d4b79f2c1c1 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -57,3 +57,7 @@
-keep class com.android.quickstep.** {
*;
}
+
+-keep class com.android.launcher3.lineage.trust.** {
+ *;
+}
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index c6e2d8cb74b..e426bae3d72 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -30,7 +30,7 @@
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
- android:icon="@drawable/ic_launcher_home"
+ android:icon="@mipmap/ic_launcher_home"
android:label="@string/derived_app_name"
android:theme="@style/AppTheme"
android:largeHeap="@bool/config_largeHeap"
@@ -65,6 +65,9 @@
+
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bf198b605f8..a3df801c688 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -57,7 +57,7 @@
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
- android:icon="@drawable/ic_launcher_home"
+ android:icon="@mipmap/ic_launcher_home"
android:label="@string/derived_app_name"
android:theme="@style/AppTheme"
android:largeHeap="@bool/config_largeHeap"
diff --git a/res/drawable/ic_launcher_home.xml b/quickstep/res/drawable/ic_clear_all.xml
similarity index 58%
rename from res/drawable/ic_launcher_home.xml
rename to quickstep/res/drawable/ic_clear_all.xml
index 7038775f355..00e8fd42a31 100644
--- a/res/drawable/ic_launcher_home.xml
+++ b/quickstep/res/drawable/ic_clear_all.xml
@@ -1,21 +1,19 @@
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_kill_app.xml b/quickstep/res/drawable/ic_kill_app.xml
new file mode 100644
index 00000000000..bb7036a6ac6
--- /dev/null
+++ b/quickstep/res/drawable/ic_kill_app.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/quickstep/res/drawable/ic_lens.xml b/quickstep/res/drawable/ic_lens.xml
new file mode 100644
index 00000000000..e372e83526c
--- /dev/null
+++ b/quickstep/res/drawable/ic_lens.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/quickstep/res/drawable/task_menu_item_bg.xml b/quickstep/res/drawable/task_menu_item_bg.xml
index 588fe9ec25a..bd863f2bc42 100644
--- a/quickstep/res/drawable/task_menu_item_bg.xml
+++ b/quickstep/res/drawable/task_menu_item_bg.xml
@@ -15,7 +15,13 @@
limitations under the License.
-->
-
-
-
-
+
+ -
+
+
+
+
+
+
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index f0ea09c1afe..4adbb282d3d 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -52,5 +52,8 @@
android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_container" />
+
+
diff --git a/quickstep/res/layout/meminfo.xml b/quickstep/res/layout/meminfo.xml
new file mode 100644
index 00000000000..63c7eb882e6
--- /dev/null
+++ b/quickstep/res/layout/meminfo.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index fcd2e5495ae..7e4d0092a17 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -45,6 +45,37 @@
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
+
+
+
+
+
+
+
+
- 3
+ 6
12
20
@@ -53,7 +53,7 @@
- 23
+ 23dp
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 340d25b0f9a..c87ae590882 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -19,7 +19,7 @@
- Quickstep
+ @string/derp_app_name
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 952505a4843..4d79daad0e4 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -42,7 +42,7 @@
parent="TextAppearance.GestureTutorial">
- start
- ?android:attr/textColorPrimary
- - google-sans
+ - @*android:string/config_headlineFontFamily
- 0.03
- 36sp
- 44sp
@@ -79,7 +79,7 @@
@@ -88,7 +88,7 @@
parent="TextAppearance.GestureTutorial">
- start
- ?android:attr/textColorPrimary
- - google-sans-text
+ - @*android:string/config_bodyFontFamily
- 0.03
- 14sp
- 20sp
@@ -103,7 +103,7 @@
@@ -255,18 +255,18 @@
-
\ No newline at end of file
+
diff --git a/res/values-zh-rCN/derp_config.xml b/res/values-zh-rCN/derp_config.xml
new file mode 100644
index 00000000000..c16313184b3
--- /dev/null
+++ b/res/values-zh-rCN/derp_config.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ true
+
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index be8b2e13d9a..ff8748f2b26 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -21,6 +21,7 @@
+
@@ -79,6 +80,12 @@
+
+
+
+
+
+
diff --git a/res/values/config.xml b/res/values/config.xml
index 2a3b588245b..ae734981835 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,7 +19,7 @@
- @string/app_name
+ @string/derp_app_name
#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end
@@ -82,15 +82,15 @@
-
+ com.android.wallpaper
- 48
- 0
+ 16
+ 24
true
diff --git a/res/values/derp_attrs.xml b/res/values/derp_attrs.xml
new file mode 100644
index 00000000000..79fd7ad7d4e
--- /dev/null
+++ b/res/values/derp_attrs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/derp_colors.xml b/res/values/derp_colors.xml
new file mode 100644
index 00000000000..1d26c086c99
--- /dev/null
+++ b/res/values/derp_colors.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @*android:color/system_accent3_500
+ @*android:color/system_neutral1_800
+ @*android:color/system_neutral1_500
+ @*android:color/system_neutral1_800
+
+
+ #50D9DADB
+
+ @*android:color/system_neutral1_0
+ #d8000000
+ #66000000
+
+ @*android:color/system_accent1_600
+
diff --git a/res/values/derp_config.xml b/res/values/derp_config.xml
new file mode 100644
index 00000000000..7cf88ceb530
--- /dev/null
+++ b/res/values/derp_config.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+ false
+
+
+ com.android.launcher3.settings.SettingsAppDrawer$AppDrawerSettingsFragment
+
+
+ com.android.launcher3.settings.SettingsHomescreen$HomescreenSettingsFragment
+
+
+ com.android.launcher3.settings.SettingsIcons$IconsSettingsFragment
+
+
+ com.android.launcher3.settings.SettingsMisc$MiscSettingsFragment
+
+
+ com.android.launcher3.settings.SettingsRecents$RecentsSettingsFragment
+
+
+ com.android.launcher3.settings.SettingsAbout$AboutFragment
+
diff --git a/res/values/derp_dimens.xml b/res/values/derp_dimens.xml
new file mode 100644
index 00000000000..fa6d2ed9560
--- /dev/null
+++ b/res/values/derp_dimens.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ 40dp
+ 22dp
+ 18dp
+ 16dp
+ 16dp
+ 5.5dp
+ 24dp
+ 42dp
+ 52.5dp
+ 24dp
+ 20dp
+
+ 6dp
+ 18dp
+ 36dp
+
+
+ 15dp
+ 96dp
+ 15dp
+ 12dp
+
diff --git a/res/values/derp_strings.xml b/res/values/derp_strings.xml
new file mode 100644
index 00000000000..07194040a89
--- /dev/null
+++ b/res/values/derp_strings.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+ DerpLauncher
+
+
+ Google
+
+
+ Home settings
+
+
+ Home screen
+ Set Google now panel and more
+ Icons
+ Set icon size and more
+ App drawer
+ Customize your app drawer
+ Recents
+ Revamp the overview screen
+ Miscellaneous
+ Other options
+ General
+ Interface
+ Search bar
+
+
+ Developer options
+ Enable some hidden features at your own risk
+
+
+ Lock layout
+ Icons and widgets can\'t be added, removed and moved on the homescreen
+ Icons and widgets can be added, removed and moved on the homescreen
+ It\'s not possible to add widgets to the home screen
+
+
+ Desktop labels
+ Show icon labels on desktop
+ Drawer labels
+ Show icon labels in drawer
+
+
+ Swipe to access Google app
+ @string/msg_minus_one_on_left
+
+ When you swipe right from main home screen
+
+ When you swipe left from main home screen
+
+
+ Themed icons
+ Follow themed icons used on home screen
+
+
+ Hidden & Protected apps
+ Unlock to manage the hidden and protected apps
+ Authenticate to open %1$s
+ Loading\u2026
+ Please set up a secure lock screen to restrict app access
+ Help
+ Hidden apps and their widgets are hidden from the drawer
+ Protected apps require authentication to be opened from the launcher
+
+
+ Suggestions
+ For All Apps & Home screen suggestions
+
+
+ Restart
+ Restart the launcher manually to apply any pending settings
+ Restarting launcher...
+ Restarting launcher to apply changes...
+ Restarting launcher to update launcher components...
+
+
+ Value: %s
+ Default
+ Default value: %s\nLong press to set
+ Default value is set
+
+
+ Icon size
+
+
+ Font size
+
+
+ Search
+ Google Lens
+ Voice search
+ Google search bar
+ Search bar in the bottom dock
+
+
+ Force close
+ App closed
+
+
+ Hotseat background
+ Add translucent background to the app dock
+
+
+ Wallpaper scrolling
+ Wallpaper scrolling effect for multiple screens
+
+
+ Wallpaper zooming
+ Zoom in or out the wallpaper when using drawer or recent apps
+
+
+ Show status bar
+ Show status bar on Home screen
+
+
+ Show top shadow
+ Add shadow underneath the status bar
+
+
+ Background blur depth
+ Set blur depth level for recents and app drawer
+
+
+ Background opacity
+
+
+ DerpLauncher
+ Based on AOSP Launcher3
+ Version
+ AP3A.240905.015.A2
+ Special thanks to
+ About
+ Info about the DerpLauncher
+
+
+ Double tap to sleep
+ Double tap on empty space for screen off
+
+
+ Quick actions
+ Lens
+ Show lens button
+ Replace screenshot button with Google Lens button, if available
+
+
+ %1$s available | %2$s
+ Memory info
+
+
+ App search bar
+ Search bar on top of the app drawer
+
+
+ Allow short parallax
+ Enable full wallpaper scroll effect on smaller numbers of pages instead of cropping the wallpaper
+ Single page center
+ Center wallpaper if only using a single page
+
+
+ Dark status bar
+ Use dark status bar on Home screen
+
+
+ Scroll Vibration
+ Haptic feedback while scrolling through recent apps, if supported
+
+
+ Force themed icons
+ Generate themed icon for apps that do not have one (requires re-toggling of themed icons)
+
diff --git a/res/values/derp_styles.xml b/res/values/derp_styles.xml
new file mode 100644
index 00000000000..77b475eb0b5
--- /dev/null
+++ b/res/values/derp_styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 05724e2a853..eab505d03e3 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -35,6 +35,14 @@
0dp
+ 16dp
+ 8dp
+ 124dp
+ 6dp
+ 64dp
+ 56dp
+ 10dp
+
8dp
@@ -100,21 +108,29 @@
300dp
- 48dp
+ 52dp
- 24dp
- 30dp
+ 26dp
+
+
+ 42dp
+
+
+ -6dp
40dp
4dp
48dp
12dp
48dp
2dp
- 33dp
+ 48dp
36dp
16dp
- 14dp
+
+
+ 12dp
6dp
4dp
16dp
@@ -133,7 +149,7 @@
4dp
8dp
- 8dp
+ 16dp
2dp
2dp
@@ -410,7 +426,6 @@
@*android:dimen/rounded_corner_content_padding
0dp
- 0dp
0dp
18dp
50dp
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ae3d3b3e111..4152448599d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,6 +35,7 @@
-
@color/material_color_surface_container_highest
- #66FFFFFF
+ - @color/widget_picker_search_text_color_light
- @color/popup_color_primary_light
- @color/popup_color_secondary_light
- @color/popup_color_tertiary_light
@@ -155,6 +156,7 @@
- ?attr/materialColorSurfaceDim
- @color/material_color_surface_container_low
- #80000000
+ - @color/widget_picker_search_text_color_dark
- @color/popup_color_primary_dark
- @color/popup_color_secondary_dark
- @color/popup_color_tertiary_dark
@@ -199,22 +201,54 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -235,7 +269,6 @@
+
+
+
diff --git a/res/xml/default_workspace_2x2.xml b/res/xml/default_workspace_2x2.xml
new file mode 100644
index 00000000000..048ae16857b
--- /dev/null
+++ b/res/xml/default_workspace_2x2.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/default_workspace_3x3.xml b/res/xml/default_workspace_3x3.xml
index 31376e1d5e0..42f67ca8a84 100644
--- a/res/xml/default_workspace_3x3.xml
+++ b/res/xml/default_workspace_3x3.xml
@@ -16,56 +16,48 @@
-
-
-
-
+
-
-
-
-
-
-
+ launcher:y="0"
+ launcher:spanX="3"
+ launcher:spanY="1"
+ launcher:packageName="com.google.android.googlequicksearchbox"
+ launcher:className="com.google.android.apps.gsa.staticplugins.smartspace.widget.SmartspaceWidgetProvider" />
-
+
+
-
-
-
-
-
-
-
-
+ launcher:y="0" >
+
+
+
+
+
-
-
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" >
+
+
diff --git a/res/xml/default_workspace_4x5.xml b/res/xml/default_workspace_4x5.xml
new file mode 100644
index 00000000000..6c92506caa4
--- /dev/null
+++ b/res/xml/default_workspace_4x5.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index b4ac8f63dfa..b5e55a64cf8 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -16,8 +16,19 @@
+
+
+
-
+
+
-
-
+
+
-
+
@@ -68,31 +78,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
new file mode 100644
index 00000000000..abd211a5e61
--- /dev/null
+++ b/res/xml/default_workspace_5x6.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/default_workspace_5x7.xml b/res/xml/default_workspace_5x7.xml
new file mode 100644
index 00000000000..abd211a5e61
--- /dev/null
+++ b/res/xml/default_workspace_5x7.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/default_workspace_6x5.xml b/res/xml/default_workspace_6x5.xml
index b078cfd7f8e..aa63d76a67a 100644
--- a/res/xml/default_workspace_6x5.xml
+++ b/res/xml/default_workspace_6x5.xml
@@ -16,15 +16,26 @@
+
+
+
-
+
-
-
+
+
+
-
+
+
+
+
+
-
-
+
-
-
+
+
-
+
-
+
+
+
-
+
+
-
-
+
-
-
-
-
-
-
-
-
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+
+
-
-
+ launcher:container="-101"
+ launcher:screen="5"
+ launcher:x="5"
+ launcher:y="0" >
+
+
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 1d0dbffc21c..1f162d23062 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -17,10 +17,49 @@
+
+
+
+
+
+
+
+
@@ -42,8 +83,10 @@
launcher:name="Shorter Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="400"
- launcher:iconImageSize="48"
- launcher:iconTextSize="13.0"
+ launcher:iconImageSize="85"
+ launcher:iconTextSize="15.0"
+ launcher:allAppsIconSize="60"
+ launcher:allAppsIconTextSize="14.0"
launcher:allAppsBorderSpace="16"
launcher:allAppsCellHeight="104"
launcher:canBeDefault="true" />
@@ -51,64 +94,26 @@
-
-
-
-
-
-
-
-
@@ -133,7 +138,7 @@
launcher:minWidthDps="406"
launcher:minHeightDps="694"
launcher:iconImageSize="56"
- launcher:iconTextSize="14.4"
+ launcher:iconTextSize="12.0"
launcher:allAppsBorderSpace="16"
launcher:allAppsCellHeight="104"
launcher:canBeDefault="true" />
@@ -143,7 +148,7 @@
launcher:minWidthDps="406"
launcher:minHeightDps="694"
launcher:iconImageSize="56"
- launcher:iconTextSize="14.4"
+ launcher:iconTextSize="12.0"
launcher:allAppsBorderSpace="16"
launcher:allAppsCellHeight="104"
launcher:canBeDefault="true" />
@@ -153,7 +158,53 @@
launcher:minWidthDps="255"
launcher:minHeightDps="400"
launcher:iconImageSize="48"
- launcher:iconTextSize="13.0"
+ launcher:iconTextSize="12.0"
+ launcher:allAppsBorderSpace="16"
+ launcher:allAppsCellHeight="104"
+ launcher:canBeDefault="true" />
+
+
+
+
+
+
+
+
+
+
+
+
@@ -207,4 +258,27 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/launcher_about.xml b/res/xml/launcher_about.xml
new file mode 100644
index 00000000000..544143763f7
--- /dev/null
+++ b/res/xml/launcher_about.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_app_drawer_preferences.xml b/res/xml/launcher_app_drawer_preferences.xml
new file mode 100644
index 00000000000..8ae3928b87c
--- /dev/null
+++ b/res/xml/launcher_app_drawer_preferences.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_home_screen_preferences.xml b/res/xml/launcher_home_screen_preferences.xml
new file mode 100644
index 00000000000..68a2e7c783c
--- /dev/null
+++ b/res/xml/launcher_home_screen_preferences.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_icons_preferences.xml b/res/xml/launcher_icons_preferences.xml
new file mode 100644
index 00000000000..2047eab88e8
--- /dev/null
+++ b/res/xml/launcher_icons_preferences.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_misc_preferences.xml b/res/xml/launcher_misc_preferences.xml
new file mode 100644
index 00000000000..d893ca01472
--- /dev/null
+++ b/res/xml/launcher_misc_preferences.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 284ab9e718e..c5d84da1420 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -15,39 +15,81 @@
-->
-
-
-
-
-
-
-
-
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.launcher3"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:aapt="http://schemas.android.com/aapt">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/launcher_recents_preferences.xml b/res/xml/launcher_recents_preferences.xml
new file mode 100644
index 00000000000..2913d34d29d
--- /dev/null
+++ b/res/xml/launcher_recents_preferences.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index b51e850c04e..90faf0d171b 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -15,7 +15,6 @@
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -248,23 +247,6 @@ private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cel
mMaxHSpan = info.maxSpanX;
mMaxVSpan = info.maxSpanY;
- // Only show resize handles for the directions in which resizing is possible.
- InvariantDeviceProfile idp = LauncherAppState.getIDP(cellLayout.getContext());
- mVerticalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0
- && mMinVSpan < idp.numRows && mMaxVSpan > 1
- && mMinVSpan < mMaxVSpan;
- if (!mVerticalResizeActive) {
- mDragHandles[INDEX_TOP].setVisibility(GONE);
- mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
- }
- mHorizontalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0
- && mMinHSpan < idp.numColumns && mMaxHSpan > 1
- && mMinHSpan < mMaxHSpan;
- if (!mHorizontalResizeActive) {
- mDragHandles[INDEX_LEFT].setVisibility(GONE);
- mDragHandles[INDEX_RIGHT].setVisibility(GONE);
- }
-
mReconfigureButton = (ImageButton) findViewById(R.id.widget_reconfigure_button);
if (info.isReconfigurable()) {
mReconfigureButton.setVisibility(VISIBLE);
@@ -330,12 +312,10 @@ private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cel
}
public boolean beginResizeIfPointInRegion(int x, int y) {
- mLeftBorderActive = (x < mTouchTargetWidth) && mHorizontalResizeActive;
- mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && mHorizontalResizeActive;
- mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment)
- && mVerticalResizeActive;
- mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment)
- && mVerticalResizeActive;
+ mLeftBorderActive = x < mTouchTargetWidth;
+ mRightBorderActive = x > getWidth() - mTouchTargetWidth;
+ mTopBorderActive = y < mTouchTargetWidth + mTopTouchRegionAdjustment;
+ mBottomBorderActive = y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment;
boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
|| mTopBorderActive || mBottomBorderActive;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 7d09164febe..dd9b64dc0c7 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,6 +19,9 @@
import static android.text.Layout.Alignment.ALIGN_NORMAL;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_ALLAPPS_THEMED_ICONS;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_SHOW_DESKTOP_LABELS;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_SHOW_DRAWER_LABELS;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
@@ -31,6 +34,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -208,6 +212,9 @@ public enum RunningAppState {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
+ private boolean mShouldShowLabel;
+ private boolean mThemeAllAppsIcons;
+
private CancellableTask mIconLoadRequest;
private boolean mEnableIconUpdateAnimation = false;
@@ -233,6 +240,8 @@ public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
mDeviceProfile = mActivity.getDeviceProfile();
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
final int defaultIconSize;
if (mDisplay == DISPLAY_WORKSPACE) {
@@ -240,26 +249,32 @@ public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
defaultIconSize = mDeviceProfile.iconSizePx;
setCenterVertically(mDeviceProfile.iconCenterVertically);
- } else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW
- || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW) {
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true);
+ } else if (displayIsAppDrawer()) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.allAppsIconTextSizePx);
setCompoundDrawablePadding(mDeviceProfile.allAppsIconDrawablePaddingPx);
defaultIconSize = mDeviceProfile.allAppsIconSizePx;
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DRAWER_LABELS, true);
+ mThemeAllAppsIcons = prefs.getBoolean(KEY_ALLAPPS_THEMED_ICONS, false);
} else if (mDisplay == DISPLAY_FOLDER) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.folderChildTextSizePx);
setCompoundDrawablePadding(mDeviceProfile.folderChildDrawablePaddingPx);
defaultIconSize = mDeviceProfile.folderChildIconSizePx;
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true);
} else if (mDisplay == DISPLAY_SEARCH_RESULT) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.allAppsIconTextSizePx);
defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_icon_size);
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true);
} else if (mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
defaultIconSize = getResources().getDimensionPixelSize(
R.dimen.search_row_small_icon_size);
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true);
} else if (mDisplay == DISPLAY_TASKBAR) {
defaultIconSize = mDeviceProfile.taskbarIconSize;
} else {
// widget_selection or shortcut_popup
defaultIconSize = mDeviceProfile.iconSizePx;
+ mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true);
}
@@ -293,6 +308,12 @@ public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f);
}
+
+ private boolean displayIsAppDrawer() {
+ return (mDisplay == DISPLAY_ALL_APPS
+ || mDisplay == DISPLAY_PREDICTION_ROW
+ || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW);
+ }
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
@@ -460,7 +481,9 @@ public void applyIconAndLabel(ItemInfoWithIcon info) {
protected boolean shouldUseTheme() {
return (mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
- || mDisplay == DISPLAY_TASKBAR) && Themes.isThemedIconEnabled(getContext());
+ || mDisplay == DISPLAY_TASKBAR
+ || (mThemeAllAppsIcons && displayIsAppDrawer()))
+ && Themes.isThemedIconEnabled(getContext());
}
/**
@@ -478,12 +501,14 @@ protected boolean isCurrentLanguageEnglish() {
@UiThread
public void applyLabel(ItemInfo info) {
- CharSequence label = info.title;
- if (label != null) {
- mLastOriginalText = label;
- mLastModifiedText = mLastOriginalText;
- mBreakPointsIntArray = StringMatcherUtility.getListOfBreakpoints(label, MATCHER);
- setText(label);
+ if (mShouldShowLabel) {
+ CharSequence label = info.title;
+ if (label != null) {
+ mLastOriginalText = label;
+ mLastModifiedText = mLastOriginalText;
+ mBreakPointsIntArray = StringMatcherUtility.getListOfBreakpoints(label, MATCHER);
+ setText(label);
+ }
}
if (info.contentDescription != null) {
setContentDescription(info.isDisabled()
@@ -825,6 +850,10 @@ public void setTextColor(ColorStateList colors) {
}
}
+ public boolean shouldShowLabel() {
+ return mShouldShowLabel;
+ }
+
public boolean shouldTextBeVisible() {
// Text should be visible everywhere but the hotseat.
Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 00db3a38860..83fd343600a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -42,6 +42,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.Surface;
@@ -320,6 +321,11 @@ public class DeviceProfile {
// DragController
public int flingToDeleteThresholdVelocity;
+ // Meminfo in overview
+ public int memInfoMarginGesturePx;
+ public int memInfoMarginTransientTaskbarPx;
+ public int memInfoMarginThreeButtonPx;
+
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
SparseArray dotRendererCache, boolean isMultiWindowMode,
@@ -353,7 +359,10 @@ public class DeviceProfile {
isTablet = info.isTablet(windowBounds);
isPhone = !isTablet;
isTwoPanels = isTablet && isMultiDisplay;
- isTaskbarPresent = (isTablet || (enableTinyTaskbar() && isGestureMode))
+ boolean isTaskBarEnabled = Settings.System.getInt(context.getContentResolver(),
+ Settings.System.ENABLE_TASKBAR, (isTablet || (enableTinyTaskbar()
+ && isGestureMode)) ? 1 : 0) == 1;
+ isTaskbarPresent = isTaskBarEnabled
&& WindowManagerProxy.INSTANCE.get(context).isTaskbarDrawnInProcess();
// Some more constants.
@@ -545,11 +554,13 @@ public class DeviceProfile {
hotseatQsbVisualHeight = hotseatQsbHeight - 2 * hotseatQsbShadowHeight;
// Whether QSB might be inline in appropriate orientation (e.g. landscape).
- boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
+ boolean showQsb = Utilities.showQSB(context);
+ boolean canQsbInline = showQsb && (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
: inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
- && hotseatQsbHeight > 0;
- isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
+ && hotseatQsbHeight > 0 && showQsb;
+ isQsbInline = (mIsScalableGrid || (isTaskbarPresent && !isLandscape))
+ && inv.inlineQsb[mTypeIndex] && canQsbInline;
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
numShownHotseatIcons =
@@ -586,8 +597,10 @@ public class DeviceProfile {
mResponsiveWorkspaceCellSpec = workspaceCellSpecs.getCalculatedSpec(
responsiveAspectRatio, heightPx);
} else {
- hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
- hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+ hotseatQsbSpace = showQsb
+ ? pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics) : 0;
+ hotseatBarBottomSpace = showQsb || isTaskbarPresent
+ ? pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics) : 0;
mHotseatBarEdgePaddingPx =
isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
mHotseatBarWorkspaceSpacePx =
@@ -596,7 +609,7 @@ public class DeviceProfile {
if (!isVerticalBarLayout()) {
// Have a little space between the inset and the QSB
- if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
+ if (showQsb && mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
// Only change the spaces if there is space
@@ -738,6 +751,13 @@ public class DeviceProfile {
isLeftRightSplit = isLandscape;
}
+ memInfoMarginGesturePx = res.getDimensionPixelSize(
+ R.dimen.meminfo_bottom_margin_gesture);
+ memInfoMarginTransientTaskbarPx = res.getDimensionPixelSize(
+ R.dimen.meminfo_bottom_margin_transient_taskbar);
+ memInfoMarginThreeButtonPx = res.getDimensionPixelSize(
+ R.dimen.meminfo_bottom_margin_three_button);
+
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(context);
@@ -822,7 +842,7 @@ private int calculateQsbWidth(int hotseatBorderSpace) {
- hotseatBorderSpace * numShownHotseatIcons
- iconExtraSpacePx;
} else {
- return getIconToIconWidthForColumns(mHotseatColumnSpan) - iconExtraSpacePx;
+ return getIconToIconWidthForColumns(mHotseatColumnSpan) - iconExtraSpacePx - hotseatBorderSpace;
}
}
@@ -891,7 +911,7 @@ private void updateHotseatSizes(int hotseatIconSizePx) {
* necessary.
*/
public void recalculateHotseatWidthAndBorderSpace() {
- if (!mIsScalableGrid) return;
+ if (!(mIsScalableGrid || (isTaskbarPresent && !isLandscape))) return;
updateHotseatWidthAndBorderSpace(inv.numColumns);
int numWorkspaceColumns = getPanelCount() * inv.numColumns;
@@ -1821,7 +1841,8 @@ public Rect getHotseatLayoutPadding(Context context) {
startSpacing = inlineNavButtonsEndSpacingPx;
endSpacing = availableWidthPx - hotseatWidth - startSpacing + hotseatBorderSpace;
} else {
- startSpacing = (availableWidthPx - hotseatWidth) / 2;
+ startSpacing = isTablet ? (availableWidthPx - hotseatWidth) / 2 :
+ (availableWidthPx - hotseatQsbWidth) / 2;
endSpacing = startSpacing;
}
startSpacing += getAdditionalQsbSpace();
@@ -1851,7 +1872,7 @@ public Rect getHotseatLayoutPadding(Context context) {
// workspace cell vs a hotseat cell.
float workspaceCellWidth = (float) widthPx / inv.numColumns;
float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
- int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
+ int hotseatAdjustment = hotseatBorderSpace / 2;
hotseatBarPadding.set(
hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
+ mInsets.left,
@@ -1897,7 +1918,7 @@ private int getIconVisibleSizePx(int iconSizePx) {
}
private int getAdditionalQsbSpace() {
- return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
+ return isTablet && isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
}
/**
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 854645418a5..8ce4912a0ef 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -64,7 +64,11 @@ public Hotseat(Context context, AttributeSet attrs) {
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+ if (Utilities.showQSB(context)) {
+ mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+ } else {
+ mQsb = LayoutInflater.from(context).inflate(R.layout.empty_view, this, false);
+ }
addView(mQsb);
}
@@ -248,7 +252,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
DeviceProfile dp = mActivity.getDeviceProfile();
- mQsb.measure(MeasureSpec.makeMeasureSpec(dp.hotseatQsbWidth, MeasureSpec.EXACTLY),
+
+ int qsbWidth = dp.isQsbInline
+ ? dp.hotseatQsbWidth
+ : getShortcutsAndWidgets().getMeasuredWidth();
+
+ mQsb.measure(MeasureSpec.makeMeasureSpec(qsbWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(dp.hotseatQsbHeight, MeasureSpec.EXACTLY));
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 54aea38ef3d..96281e2d84b 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,6 +28,8 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -77,7 +79,7 @@
import java.util.Objects;
import java.util.stream.Collectors;
-public class InvariantDeviceProfile implements SafeCloseable {
+public class InvariantDeviceProfile implements SafeCloseable, OnSharedPreferenceChangeListener {
public static final String TAG = "IDP";
// We do not need any synchronization for this variable as its only written on UI thread.
@@ -94,6 +96,13 @@ public class InvariantDeviceProfile implements SafeCloseable {
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
+ public static final String KEY_ALLAPPS_THEMED_ICONS = "pref_allapps_themed_icons";
+ public static final String KEY_SHOW_DESKTOP_LABELS = "pref_desktop_show_labels";
+ public static final String KEY_SHOW_DRAWER_LABELS = "pref_drawer_show_labels";
+ public static final String KEY_WORKSPACE_LOCK = "pref_workspace_lock";
+ public static final String KEY_ICON_SIZE = "pref_custom_icon_size";
+ public static final String KEY_FONT_SIZE = "pref_custom_font_size";
+
// Constants that affects the interpolation curve between statically defined device profile
// buckets.
private static final float KNEARESTNEIGHBOR = 3;
@@ -219,6 +228,8 @@ public class InvariantDeviceProfile implements SafeCloseable {
public Point defaultWallpaperSize;
+ private Context mContext;
+
private final ArrayList mChangeListeners = new ArrayList<>();
@VisibleForTesting
@@ -226,6 +237,10 @@ public InvariantDeviceProfile() { }
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
+ mContext = context;
+
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context);
+ prefs.registerOnSharedPreferenceChangeListener(this);
String gridName = getCurrentGridName(context);
String newGridName = initGrid(context, gridName);
if (!newGridName.equals(gridName)) {
@@ -331,6 +346,19 @@ public void reinitializeAfterRestore(Context context) {
}
}
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ switch (key) {
+ case KEY_ALLAPPS_THEMED_ICONS:
+ case KEY_SHOW_DESKTOP_LABELS:
+ case KEY_SHOW_DRAWER_LABELS:
+ case KEY_ICON_SIZE:
+ case KEY_FONT_SIZE:
+ onConfigChanged(mContext);
+ break;
+ }
+ }
+
public static String getCurrentGridName(Context context) {
return LauncherPrefs.get(context).get(GRID_NAME);
}
@@ -1084,6 +1112,11 @@ static final class DisplayOption {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProfileDisplayOption);
+ float iconSizeModifier =
+ (float) LauncherPrefs.getPrefs(context).getInt(KEY_ICON_SIZE, 100) / 100F;
+ float fontSizeModifier =
+ (float) LauncherPrefs.getPrefs(context).getInt(KEY_FONT_SIZE, 100) / 100F;
+
minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
@@ -1213,7 +1246,7 @@ static final class DisplayOption {
allAppsBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
iconSizes[INDEX_DEFAULT] =
- a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
+ a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0) * iconSizeModifier;
iconSizes[INDEX_LANDSCAPE] =
a.getFloat(R.styleable.ProfileDisplayOption_iconSizeLandscape,
iconSizes[INDEX_DEFAULT]);
@@ -1237,7 +1270,7 @@ static final class DisplayOption {
allAppsIconSizes[INDEX_DEFAULT]);
textSizes[INDEX_DEFAULT] =
- a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
+ a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0) * fontSizeModifier;
textSizes[INDEX_LANDSCAPE] =
a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeLandscape,
textSizes[INDEX_DEFAULT]);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4e566abddc7..b8c0e2ff05b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -94,6 +94,7 @@
import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.UNINSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
@@ -152,6 +153,7 @@
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
@@ -290,7 +292,7 @@
*/
public class Launcher extends StatefulActivity
implements Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
- PluginListener {
+ PluginListener, SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "Launcher";
public static final ActivityTracker ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -316,6 +318,8 @@ public class Launcher extends StatefulActivity
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
+ private static final String KEY_DARK_STATUS_BAR = "pref_dark_status_bar";
+
// How long to wait before the new-shortcut animation automatically pans the workspace
@VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@@ -418,6 +422,8 @@ public class Launcher extends StatefulActivity
private final SettingsCache.OnChangeListener mNaturalScrollingChangedListener =
enabled -> mIsNaturalScrollingEnabled = enabled;
+ private InputMethodManager mInputMethodManager;
+
public static Launcher getLauncher(Context context) {
return fromContext(context);
}
@@ -577,7 +583,10 @@ protected void onCreate(Bundle savedInstanceState) {
// Listen for screen turning off
ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
- Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
+ || mSharedPrefs.getBoolean(KEY_DARK_STATUS_BAR, false));
+
+ mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
mOverlayManager = getDefaultOverlay();
PluginManagerWrapper.INSTANCE.get(this)
@@ -595,6 +604,9 @@ protected void onCreate(Bundle savedInstanceState) {
RuleController.getInstance(this).setRules(
RuleController.parseRules(this, R.xml.split_configuration));
}
+
+ mInputMethodManager = (InputMethodManager) mWorkspace.getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
}
protected ModelCallbacks createModelCallbacks() {
@@ -694,6 +706,13 @@ protected LauncherOverlayManager getDefaultOverlay() {
return new LauncherOverlayManager() { };
}
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences SharedPrefs, String key) {
+ if (key.equals(KEY_DARK_STATUS_BAR)) {
+ recreate();
+ }
+ }
+
@Override
public void onPluginConnected(LauncherOverlayPlugin overlayManager, Context context) {
switchOverlay(() -> overlayManager.createOverlayManager(this));
@@ -1302,6 +1321,8 @@ protected void onResume() {
DragView.removeAllViews(this);
TraceHelper.INSTANCE.endSection();
+
+ LauncherAppState.INSTANCE.executeIfCreated(app -> app.checkIfRestartNeeded());
}
@Override
@@ -1373,6 +1394,8 @@ protected void setupViews() {
mHotseat = findViewById(R.id.hotseat);
mHotseat.setWorkspace(mWorkspace);
+ mHotseat.setBackgroundResource(Utilities.isHotseatBgEnabled(this) ? R.drawable.bkg_appseat : 0);
+
// Setup the drag layer
mDragLayer.setup(mDragController, mWorkspace);
@@ -2166,6 +2189,12 @@ public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
return result;
}
+ public void startActivitySafelyAuth(View v, Intent intent, ItemInfo item) {
+ Utilities.showLockScreen(this, getString(R.string.trust_apps_manager_name), () -> {
+ startActivitySafely(v, intent, item);
+ });
+ }
+
boolean isHotseatLayout(View layout) {
// TODO: Remove this method
return mHotseat != null && (layout == mHotseat);
@@ -2871,7 +2900,11 @@ public boolean supportsAdaptiveIconAnimation(View clickedView) {
* @param progress Transition progress from 0 to 1; where 0 => home and 1 => all apps.
*/
public void onAllAppsTransition(float progress) {
- // No-Op
+ if (progress > 0) return;
+ if (mAppsView == null) return;
+ if (mInputMethodManager == null) return;
+ // make sure the keyboard is hidden when the AllApps view is closed
+ mInputMethodManager.hideSoftInputFromWindow(mAppsView.getWindowToken(), 0);
}
/**
@@ -3101,7 +3134,7 @@ protected RectF getPopupTarget(float x, float y) {
}
public Stream getSupportedShortcuts() {
- return Stream.of(APP_INFO, WIDGETS, INSTALL);
+ return Stream.of(APP_INFO, WIDGETS, INSTALL, UNINSTALL);
}
/**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 3b8ff62d8af..bb75b444fcc 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -37,6 +37,7 @@
import android.content.pm.LauncherApps.ArchiveCompatibilityParams;
import android.os.UserHandle;
import android.util.Log;
+import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.os.BuildCompat;
@@ -46,6 +47,8 @@
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIconProvider;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.lineage.trust.HiddenAppsFilter;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
import com.android.launcher3.model.ModelLauncherCallbacks;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
@@ -80,6 +83,8 @@ public class LauncherAppState implements SafeCloseable {
private final RunnableList mOnTerminateCallback = new RunnableList();
+ private boolean mNeedsRestart;
+
public static LauncherAppState getInstance(Context context) {
return INSTANCE.get(context);
}
@@ -182,7 +187,7 @@ public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
mIconProvider = new LauncherIconProvider(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
- mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
+ mModel = new LauncherModel(context, this, mIconCache, new HiddenAppsFilter(mContext),
PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
mOnTerminateCallback.add(mModel::destroy);
@@ -207,6 +212,19 @@ private void onPrivateSpaceHideWhenLockChanged(boolean isPrivateSpaceHideOnLockE
mModel.forceReload();
}
+ public void setNeedsRestart() {
+ mNeedsRestart = true;
+ }
+
+ public void checkIfRestartNeeded() {
+ // we destroyed Settings activity with the back button
+ // so we force a restart now if needed without waiting for home button press
+ if (mNeedsRestart) {
+ Toast.makeText(mContext, R.string.restarting_launcher_changes, Toast.LENGTH_SHORT).show();
+ Utilities.restart(mContext);
+ }
+ }
+
private void refreshAndReloadLauncher() {
LauncherIcons.clearPool(mContext);
mIconCache.updateIconParams(
@@ -242,6 +260,10 @@ public boolean isSafeModeEnabled() {
return mIsSafeModeEnabled;
}
+ public TrustDatabaseHelper getTrustData() {
+ return TrustDatabaseHelper.getInstance(this.mContext);
+ }
+
/**
* Shorthand for {@link #getInvariantDeviceProfile()}
*/
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index d730cea8041..13552307608 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -16,7 +16,10 @@ public class LauncherFiles {
private static final String XML = ".xml";
public static final String LAUNCHER_DB = "launcher.db";
+ public static final String LAUNCHER_6_BY_6_DB = "launcher_6_by_6.db";
public static final String LAUNCHER_6_BY_5_DB = "launcher_6_by_5.db";
+ public static final String LAUNCHER_5_BY_7_DB = "launcher_5_by_7.db";
+ public static final String LAUNCHER_5_BY_6_DB = "launcher_5_by_6.db";
public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db";
public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
@@ -33,7 +36,10 @@ public class LauncherFiles {
public static final List GRID_DB_FILES = Collections.unmodifiableList(Arrays.asList(
LAUNCHER_DB,
+ LAUNCHER_6_BY_6_DB,
LAUNCHER_6_BY_5_DB,
+ LAUNCHER_5_BY_7_DB,
+ LAUNCHER_5_BY_6_DB,
LAUNCHER_4_BY_5_DB,
LAUNCHER_4_BY_4_DB,
LAUNCHER_3_BY_3_DB,
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ca1b2a91f75..6c6a09ed40f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -159,7 +159,7 @@ public void run() {
mApp = app;
mPmHelper = pmHelper;
mModelDbController = new ModelDbController(context);
- mBgAllAppsList = new AllAppsList(iconCache, appFilter);
+ mBgAllAppsList = new AllAppsList(iconCache, appFilter, app.getTrustData());
mModelDelegate = ModelDelegate.newInstance(context, app, mPmHelper, mBgAllAppsList,
mBgDataModel, isPrimaryInstance);
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 102189b530f..b32c3294978 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -70,6 +70,7 @@ public abstract class LauncherState implements BaseState {
public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
public static final int FLOATING_SEARCH_BAR = 1 << 7;
+ public static final int MEMINFO = 1 << 8;
// Flag indicating workspace has multiple pages visible.
public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 19a3002665f..4f6ff9c04b3 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,11 +28,14 @@
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.KeyguardManager;
import android.app.Person;
import android.app.WallpaperManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -53,12 +56,17 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
+import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.os.Build.VERSION_CODES;
+import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.TransactionTooLargeException;
+import android.provider.Settings;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -72,6 +80,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import android.widget.Toast;
import androidx.annotation.ChecksSdkIntAtLeast;
import androidx.annotation.IntDef;
@@ -80,6 +89,8 @@
import androidx.annotation.WorkerThread;
import androidx.core.graphics.ColorUtils;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.R;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.BitmapInfo;
@@ -109,6 +120,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
/**
* Various utilities shared amongst the Launcher's classes.
*/
@@ -138,6 +151,8 @@ public final class Utilities {
public static final boolean ATLEAST_V = Build.VERSION.SDK_INT
>= VERSION_CODES.VANILLA_ICE_CREAM;
+ private static final long WAIT_BEFORE_RESTART = 250;
+
/**
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
*/
@@ -162,6 +177,24 @@ public final class Utilities {
@IntDef({TRANSLATE_UP, TRANSLATE_DOWN, TRANSLATE_LEFT, TRANSLATE_RIGHT})
public @interface AdjustmentDirection{}
+ public static final String GSA_PACKAGE = "com.google.android.googlequicksearchbox";
+ public static final String LENS_ACTIVITY = "com.google.android.apps.lens.MainActivity";
+ public static final String LENS_URI = "google://lens";
+ public static final String LENS_SHARE_ACTIVITY = "com.google.android.apps.search.lens.LensShareEntryPointActivity";
+
+ public static final String KEY_DOCK_SEARCH = "pref_dock_search";
+ public static final String KEY_SHOW_HOTSEAT_BG = "pref_show_hotseat_bg";
+ public static final String KEY_ALLOW_WALLPAPER_ZOOMING = "pref_allow_wallpaper_zooming";
+ public static final String KEY_STATUS_BAR = "pref_show_statusbar";
+ public static final String KEY_BLUR_DEPTH = "pref_blur_depth";
+ public static final String KEY_RECENTS_OPACITY = "pref_recents_opacity";
+ public static final String KEY_APP_DRAWER_OPACITY = "pref_app_drawer_opacity";
+ public static final String KEY_RECENTS_MEMINFO = "pref_recents_meminfo";
+ public static final String KEY_DRAWER_SEARCH = "pref_drawer_search";
+ public static final String KEY_SHORT_PARALLAX = "pref_short_parallax";
+ public static final String KEY_SINGLE_PAGE_CENTER = "pref_single_page_center";
+ public static final String KEY_FORCE_MONOCHROME_ICONS = "pref_forced_monochrome_icons";
+
/**
* Returns true if theme is dark.
*/
@@ -595,7 +628,9 @@ public static ActivityOptions allowBGLaunch(ActivityOptions options) {
Drawable badge = null;
if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.usingLowResIcon()) {
- badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ badge = iiwi.bitmap.withUser(iiwi.user, li).getBadgeDrawable(context, useTheme);
+ }
}
if (info instanceof PendingAddShortcutInfo) {
@@ -678,11 +713,13 @@ public static ActivityOptions allowBGLaunch(ActivityOptions options) {
}
if (badge == null) {
- badge = BitmapInfo.LOW_RES_INFO.withFlags(
- UserCache.INSTANCE.get(context)
- .getUserInfo(info.user)
- .applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, useTheme);
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ badge = BitmapInfo.LOW_RES_INFO.withUser(info.user, li).withFlags(
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP))
+ .getBadgeDrawable(context, useTheme);
+ }
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
@@ -864,4 +901,146 @@ public static T findViewByPredicate(@NonNull View root,
}
return null;
}
+
+ public static void restart(final Context context) {
+ MODEL_EXECUTOR.execute(() -> {
+ try {
+ Thread.sleep(WAIT_BEFORE_RESTART);
+ } catch (Exception ignored) {
+ }
+ android.os.Process.killProcess(android.os.Process.myPid());
+ });
+ }
+
+ public static boolean isWorkspaceEditAllowed(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return !prefs.getBoolean(InvariantDeviceProfile.KEY_WORKSPACE_LOCK, false);
+ }
+
+ public static boolean isGSAEnabled(Context context) {
+ try {
+ return context.getPackageManager().getApplicationInfo(GSA_PACKAGE, 0).enabled;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ public static boolean isLongPressToSearchEnabled(Context context) {
+ return Settings.System.getInt(context.getContentResolver(),
+ Settings.System.NAVBAR_LONG_PRESS_GESTURE, 1) == 1;
+ }
+
+ /**
+ * Shows authentication screen to confirm credentials (pin, pattern or password) for the current
+ * user of the device.
+ *
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param title the {@code String} which will be shown as the pompt title
+ * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreen(Context context, String title, Runnable successRunnable) {
+ if (hasSecureKeyguard(context)) {
+ final BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ successRunnable.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ //Do nothing
+ }
+ };
+
+ final BiometricPrompt bp = new BiometricPrompt.Builder(context)
+ .setTitle(title)
+ .setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG |
+ Authenticators.DEVICE_CREDENTIAL)
+ .build();
+
+ final Handler handler = new Handler(Looper.getMainLooper());
+ bp.authenticate(new CancellationSignal(),
+ runnable -> handler.post(runnable),
+ authenticationCallback);
+ } else {
+ // Notify the user a secure keyguard is required for protected apps,
+ // but allow to set hidden apps
+ Toast.makeText(context, R.string.trust_apps_no_lock_error, Toast.LENGTH_LONG)
+ .show();
+ successRunnable.run();
+ }
+ }
+
+ public static boolean hasSecureKeyguard(Context context) {
+ final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ return keyguardManager != null && keyguardManager.isKeyguardSecure();
+ }
+
+ public static boolean showQSB(Context context) {
+ return isGSAEnabled(context) && isQSBEnabled(context);
+ }
+
+ private static boolean isQSBEnabled(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_DOCK_SEARCH, true);
+ }
+
+ public static boolean isHotseatBgEnabled(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_SHOW_HOTSEAT_BG, false);
+ }
+
+ public static boolean canZoomWallpaper(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_ALLOW_WALLPAPER_ZOOMING, true);
+ }
+
+ public static boolean showStatusbarEnabled(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_STATUS_BAR, true);
+ }
+
+ public static int getBlurRadius(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getInt(KEY_BLUR_DEPTH,
+ (int) context.getResources().getDimension(R.dimen.max_depth_blur_radius));
+ }
+
+ public static int getRecentsOpacity(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getInt(KEY_RECENTS_OPACITY, 40);
+ }
+
+ public static int getAllAppsOpacity(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getInt(KEY_APP_DRAWER_OPACITY, 80);
+ }
+
+ public static boolean isShowMeminfo(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_RECENTS_MEMINFO, false);
+ }
+
+ public static boolean showSearch(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_DRAWER_SEARCH, true);
+ }
+
+ public static boolean isShortParallax(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_SHORT_PARALLAX, false);
+ }
+
+ public static boolean isSinglePageCentered(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_SINGLE_PAGE_CENTER, false);
+ }
+
+ public static boolean enableMonoChromeThemedIcons(Context context) {
+ SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext());
+ return prefs.getBoolean(KEY_FORCE_MONOCHROME_ICONS, false);
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e601a3e2985..7959e5304dc 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1364,7 +1364,9 @@ protected void setWallpaperDimension() {
@Override
public void run() {
final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize;
- if (size.x != mWallpaperManager.getDesiredMinimumWidth()
+ if (!mWallpaperManager.isWallpaperSupported()) {
+ mWallpaperManager.suggestDesiredDimensions(0, 0);
+ } else if (size.x != mWallpaperManager.getDesiredMinimumWidth()
|| size.y != mWallpaperManager.getDesiredMinimumHeight()) {
mWallpaperManager.suggestDesiredDimensions(size.x, size.y);
}
@@ -2224,7 +2226,7 @@ public void onDrop(final DragObject d, DragOptions options) {
private Runnable getWidgetResizeFrameRunnable(DragOptions options,
LauncherAppWidgetHostView hostView, CellLayout cellLayout) {
AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
- if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
+ if (pInfo != null) {
return () -> {
if (!isPageInTransition()) {
AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 56a7fef5261..db79a2b7996 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -67,6 +67,8 @@
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.derp.app.ParallelSpaceManager;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
@@ -129,15 +131,15 @@ public void setValue(ActivityAllAppsContainerView> containerView, float v) {
public static final float PULL_MULTIPLIER = .02f;
public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
- private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
+ // As of this writing, search transition does not seem to work properly, so set duration to 0.
+ private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 0;
// Render the header protection at all times to debug clipping issues.
private static final boolean DEBUG_HEADER_PROTECTION = false;
/** Context of an activity or window that is inflating this container. */
protected final T mActivityContext;
protected final List mAH;
- protected final Predicate mPersonalMatcher = ItemInfoMatcher.ofUser(
- Process.myUserHandle());
+ protected Predicate mPersonalMatcher;
protected WorkProfileManager mWorkManager;
protected final PrivateProfileManager mPrivateProfileManager;
protected final Point mFastScrollerOffset = new Point();
@@ -207,6 +209,7 @@ public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int def
mAllAppsStore = new AllAppsStore<>(mActivityContext);
mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ mBottomSheetBackgroundColor = mScrimColor;
mHeaderThreshold = getResources().getDimensionPixelSize(
R.dimen.dynamic_grid_cell_border_spacing);
mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
@@ -221,6 +224,7 @@ public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int def
this,
mActivityContext.getStatsLogManager(),
UserCache.INSTANCE.get(mActivityContext));
+ updateMatcher();
mPrivateSpaceBottomExtraSpace = context.getResources().getDimensionPixelSize(
R.dimen.ps_extra_bottom_padding);
mAH = Arrays.asList(null, null, null);
@@ -293,6 +297,11 @@ protected void initContent() {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ if (Utilities.showSearch(getContext())) {
+ mSearchContainer.setVisibility(View.VISIBLE);
+ } else {
+ mSearchContainer.setVisibility(View.GONE);
+ }
mAH.get(SEARCH).setup(mSearchRecyclerView,
/* Filter out A-Z apps */ itemInfo -> false);
@@ -308,8 +317,6 @@ protected void onFinishInflate() {
0,
0 // Bottom left
};
- mBottomSheetBackgroundColor =
- Themes.getAttrColor(getContext(), R.attr.materialColorSurfaceDim);
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
mSearchUiManager.initializeSearch(this);
}
@@ -407,6 +414,7 @@ void animateToSearchState(boolean goingToSearch, long durationMs) {
// If exiting search, revert predictive back scale on all apps
mAllAppsTransitionController.animateAllAppsToNoScale();
}
+ setScrollbarVisibility(!goingToSearch);
mSearchTransitionController.animateToState(goingToSearch, durationMs,
/* onEndRunnable = */ () -> {
mIsSearching = goingToSearch;
@@ -557,9 +565,6 @@ public void onActivePageChanged(int currentActivePage) {
// Will be called at the end of the animation.
return;
}
- if (currentActivePage != SEARCH) {
- mActivityContext.hideKeyboard();
- }
if (mAH.get(currentActivePage).mRecyclerView != null) {
mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller);
}
@@ -746,13 +751,22 @@ void setupHeader() {
tabsHidden);
int padding = mHeader.getMaxTranslation();
- mAH.forEach(adapterHolder -> {
- adapterHolder.mPadding.top = padding;
+ for (int i = 0; i < mAH.size(); i++) {
+ final AdapterHolder adapterHolder = mAH.get(i);
+ // Search and other adapters need to be handled a bit differently; otherwise, when
+ // when leaving search, the All Apps view may be noticeably shifted downward because
+ // its padding was unnecessarily impacted, and never restored, upon entering search.
+ if (i != AdapterHolder.SEARCH && !tabsHidden && mHeader.getFloatingRowsHeight() == 0) {
+ // Only the Search adapter needs padding when there are tabs but no floating rows.
+ adapterHolder.mPadding.top = 0;
+ } else {
+ adapterHolder.mPadding.top = padding;
+ }
adapterHolder.applyPadding();
if (adapterHolder.mRecyclerView != null) {
adapterHolder.mRecyclerView.scrollToTop();
}
- });
+ }
removeCustomRules(mHeader);
if (!isSearchSupported()) {
@@ -776,6 +790,7 @@ protected void updateHeaderScroll(int scrolledOffset) {
mTabsProtectionAlpha = tabsAlpha;
invalidateHeader();
}
+ getSearchView().setBackgroundResource(R.drawable.bg_all_apps_searchbox);
if (mSearchUiManager.getEditText() == null) {
return;
}
@@ -861,13 +876,15 @@ private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
}
RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
- layoutParams.addRule(RelativeLayout.ALIGN_TOP, R.id.search_container_all_apps);
+ layoutParams.addRule(RelativeLayout.BELOW, R.id.search_container_all_apps);
int topMargin = getContext().getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_top_margin);
+ R.dimen.all_apps_search_bar_bottom_adjustment);
if (includeTabsMargin) {
topMargin += getContext().getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_pill_height);
+ R.dimen.all_apps_header_pill_height)
+ + getContext().getResources().getDimensionPixelSize(
+ R.dimen.all_apps_tabs_margin_top);
}
layoutParams.topMargin = topMargin;
}
@@ -895,6 +912,7 @@ private void removeCustomRules(View v) {
layoutParams.removeRule(RelativeLayout.ABOVE);
layoutParams.removeRule(RelativeLayout.ALIGN_TOP);
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
+ layoutParams.removeRule(RelativeLayout.BELOW);
}
protected BaseAllAppsAdapter createAdapter(AlphabeticalAppsList appsList) {
@@ -1015,8 +1033,15 @@ private void setBottomSheetAlpha(float alpha) {
mBottomSheetAlpha = mActivityContext.getDeviceProfile().isTablet ? 1f : alpha;
}
+ private void updateMatcher() {
+ mPersonalMatcher = ItemInfoMatcher.ofUser(
+ Process.myUserHandle()).or(ItemInfoMatcher.ofUsers(
+ ParallelSpaceManager.getInstance().getParallelUserHandles()));
+ }
+
@VisibleForTesting
public void onAppsUpdated() {
+ updateMatcher();
mHasWorkApps = Stream.of(mAllAppsStore.getApps())
.anyMatch(mWorkManager.getItemInfoMatcher());
mHasPrivateApps = Stream.of(mAllAppsStore.getApps())
@@ -1203,6 +1228,13 @@ protected void dispatchDraw(Canvas canvas) {
}
}
+ protected void setScrollbarVisibility(boolean visible) {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null) {
+ rv.getScrollbar().setVisibility(visible ? VISIBLE : GONE);
+ }
+ }
+
protected void updateSearchResultsVisibility() {
if (isSearching()) {
getSearchRecyclerView().setVisibility(VISIBLE);
@@ -1380,6 +1412,7 @@ public void setScrimView(ScrimView scrimView) {
@Override
public void drawOnScrimWithScaleAndBottomOffset(
Canvas canvas, float scale, @Px int bottomOffsetPx) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
final View panel = mBottomSheetBackground;
final boolean hasBottomSheet = panel.getVisibility() == VISIBLE;
final float translationY = ((View) panel.getParent()).getTranslationY();
@@ -1389,8 +1422,8 @@ public void drawOnScrimWithScaleAndBottomOffset(
final float topNoScale = panel.getTop() + translationY;
final float topWithScale = topNoScale + verticalScaleOffset;
- final float leftWithScale = panel.getLeft() + horizontalScaleOffset;
- final float rightWithScale = panel.getRight() - horizontalScaleOffset;
+ final float leftWithScale = mlp.leftMargin + panel.getLeft() + horizontalScaleOffset;
+ final float rightWithScale = mlp.leftMargin + panel.getRight() - mlp.rightMargin - horizontalScaleOffset;
final float bottomWithOffset = panel.getBottom() + bottomOffsetPx;
// Draw full background panel for tablets.
if (hasBottomSheet) {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 5d03a932547..cc99aaa23fc 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -40,7 +40,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -102,6 +101,7 @@ public FastScrollSectionInfo(CharSequence sectionName, int position) {
private int mNumAppsPerRowAllApps;
private int mNumAppRowsInAdapter;
private Predicate mItemFilter;
+ private final boolean mSortSections;
public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore,
WorkProfileManager workProfileManager, PrivateProfileManager privateProfileManager) {
@@ -118,6 +118,7 @@ public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore
mPrivateProfileAppScrollerBadge.setSpan(new ImageSpan(context,
R.drawable.ic_private_profile_app_scroller_badge, ImageSpan.ALIGN_CENTER),
0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ mSortSections = context.getResources().getBoolean(R.bool.config_appsListSortSections);
}
/** Set the number of apps per row when device profile changes. */
@@ -239,9 +240,7 @@ public void onAppsUpdated() {
// As a special case for some languages (currently only Simplified Chinese), we may need to
// coalesce sections
- Locale curLocale = mActivityContext.getResources().getConfiguration().locale;
- boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
- if (localeRequiresSectionSorting) {
+ if (mSortSections) {
// Compute the section headers. We use a TreeMap with the section name comparator to
// ensure that the sections are ordered when we iterate over it later
appSteam = appSteam.collect(Collectors.groupingBy(
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 92c589c9663..fd5d15bbd60 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -251,6 +251,8 @@ void setActiveRV(int rvType) {
: rvType == AdapterHolder.WORK ? mWorkRV : mSearchRV;
mCurrentRV.addOnScrollListener(mOnScrollListener);
maybeSetTabVisibility(rvType == AdapterHolder.SEARCH ? GONE : VISIBLE);
+
+ updateExpectedHeight();
}
/** Update tab visibility to the given state, only if tabs are active (work profile exists). */
@@ -265,10 +267,7 @@ private void updateExpectedHeight() {
return;
}
mMaxTranslation += mFloatingRowsHeight;
- if (!mTabsHidden) {
- mMaxTranslation += mTabsAdditionalPaddingBottom
- + getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_margin_top);
- }
+ // No need for mMaxTranslation to be any taller now that we align below the header.
}
int getMaxTranslation() {
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index d5c3b577883..cef132de63a 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps;
+import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.app.animation.Interpolators.DECELERATE_1_7;
@@ -67,9 +68,9 @@ protected void animateToState(boolean goingToSearch, long duration, Runnable onE
}));
}
mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true);
- mAllAppsContainerView.getFloatingHeaderView().setVisibility(VISIBLE);
- mAllAppsContainerView.getFloatingHeaderView().maybeSetTabVisibility(VISIBLE);
- mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+ mAllAppsContainerView.getFloatingHeaderView().setVisibility(GONE);
+ mAllAppsContainerView.getFloatingHeaderView().maybeSetTabVisibility(GONE);
+ mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(GONE);
getRecyclerView().setVisibility(VISIBLE);
}
@@ -114,7 +115,7 @@ protected int onProgressUpdated(float searchToAzProgress) {
*/
@Override
protected boolean shouldAnimate(View view, boolean hasDecorationInfo, boolean appRowComplete) {
- return !isAppIcon(view) || appRowComplete;
+ return appRowComplete;
}
@Override
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index 96998a3e388..76435814c40 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -116,14 +116,14 @@ private void updateCurrentState(@UserProfileState int currentState) {
if (getAH() != null) {
getAH().mAppsList.updateAdapterItems();
}
- if (mWorkModeSwitch != null) {
- updateWorkFAB(mAllApps.getCurrentPage());
- }
if (getCurrentState() == STATE_ENABLED) {
attachWorkModeSwitch();
} else if (getCurrentState() == STATE_DISABLED) {
detachWorkModeSwitch();
}
+ if (mWorkModeSwitch != null) {
+ updateWorkFAB(mAllApps.getCurrentPage());
+ }
}
/**
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index ec45415afab..ecd4a4d668f 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -125,8 +125,14 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
Log.i(TAG, "User tapped ime search button");
+ // Skip if the query is empty
+ String query = v.getText().toString();
+ if (query.isEmpty()) {
+ return false;
+ }
+
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
- return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
+ return mLauncher.getAppsView().getMainAdapterProvider().performGoogleSearch(v, query);
}
return false;
}
@@ -135,7 +141,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
public boolean onBackKey() {
// Only hide the search field if there is no query
String query = Utilities.trim(mInput.getEditableText().toString());
- if (query.isEmpty()) {
+ if (!query.isEmpty()) {
reset();
return true;
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 78c305b2ebd..88b02235f5f 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -77,7 +77,6 @@ public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defSty
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
- setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
mContentOverlap =
getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_content_overlap);
@@ -86,13 +85,15 @@ public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defSty
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mAppsView.getAppsStore().addUpdateListener(this);
+ if(mAppsView != null)
+ mAppsView.getAppsStore().addUpdateListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mAppsView.getAppsStore().removeUpdateListener(this);
+ if(mAppsView != null)
+ mAppsView.getAppsStore().removeUpdateListener(this);
}
@Override
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 8121d2afbf9..812ce410114 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -38,8 +38,6 @@
*/
public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
- private static final int MAX_RESULTS_COUNT = 5;
-
private final LauncherAppState mAppState;
private final Handler mResultHandler;
private final boolean mAddNoResultsMessage;
@@ -93,13 +91,11 @@ public static ArrayList getTitleMatchResult(List apps, Str
StringMatcherUtility.StringMatcher matcher =
StringMatcherUtility.StringMatcher.getInstance();
- int resultCount = 0;
int total = apps.size();
- for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
+ for (int i = 0; i < total; i++) {
AppInfo info = apps.get(i);
if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
result.add(AdapterItem.asApp(info));
- resultCount++;
}
}
return result;
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 4a8c96b3bd3..9e439c80c76 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps.search;
+import android.app.SearchManager;
+import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -63,6 +65,17 @@ public boolean launchHighlightedItem() {
return false;
}
+ @Override
+ public boolean performGoogleSearch(View view, String query) {
+ if (view instanceof View) {
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.setPackage("com.google.android.googlequicksearchbox");
+ intent.putExtra(SearchManager.QUERY, query);
+ return mLauncher.startActivitySafely(view, intent, null) != null;
+ }
+ return false;
+ }
+
@Override
public View getHighlightedItem() {
return mHighlightedView;
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 82c9c90f333..39868ef39c5 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -81,4 +81,9 @@ public int[] getSupportedItemsPerRowArray() {
public int getItemsPerRow(int viewType, int appsPerRow) {
return appsPerRow;
}
+
+ /**
+ * Performs a Google search using the given query.
+ */
+ public abstract boolean performGoogleSearch(View view, String query);
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 33e6f9106f7..35f2b40f923 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -175,7 +175,7 @@ public static boolean enableTaskbarNoRecreate() {
"PROMISE_APPS_IN_ALL_APPS", DISABLED, "Add promise icon in all-apps");
public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(270390904,
- "KEYGUARD_ANIMATION", DISABLED,
+ "KEYGUARD_ANIMATION", ENABLED,
"Enable animation for keyguard going away on wallpaper");
public static final BooleanFlag ENABLE_DEVICE_SEARCH = getReleaseFlag(270390907,
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index bc5a164da8d..ff902e0817e 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -32,6 +32,7 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Flags;
+import com.android.launcher3.Utilities;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -407,6 +408,11 @@ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return false;
}
+ if (!Utilities.isWorkspaceEditAllowed(mActivity.getDragLayer().getContext())) {
+ cancelDrag();
+ return false;
+ }
+
Point dragLayerPos = getClampedDragLayerPos(getX(ev), getY(ev));
mLastTouch.set(dragLayerPos.x, dragLayerPos.y);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 00636a30a30..7e3ac1ae4cc 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -201,7 +201,9 @@ public static FolderIcon inflateIcon(int resId, ActivityContext activity,
icon.setClipToPadding(false);
icon.mFolderName = icon.findViewById(R.id.folder_icon_name);
- icon.mFolderName.setText(folderInfo.title);
+ if (icon.mFolderName.shouldShowLabel()) {
+ icon.mFolderName.setText(folderInfo.title);
+ }
icon.mFolderName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index be5f8f76efe..c408946a933 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -48,6 +48,8 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -111,9 +113,11 @@ public void getSuggestedFolderName(Context context,
Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
}
+ // A shallow copy tring to avoid ConcurrentModificationException
+ final ArrayList candidates = new ArrayList<>(workspaceItemInfos);
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
- Set users = workspaceItemInfos.stream().map(w -> w.user)
+ Set users = candidates.stream().map(w -> w.user)
.collect(Collectors.toSet());
if (users.size() == 1 && !users.contains(Process.myUserHandle())) {
setAsLastSuggestion(nameInfos, getWorkFolderName(context));
@@ -121,10 +125,9 @@ public void getSuggestedFolderName(Context context,
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
- Set packageNames = workspaceItemInfos.stream()
- .map(WorkspaceItemInfo::getTargetComponent)
- .filter(Objects::nonNull)
- .map(ComponentName::getPackageName)
+ Set packageNames = candidates.stream()
+ .filter(workspaceItemInfo -> workspaceItemInfo.getTargetComponent() != null)
+ .map(workspaceItemInfo -> workspaceItemInfo.getTargetComponent().getPackageName())
.collect(Collectors.toSet());
if (packageNames.size() == 1) {
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index 260d490926f..0d6897160e9 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import android.animation.ObjectAnimator;
+import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
@@ -36,6 +37,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.testing.shared.ResourceUtils;
@@ -46,7 +48,8 @@
/**
* View scrim which draws behind hotseat and workspace
*/
-public class SysUiScrim implements View.OnAttachStateChangeListener {
+public class SysUiScrim implements View.OnAttachStateChangeListener,
+ SharedPreferences.OnSharedPreferenceChangeListener {
/**
* Receiver used to get a signal that the user unlocked their device.
@@ -73,6 +76,8 @@ public void onUserPresent() {
private static final int BOTTOM_MASK_HEIGHT_DP = 200;
private static final int TOP_MASK_HEIGHT_DP = 70;
+ private static final String KEY_SHOW_TOP_SHADOW = "pref_show_top_shadow";
+
private boolean mDrawTopScrim, mDrawBottomScrim;
private final RectF mTopMaskRect = new RectF();
@@ -101,7 +106,9 @@ public SysUiScrim(View view) {
mTopMaskHeight = ResourceUtils.pxFromDp(TOP_MASK_HEIGHT_DP, dm);
mBottomMaskHeight = ResourceUtils.pxFromDp(BOTTOM_MASK_HEIGHT_DP, dm);
- mHideSysUiScrim = Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText);
+ SharedPreferences prefs = LauncherPrefs.getPrefs(view.getContext());
+ final boolean showScrim = prefs.getBoolean(KEY_SHOW_TOP_SHADOW, true);
+ mHideSysUiScrim = !showScrim;
mTopMaskBitmap = mHideSysUiScrim ? null : createDitheredAlphaMask(mTopMaskHeight,
new int[]{0x3DFFFFFF, 0x0AFFFFFF, 0x00FFFFFF},
@@ -114,6 +121,7 @@ public SysUiScrim(View view) {
if (!KEYGUARD_ANIMATION.get() && !mHideSysUiScrim) {
view.addOnAttachStateChangeListener(this);
}
+ prefs.registerOnSharedPreferenceChangeListener(this);
}
/**
@@ -183,6 +191,13 @@ public void onViewDetachedFromWindow(View view) {
ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
}
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (key.equals(KEY_SHOW_TOP_SHADOW)) {
+ mRoot.invalidate();
+ }
+ }
+
/**
* Set the width and height of the view being scrimmed
*/
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 44e448eea11..91b8ed77245 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -592,7 +592,9 @@ private synchronized BitmapInfo getBadgedIcon(@Nullable final BitmapInfo bitmap,
if (bitmap == null) {
return getDefaultIcon(user);
}
- return bitmap.withFlags(getUserFlagOpLocked(user));
+ try (BaseIconFactory bif = getIconFactory()) {
+ return bitmap.withUser(user, bif);
+ }
}
protected void applyCacheEntry(@NonNull final CacheEntry entry,
diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java
index c4d5f2b5b56..6986578be9b 100644
--- a/src/com/android/launcher3/icons/LauncherIconProvider.java
+++ b/src/com/android/launcher3/icons/LauncherIconProvider.java
@@ -90,7 +90,7 @@ private Map getThemedIconMap() {
if (TAG_ICON.equals(parser.getName())) {
String pkg = parser.getAttributeValue(null, ATTR_PACKAGE);
int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0);
- if (iconId != 0 && !TextUtils.isEmpty(pkg)) {
+ if (iconId != 0 && pkg != null && !pkg.isEmpty()) {
map.put(pkg, new ThemeData(res, iconId));
}
}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 884d448b6c1..617a7baecbb 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -31,6 +31,7 @@
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.UserIconInfo;
+import com.android.launcher3.Utilities;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -59,12 +60,15 @@ public static void clearPool(Context context) {
private MonochromeIconFactory mMonochromeIconFactory;
+ private boolean isMonochromeIconsEnabled;
+
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
ConcurrentLinkedQueue pool) {
super(context, fillResIconDpi, iconBitmapSize,
IconShape.INSTANCE.get(context).getShape().enableShapeDetection());
mMonoIconEnabled = Themes.isThemedIconEnabled(context);
mPool = pool;
+ isMonochromeIconsEnabled = Utilities.enableMonoChromeThemedIcons(context);
}
/**
@@ -78,7 +82,7 @@ public void recycle() {
@Override
protected Drawable getMonochromeDrawable(AdaptiveIconDrawable base) {
Drawable mono = super.getMonochromeDrawable(base);
- if (mono != null || !Flags.forceMonochromeAppIcons()) {
+ if (mono != null || !isMonochromeIconsEnabled) {
return mono;
}
if (mMonochromeIconFactory == null) {
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index 21d157a41b2..7590828fe68 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -90,7 +90,7 @@ private void computeLocationRelativeToContainer(View child, Rect outRect) {
}
View parent = (View) child.getParent();
- if (parent != mContainer) {
+ if (parent != null && parent != mContainer) {
if (parent instanceof PagedView) {
PagedView page = (PagedView) parent;
outRect.left -= page.getScrollForPage(page.indexOfChild(child));
diff --git a/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java
new file mode 100644
index 00000000000..a2bf22d1f21
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
+
+@SuppressWarnings("unused")
+public class HiddenAppsFilter extends AppFilter {
+ private TrustDatabaseHelper mDbHelper;
+
+ public HiddenAppsFilter(Context context) {
+ super(context);
+
+ mDbHelper = TrustDatabaseHelper.getInstance(context);
+ }
+
+ @Override
+ public boolean shouldShowApp(ComponentName app) {
+ return !mDbHelper.isPackageHidden(app.getPackageName()) && super.shouldShowApp(app);
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java
new file mode 100644
index 00000000000..dec109feb48
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.lineage.trust.db.TrustComponent;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LoadTrustComponentsTask extends AsyncTask> {
+ @NonNull
+ private TrustDatabaseHelper mDbHelper;
+
+ @NonNull
+ private PackageManager mPackageManager;
+
+ @NonNull
+ private Callback mCallback;
+
+ LoadTrustComponentsTask(@NonNull TrustDatabaseHelper dbHelper,
+ @NonNull PackageManager packageManager,
+ @NonNull Callback callback) {
+ mDbHelper = dbHelper;
+ mPackageManager = packageManager;
+ mCallback = callback;
+ }
+
+ @Override
+ protected List doInBackground(Void... voids) {
+ List list = new ArrayList<>();
+
+ Intent filter = new Intent(Intent.ACTION_MAIN, null);
+ filter.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ List apps = mPackageManager.queryIntentActivities(filter,
+ PackageManager.GET_META_DATA);
+
+ int numPackages = apps.size();
+ for (int i = 0; i < numPackages; i++) {
+ ResolveInfo app = apps.get(i);
+ try {
+ String pkgName = app.activityInfo.packageName;
+ String label = mPackageManager.getApplicationLabel(
+ mPackageManager.getApplicationInfo(pkgName,
+ PackageManager.GET_META_DATA)).toString();
+ Drawable icon = app.loadIcon(mPackageManager);
+ boolean isHidden = mDbHelper.isPackageHidden(pkgName);
+ boolean isProtected = mDbHelper.isPackageProtected(pkgName);
+
+ list.add(new TrustComponent(pkgName, icon, label, isHidden, isProtected));
+
+ publishProgress(Math.round(i * 100f / numPackages));
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Collections.sort(list, (a, b) -> a.getLabel().compareTo(b.getLabel()));
+ }
+
+ return list;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ if (values.length > 0) {
+ mCallback.onLoadListProgress(values[0]);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List trustComponents) {
+ mCallback.onLoadCompleted(trustComponents);
+ }
+
+ interface Callback {
+ void onLoadListProgress(int progress);
+ void onLoadCompleted(List result);
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java
new file mode 100644
index 00000000000..54e88e1b39f
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust;
+
+import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.HIDDEN;
+import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.PROTECTED;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.lineage.trust.db.TrustComponent;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
+
+import java.util.List;
+
+public class TrustAppsActivity extends Activity implements
+ TrustAppsAdapter.Listener,
+ LoadTrustComponentsTask.Callback,
+ UpdateItemTask.UpdateCallback {
+
+ private static final String KEY_TRUST_ONBOARDING = "pref_trust_onboarding";
+
+ private RecyclerView mRecyclerView;
+ private LinearLayout mLoadingView;
+ private ProgressBar mProgressBar;
+
+ private TrustDatabaseHelper mDbHelper;
+ private TrustAppsAdapter mAdapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstance) {
+ super.onCreate(savedInstance);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ setContentView(R.layout.activity_hidden_apps);
+ mRecyclerView = findViewById(R.id.hidden_apps_list);
+ mLoadingView = findViewById(R.id.hidden_apps_loading);
+ mLoadingView.setVisibility(View.VISIBLE);
+ mProgressBar = findViewById(R.id.hidden_apps_progress_bar);
+
+ final boolean hasSecureKeyguard = Utilities.hasSecureKeyguard(this);
+ mAdapter = new TrustAppsAdapter(this, hasSecureKeyguard);
+ mDbHelper = TrustDatabaseHelper.getInstance(this);
+
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mRecyclerView.setItemAnimator(new DefaultItemAnimator());
+ mRecyclerView.setAdapter(mAdapter);
+
+ showOnBoarding(false);
+
+ new LoadTrustComponentsTask(mDbHelper, getPackageManager(), this).execute();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.menu_trust_apps, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ finish();
+ return true;
+ } else if (id == R.id.menu_trust_help) {
+ showOnBoarding(true);
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onHiddenItemChanged(@NonNull TrustComponent component) {
+ new UpdateItemTask(mDbHelper, this, HIDDEN).execute(component);
+ }
+
+ @Override
+ public void onProtectedItemChanged(@NonNull TrustComponent component) {
+ new UpdateItemTask(mDbHelper, this, PROTECTED).execute(component);
+ }
+
+ @Override
+ public void onUpdated(boolean result) {
+ LauncherAppState.INSTANCE.executeIfCreated(app -> app.getModel().forceReload());
+ }
+
+ @Override
+ public void onLoadListProgress(int progress) {
+ mProgressBar.setProgress(progress);
+ }
+
+ @Override
+ public void onLoadCompleted(List result) {
+ mLoadingView.setVisibility(View.GONE);
+ mRecyclerView.setVisibility(View.VISIBLE);
+ mAdapter.update(result);
+ }
+
+ private void showOnBoarding(boolean forceShow) {
+ SharedPreferences preferenceManager = LauncherPrefs.getPrefs(this);
+ if (!forceShow && preferenceManager.getBoolean(KEY_TRUST_ONBOARDING, false)) {
+ return;
+ }
+
+ preferenceManager.edit()
+ .putBoolean(KEY_TRUST_ONBOARDING, true)
+ .apply();
+
+ new AlertDialog.Builder(this)
+ .setView(R.layout.dialog_trust_welcome)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java
new file mode 100644
index 00000000000..6b827d6e1e8
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust;
+
+import android.graphics.drawable.Animatable2;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.lineage.trust.db.TrustComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class TrustAppsAdapter extends RecyclerView.Adapter {
+ private List mList = new ArrayList<>();
+ private Listener mListener;
+ private boolean mHasSecureKeyguard;
+
+ TrustAppsAdapter(Listener listener, boolean hasSecureKeyguard) {
+ mListener = listener;
+ mHasSecureKeyguard = hasSecureKeyguard;
+ }
+
+ public void update(List list) {
+ DiffUtil.DiffResult result = DiffUtil.calculateDiff(new Callback(mList, list));
+ mList = list;
+ result.dispatchUpdatesTo(this);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
+ return new ViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_hidden_app, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
+ viewHolder.bind(mList.get(i), mHasSecureKeyguard);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mList.size();
+ }
+
+ public interface Listener {
+ void onHiddenItemChanged(@NonNull TrustComponent component);
+
+ void onProtectedItemChanged(@NonNull TrustComponent component);
+ }
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+ private ImageView mIconView;
+ private TextView mLabelView;
+ private ImageView mHiddenView;
+ private ImageView mProtectedView;
+
+ ViewHolder(@NonNull View itemView) {
+ super(itemView);
+
+ mIconView = itemView.findViewById(R.id.item_hidden_app_icon);
+ mLabelView = itemView.findViewById(R.id.item_hidden_app_title);
+ mHiddenView = itemView.findViewById(R.id.item_hidden_app_switch);
+ mProtectedView = itemView.findViewById(R.id.item_protected_app_switch);
+ }
+
+ void bind(TrustComponent component, boolean hasSecureKeyguard) {
+ mIconView.setImageDrawable(component.getIcon());
+ mLabelView.setText(component.getLabel());
+
+ mHiddenView.setImageResource(component.isHidden() ?
+ R.drawable.ic_hidden_locked : R.drawable.ic_hidden_unlocked);
+ mProtectedView.setImageResource(component.isProtected() ?
+ R.drawable.ic_protected_locked : R.drawable.ic_protected_unlocked);
+
+ mProtectedView.setVisibility(hasSecureKeyguard ? View.VISIBLE : View.GONE);
+
+ mHiddenView.setOnClickListener(v -> {
+ component.invertVisibility();
+
+ mHiddenView.setImageResource(component.isHidden() ?
+ R.drawable.avd_hidden_lock : R.drawable.avd_hidden_unlock);
+ AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mHiddenView.getDrawable();
+
+ int position = getAdapterPosition();
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
+ avd.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ updateHiddenList(position, component);
+ }
+ });
+ avd.start();
+ } else {
+ avd.start();
+ updateHiddenList(position, component);
+ }
+ });
+
+ mProtectedView.setOnClickListener(v -> {
+ component.invertProtection();
+
+ mProtectedView.setImageResource(component.isProtected() ?
+ R.drawable.avd_protected_lock : R.drawable.avd_protected_unlock);
+ AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mProtectedView.getDrawable();
+
+ int position = getAdapterPosition();
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
+ avd.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ updateProtectedList(position, component);
+ }
+ });
+ avd.start();
+ } else {
+ avd.start();
+ updateProtectedList(position, component);
+ }
+ });
+ }
+
+ private void updateHiddenList(int position, TrustComponent component) {
+ mListener.onHiddenItemChanged(component);
+ updateList(position, component);
+ }
+
+ private void updateProtectedList(int position, TrustComponent component) {
+ mListener.onProtectedItemChanged(component);
+ updateList(position, component);
+ }
+
+ private void updateList(int position, TrustComponent component) {
+ mList.set(position, component);
+ notifyItemChanged(position);
+ }
+ }
+
+ private static class Callback extends DiffUtil.Callback {
+ List mOldList;
+ List mNewList;
+
+ public Callback(List oldList,
+ List newList) {
+ mOldList = oldList;
+ mNewList = newList;
+ }
+
+
+ @Override
+ public int getOldListSize() {
+ return mOldList.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return mNewList.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int iOld, int iNew) {
+ String oldPkg = mOldList.get(iOld).getPackageName();
+ String newPkg = mNewList.get(iNew).getPackageName();
+ return oldPkg.equals(newPkg);
+ }
+
+ @Override
+ public boolean areContentsTheSame(int iOld, int iNew) {
+ return mOldList.get(iOld).equals(mNewList.get(iNew));
+ }
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/UpdateItemTask.java b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java
new file mode 100644
index 00000000000..aad0f1a99e6
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust;
+
+import android.os.AsyncTask;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.lineage.trust.db.TrustComponent;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
+
+public class UpdateItemTask extends AsyncTask {
+ @NonNull
+ private TrustDatabaseHelper mDbHelper;
+ @NonNull
+ private UpdateCallback mCallback;
+ @NonNull
+ private TrustComponent.Kind mKind;
+
+ UpdateItemTask(@NonNull TrustDatabaseHelper dbHelper,
+ @NonNull UpdateCallback callback,
+ @NonNull TrustComponent.Kind kind) {
+ mDbHelper = dbHelper;
+ mCallback = callback;
+ mKind = kind;
+ }
+
+ @Override
+ protected Boolean doInBackground(TrustComponent... trustComponents) {
+ if (trustComponents.length < 1) {
+ return false;
+ }
+
+ TrustComponent component = trustComponents[0];
+ String pkgName = component.getPackageName();
+
+ switch (mKind) {
+ case HIDDEN:
+ if (component.isHidden()) {
+ mDbHelper.addHiddenApp(pkgName);
+ } else {
+ mDbHelper.removeHiddenApp(pkgName);
+ }
+ break;
+ case PROTECTED:
+ if (component.isProtected()) {
+ mDbHelper.addProtectedApp(pkgName);
+ } else {
+ mDbHelper.removeProtectedApp(pkgName);
+ }
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ mCallback.onUpdated(result);
+ }
+
+ interface UpdateCallback {
+ void onUpdated(boolean result);
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/db/TrustComponent.java b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java
new file mode 100644
index 00000000000..5342bde3df4
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust.db;
+
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+
+public class TrustComponent {
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private final Drawable mIcon;
+ @NonNull
+ private final String mLabel;
+
+ private boolean mIsHidden;
+ private boolean mIsProtected;
+
+ public TrustComponent(@NonNull String packageName, @NonNull Drawable icon,
+ @NonNull String label, boolean isHidden, boolean isProtected) {
+ mPackageName = packageName;
+ mIcon = icon;
+ mLabel = label;
+ mIsHidden = isHidden;
+ mIsProtected = isProtected;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @NonNull
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ @NonNull
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public boolean isHidden() {
+ return mIsHidden;
+ }
+
+ public boolean isProtected() {
+ return mIsProtected;
+ }
+
+ public void invertVisibility() {
+ mIsHidden = !mIsHidden;
+ }
+
+ public void invertProtection() {
+ mIsProtected = !mIsProtected;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof TrustComponent)) {
+ return false;
+ }
+
+ TrustComponent otherComponent = (TrustComponent) other;
+ return otherComponent.getPackageName().equals(mPackageName) &&
+ otherComponent.isHidden() == mIsHidden;
+ }
+
+ @Override
+ public int hashCode() {
+ return mPackageName.hashCode() + (mIsHidden ? 1 : 0);
+ }
+
+ public enum Kind {
+ HIDDEN,
+ PROTECTED,
+ }
+}
diff --git a/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java
new file mode 100644
index 00000000000..8923e6a0324
--- /dev/null
+++ b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.lineage.trust.db;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class TrustDatabaseHelper extends SQLiteOpenHelper {
+ private static final int DATABASE_VERSION = 1;
+ private static final String DATABASE_NAME = "trust_apps_db";
+
+ private static final String TABLE_NAME = "trust_apps";
+ private static final String KEY_UID = "uid";
+ private static final String KEY_PKGNAME = "pkgname";
+ private static final String KEY_HIDDEN = "hidden";
+ private static final String KEY_PROTECTED = "protected";
+
+ @Nullable
+ private static TrustDatabaseHelper sSingleton;
+
+ private TrustDatabaseHelper(@NonNull Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ public static synchronized TrustDatabaseHelper getInstance(@NonNull Context context) {
+ if (sSingleton == null) {
+ sSingleton = new TrustDatabaseHelper(context);
+ }
+
+ return sSingleton;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ String CMD_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
+ "(" +
+ KEY_UID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ KEY_PKGNAME + " TEXT," +
+ KEY_HIDDEN + " INTEGER DEFAULT 0," +
+ KEY_PROTECTED + " INTEGER DEFAULT 0" +
+ ")";
+ db.execSQL(CMD_CREATE_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+
+ public void addHiddenApp(@NonNull String packageName) {
+ if (isPackageHidden(packageName)) {
+ return;
+ }
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.beginTransaction();
+
+ try {
+ ContentValues values = new ContentValues();
+ values.put(KEY_PKGNAME, packageName);
+ values.put(KEY_HIDDEN, 1);
+
+ int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?",
+ new String[]{KEY_PKGNAME});
+ if (rows != 1) {
+ // Entry doesn't exist, create a new one
+ db.insertOrThrow(TABLE_NAME, null, values);
+ }
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void addProtectedApp(@NonNull String packageName) {
+ if (isPackageProtected(packageName)) {
+ return;
+ }
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.beginTransaction();
+
+ try {
+ ContentValues values = new ContentValues();
+ values.put(KEY_PKGNAME, packageName);
+ values.put(KEY_PROTECTED, 1);
+
+ int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?",
+ new String[]{KEY_PKGNAME});
+ if (rows != 1) {
+ // Entry doesn't exist, create a new one
+ db.insertOrThrow(TABLE_NAME, null, values);
+ }
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+
+ public void removeHiddenApp(@NonNull String packageName) {
+ if (!isPackageHidden(packageName)) {
+ return;
+ }
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.beginTransaction();
+
+ try {
+ ContentValues values = new ContentValues();
+ values.put(KEY_HIDDEN, 0);
+
+ db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName});
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void removeProtectedApp(@NonNull String packageName) {
+ if (!isPackageProtected(packageName)) {
+ return;
+ }
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.beginTransaction();
+
+ try {
+ ContentValues values = new ContentValues();
+ values.put(KEY_PROTECTED, 0);
+
+ db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName});
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public boolean isPackageHidden(@NonNull String packageName) {
+ String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME,
+ KEY_PKGNAME, KEY_HIDDEN);
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)});
+ boolean result = false;
+ try {
+ result = cursor.getCount() != 0;
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+
+ return result;
+ }
+
+ public int getTotalPackageHidden() {
+ String query = String.format("SELECT * FROM %s WHERE %s = ?", TABLE_NAME, KEY_HIDDEN);
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor cursor = db.rawQuery(query, new String[]{ });
+ int result = 0;
+ try {
+ result = cursor.getCount();
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+
+ return result;
+ }
+
+ public boolean isPackageProtected(@NonNull String packageName) {
+ String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME,
+ KEY_PKGNAME, KEY_PROTECTED);
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)});
+ boolean result = false;
+ try {
+ result = cursor.getCount() != 0;
+ } catch (Exception e) {
+ // Ignored
+ } finally {
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 64ebbf38337..b3b5c600e23 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -34,6 +34,7 @@
import com.android.launcher3.AppFilter;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -75,6 +76,7 @@ public class AllAppsList {
private boolean mDataChanged = false;
private Consumer mRemoveListener = NO_OP_CONSUMER;
+ private TrustDatabaseHelper mTrustData;
private AlphabeticIndexCompat mIndex;
@@ -90,9 +92,10 @@ public class AllAppsList {
/**
* Boring constructor.
*/
- public AllAppsList(IconCache iconCache, AppFilter appFilter) {
+ public AllAppsList(IconCache iconCache, AppFilter appFilter, TrustDatabaseHelper trustDatabaseHelper) {
mIconCache = iconCache;
mAppFilter = appFilter;
+ mTrustData = trustDatabaseHelper;
mIndex = new AlphabeticIndexCompat(LocaleList.getDefault());
}
@@ -143,6 +146,9 @@ public void add(AppInfo info, LauncherActivityInfo activityInfo) {
}
public void add(AppInfo info, LauncherActivityInfo activityInfo, boolean loadIcon) {
+ if (mTrustData != null && mTrustData.isPackageHidden(info.getTargetPackage())) {
+ return;
+ }
if (!mAppFilter.shouldShowApp(info.componentName)) {
return;
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 079987b0b54..912a136bce8 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -30,6 +30,7 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.os.UserManager;
+import android.widget.Toast;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -39,6 +40,8 @@
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
@@ -117,6 +120,7 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
: ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet removedComponents = new HashSet<>();
final HashMap> activitiesLists = new HashMap<>();
+ boolean needsRestart = false;
if (DEBUG) {
Log.d(TAG, "Package updated: mOp=" + getOpString()
+ " packages=" + Arrays.toString(packages));
@@ -134,6 +138,9 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
}
activitiesLists.put(packages[i],
appsList.addPackage(context, packages[i], mUser));
+ if (isTargetPackage(packages[i])) {
+ needsRestart = true;
+ }
}
flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
@@ -150,6 +157,9 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
iconCache.updateIconsForPkg(packages[i], mUser);
activitiesLists.put(packages[i],
appsList.updatePackage(context, packages[i], mUser));
+ if (isTargetPackage(packages[i])) {
+ needsRestart = true;
+ }
}
}
// Since package was just updated, the target must be available now.
@@ -158,6 +168,9 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
case OP_REMOVE: {
for (int i = 0; i < N; i++) {
iconCache.removeIconsForPkg(packages[i], mUser);
+ if (isTargetPackage(packages[i])) {
+ needsRestart = true;
+ }
}
// Fall through
}
@@ -175,6 +188,11 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
flagOp = FlagOp.NO_OP.setFlag(
WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND);
appsList.updateDisabledFlags(matcher, flagOp);
+ for (int i = 0; i < N; i++) {
+ if (isTargetPackage(packages[i])) {
+ needsRestart = true;
+ }
+ }
break;
case OP_USER_AVAILABILITY_CHANGE: {
UserManagerState ums = new UserManagerState();
@@ -428,6 +446,11 @@ public void execute(@NonNull ModelTaskController taskController, @NonNull BgData
}
taskController.bindUpdatedWidgets(dataModel);
}
+
+ if (needsRestart) {
+ Toast.makeText(context, R.string.updating_launcher_components, Toast.LENGTH_SHORT).show();
+ Utilities.restart(context);
+ }
}
/**
@@ -465,4 +488,8 @@ private String getOpString() {
default -> "UNKNOWN";
};
}
+
+ private boolean isTargetPackage(String packageName) {
+ return packageName.equals(Utilities.GSA_PACKAGE);
+ }
}
diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java
index ed3243043bd..d6b7c205694 100644
--- a/src/com/android/launcher3/model/UserManagerState.java
+++ b/src/com/android/launcher3/model/UserManagerState.java
@@ -39,7 +39,7 @@ public class UserManagerState {
* Initialises the state values for all users
*/
public void init(UserCache userCache, UserManager userManager) {
- for (UserHandle user : userManager.getUserProfiles()) {
+ for (UserHandle user : userCache.getUserProfiles()) {
long serialNo = userCache.getSerialNumberForUser(user);
boolean isUserQuiet = userManager.isQuietModeEnabled(user);
// Mapping different UserHandles to the same serialNo in allUsers could lead to losing
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 454ae966124..e1875d4acd9 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -30,6 +30,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
@@ -343,14 +344,19 @@ private static class WidgetValidityCheck implements Predicate {
private final InvariantDeviceProfile mIdp;
private final AppFilter mAppFilter;
+ private TrustDatabaseHelper mTrustData;
WidgetValidityCheck(LauncherAppState app) {
mIdp = app.getInvariantDeviceProfile();
mAppFilter = new AppFilter(app.getContext());
+ mTrustData = app.getTrustData();
}
@Override
public boolean test(WidgetItem item) {
+ if (mTrustData != null && mTrustData.isPackageHidden(item.componentName.getPackageName())) {
+ return false;
+ }
if (item.widgetInfo != null) {
if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
// Widget is hidden from picker
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index f4dda5593a5..04a14d27b56 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -236,13 +236,13 @@ private static int computeWidgetFeatures(
if (ATLEAST_S && providerInfo.previewLayout != Resources.ID_NULL) {
widgetFeatures |= FEATURE_PREVIEW_LAYOUT;
}
- if (ATLEAST_S && providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0) {
+ if (ATLEAST_S && (providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0)) {
widgetFeatures |= FEATURE_TARGET_CELL_SIZE;
}
if (providerInfo.minResizeWidth > 0 || providerInfo.minResizeHeight > 0) {
widgetFeatures |= FEATURE_MIN_SIZE;
}
- if (ATLEAST_S && providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0) {
+ if (ATLEAST_S && (providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0)) {
widgetFeatures |= FEATURE_MAX_SIZE;
}
if (hostView instanceof LauncherAppWidgetHostView &&
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index e44ea1d69d0..e2b1844265f 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -187,6 +187,10 @@ public void setScroll(int currentScroll, int totalScroll) {
mTotalScroll = totalScroll;
int scrollPerPage = totalScroll / (mNumPages - 1);
+ if (scrollPerPage == 0) {
+ return;
+ }
+
int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
int pageToLeftScroll = pageToLeft * scrollPerPage;
int pageToRightScroll = pageToLeftScroll + scrollPerPage;
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index ed25186da4f..92a229fe524 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -99,6 +99,7 @@ public void close() {
@WorkerThread
private void initAsync() {
mUserChangeReceiver.register(mContext,
+ Context.RECEIVER_EXPORTED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
@@ -107,7 +108,8 @@ private void initAsync() {
ACTION_PROFILE_UNLOCKED,
ACTION_PROFILE_LOCKED,
ACTION_PROFILE_AVAILABLE,
- ACTION_PROFILE_UNAVAILABLE);
+ ACTION_PROFILE_UNAVAILABLE,
+ Intent.ACTION_PARALLEL_SPACE_CHANGED);
updateCache();
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 1c9db17822f..53824999707 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -48,6 +48,7 @@
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragController;
@@ -625,6 +626,9 @@ public boolean onLongClick(View v) {
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
+ // Return early if workspace edit is disabled
+ if (!Utilities.isWorkspaceEditAllowed(mLauncher.getApplicationContext())) return false;
+
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
sv.setWillDrawIcon(false);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 83e98100609..884070be562 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -5,18 +5,26 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -40,6 +48,7 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetsBottomSheet;
+import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
@@ -125,6 +134,7 @@ public Widgets(T target, ItemInfo itemInfo, @NonNull View originalView) {
@Override
public void onClick(View view) {
+ if (!Utilities.isWorkspaceEditAllowed((Context) mTarget)) return;
AbstractFloatingView.closeAllOpenViews(mTarget);
WidgetsBottomSheet widgetsBottomSheet =
(WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
@@ -378,6 +388,64 @@ public void onClick(View view) {
}
}
+ public static final Factory UNINSTALL = (activity, itemInfo, originalView) ->
+ itemInfo.getTargetComponent() == null ||
+ PackageManagerHelper.isSystemApp((Context) activity,
+ itemInfo.getTargetComponent().getPackageName())
+ ? null : new UnInstall(activity, itemInfo, originalView);
+
+ public static class UnInstall extends SystemShortcut {
+
+ public UnInstall(T target, ItemInfo itemInfo, View originalView) {
+ super(R.drawable.ic_uninstall_no_shadow, R.string.uninstall_drop_target_label,
+ target, itemInfo, originalView);
+ }
+
+ /**
+ * @return the component name that should be uninstalled or null.
+ */
+ private ComponentName getUninstallTarget(ItemInfo item, Context context) {
+ Intent intent = null;
+ UserHandle user = null;
+ if (item != null &&
+ (item.itemType == ITEM_TYPE_APPLICATION || item.itemType == ITEM_TYPE_TASK)) {
+ intent = item.getIntent();
+ user = item.user;
+ }
+ if (intent != null) {
+ LauncherActivityInfo info = context.getSystemService(LauncherApps.class)
+ .resolveActivity(intent, user);
+ if (info != null
+ && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return info.getComponentName();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onClick(View view) {
+ ComponentName cn = getUninstallTarget(mItemInfo, view.getContext());
+ if (cn == null) {
+ // System applications cannot be installed. For now, show a toast explaining that.
+ // We may give them the option of disabling apps this way.
+ Toast.makeText(view.getContext(), R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ try {
+ Intent intent = Intent.parseUri(view.getContext().getString(R.string.delete_package_intent), 0)
+ .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+ .putExtra(Intent.EXTRA_USER, mItemInfo.user);
+
+ ((Context) mTarget).startActivity(intent);
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ } catch (URISyntaxException e) {
+ // Do nothing.
+ }
+ }
+ }
+
protected void dismissTaskMenuView() {
mAbstractFloatingViewHelper.closeOpenViews(mTarget, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
diff --git a/src/com/android/launcher3/qsb/AssistantIconView.java b/src/com/android/launcher3/qsb/AssistantIconView.java
new file mode 100644
index 00000000000..0821164b8e9
--- /dev/null
+++ b/src/com/android/launcher3/qsb/AssistantIconView.java
@@ -0,0 +1,31 @@
+package com.android.launcher3.qsb;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import com.android.launcher3.R;
+import com.android.launcher3.qsb.QsbContainerView;
+
+public class AssistantIconView extends ImageView {
+
+ public AssistantIconView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setScaleType(ScaleType.CENTER);
+ setOnClickListener(view -> {
+ Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK).setPackage(QsbContainerView.getSearchWidgetPackageName(context));
+ context.startActivity(intent);
+ });
+ }
+
+ public AssistantIconView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setScaleType(ScaleType.CENTER);
+ setOnClickListener(view -> {
+ Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK).setPackage(QsbContainerView.getSearchWidgetPackageName(context));
+ context.startActivity(intent);
+ });
+ }
+
+}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index d6b41b06a39..bc28bdc71ae 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -48,6 +48,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.FragmentWithPreview;
import com.android.launcher3.widget.util.WidgetSizes;
@@ -79,6 +80,9 @@ public static String getSearchWidgetPackageName(@NonNull Context context) {
if (componentName != null) {
providerPkg = searchManager.getGlobalSearchActivity().getPackageName();
}
+ if (providerPkg == null && Utilities.isGSAEnabled(context)) {
+ providerPkg = Utilities.GSA_PACKAGE;
+ }
}
return providerPkg;
}
@@ -280,12 +284,7 @@ public void onDestroy() {
}
private void rebindFragment() {
- // Exit if the embedded qsb is disabled
- if (!isQsbEnabled()) {
- return;
- }
-
- if (mWrapper != null && getContext() != null) {
+ if (mWrapper != null && getContext() != null && isQsbEnabled()) {
mWrapper.removeAllViews();
mWrapper.addView(createQsb(mWrapper));
}
diff --git a/src/com/android/launcher3/qsb/QsbLayout.java b/src/com/android/launcher3/qsb/QsbLayout.java
new file mode 100644
index 00000000000..70180d0cf23
--- /dev/null
+++ b/src/com/android/launcher3/qsb/QsbLayout.java
@@ -0,0 +1,118 @@
+package com.android.launcher3.qsb;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import androidx.core.view.ViewCompat;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.qsb.QsbContainerView;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.LauncherPrefs;
+
+public class QsbLayout extends FrameLayout implements
+ SharedPreferences.OnSharedPreferenceChangeListener {
+
+ ImageView mAssistantIcon;
+ ImageView mGoogleIcon;
+ ImageView mLensIcon;
+ Context mContext;
+
+ public QsbLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ public QsbLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mContext = context;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mAssistantIcon = findViewById(R.id.mic_icon);
+ mGoogleIcon = findViewById(R.id.g_icon);
+ mLensIcon = findViewById(R.id.lens_icon);
+ setIcons();
+
+ LauncherPrefs.getPrefs(mContext).registerOnSharedPreferenceChangeListener(this);
+
+ String searchPackage = QsbContainerView.getSearchWidgetPackageName(mContext);
+ setOnClickListener(view -> {
+ mContext.startActivity(new Intent("android.search.action.GLOBAL_SEARCH").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TASK).setPackage(searchPackage));
+ });
+
+ if (Utilities.isGSAEnabled(mContext)) {
+ enableLensIcon();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int requestedWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ DeviceProfile dp = ActivityContext.lookupContext(mContext).getDeviceProfile();
+ int cellWidth = DeviceProfile.calculateCellWidth(requestedWidth, dp.cellLayoutBorderSpacePx.x, dp.numShownHotseatIcons);
+ int iconSize = (int)(Math.round((dp.iconSizePx * 0.92f)));
+ int width = requestedWidth - (cellWidth - iconSize);
+ setMeasuredDimension(width, height);
+
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child != null) {
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ }
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (key.equals(Themes.KEY_THEMED_ICONS)) {
+ setIcons();
+ }
+ }
+
+ private void setIcons() {
+ if (Themes.isThemedIconEnabled(mContext)) {
+ mAssistantIcon.setImageResource(R.drawable.ic_mic_themed);
+ mGoogleIcon.setImageResource(R.drawable.ic_super_g_themed);
+ mLensIcon.setImageResource(R.drawable.ic_lens_themed);
+ } else {
+ mAssistantIcon.setImageResource(R.drawable.ic_mic_color);
+ mGoogleIcon.setImageResource(R.drawable.ic_super_g_color);
+ mLensIcon.setImageResource(R.drawable.ic_lens_color);
+ }
+ }
+
+ private void enableLensIcon() {
+ mLensIcon.setVisibility(View.VISIBLE);
+ mLensIcon.setOnClickListener(view -> {
+ Intent lensIntent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putString("caller_package", Utilities.GSA_PACKAGE);
+ bundle.putLong("start_activity_time_nanos", SystemClock.elapsedRealtimeNanos());
+ lensIntent.setComponent(new ComponentName(Utilities.GSA_PACKAGE, Utilities.LENS_ACTIVITY))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .setPackage(Utilities.GSA_PACKAGE)
+ .setData(Uri.parse(Utilities.LENS_URI))
+ .putExtra("lens_activity_params", bundle);
+ mContext.startActivity(lensIntent);
+ });
+ }
+
+}
diff --git a/src/com/android/launcher3/settings/SettingsAbout.java b/src/com/android/launcher3/settings/SettingsAbout.java
new file mode 100644
index 00000000000..efb08bf1779
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsAbout.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.core.view.WindowCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+public class SettingsAbout extends FragmentActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+ SharedPreferences.OnSharedPreferenceChangeListener{
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+ public static final String EXTRA_FRAGMENT = ":settings:fragment";
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
+ getString(R.string.about_fragment_name));
+ f.setArguments(args);
+ // Display the fragment as the main content.
+ fm.beginTransaction().replace(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
+ }
+ LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { }
+
+ private boolean startFragment(String fragment, Bundle args, String key) {
+ if (getSupportFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
+ if (f instanceof DialogFragment) {
+ f.setArguments(args);
+ ((DialogFragment) f).show(fm, key);
+ } else {
+ startActivity(new Intent(this, SettingsAbout.class)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragmentCompat preferenceFragment, Preference pref) {
+ return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startFragment(getString(R.string.about), args, pref.getKey());
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class AboutFragment extends PreferenceFragmentCompat {
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_about, rootKey);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ /**
+ switch (preference.getKey()) {
+ }
+ **/
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 52ce4e85a64..24f3468a718 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -16,25 +16,16 @@
package com.android.launcher3.settings;
-import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
-
import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
-import static com.android.launcher3.BuildConfig.IS_DEBUG_DEVICE;
-import static com.android.launcher3.BuildConfig.IS_STUDIO_BUILD;
-import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-
import android.app.Activity;
import android.content.Intent;
-import android.net.Uri;
+import android.content.SharedPreferences;
import android.os.Bundle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.view.MenuItem;
import android.view.View;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import androidx.core.view.WindowCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
@@ -48,24 +39,17 @@
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.launcher3.BuildConfig;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.Utilities;
import com.android.launcher3.R;
-import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.SettingsCache;
/**
- * Settings activity for Launcher. Currently implements the following setting: Allow rotation
+ * Settings activity for Launcher.
*/
public class SettingsActivity extends FragmentActivity
implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
- @VisibleForTesting
- static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
-
- private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
-
public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
// Intent extra to indicate the pref-key to highlighted when opening the settings activity
@@ -142,7 +126,7 @@ public boolean onPreferenceStartFragment(
public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
Bundle args = new Bundle();
args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
- return startPreference(getString(R.string.settings_fragment_name), args, pref.getKey());
+ return startPreference(getString(R.string.derp_app_name), args, pref.getKey());
}
@Override
@@ -157,27 +141,13 @@ public boolean onOptionsItemSelected(MenuItem item) {
/**
* This fragment shows the launcher preferences.
*/
- public static class LauncherSettingsFragment extends PreferenceFragmentCompat implements
- SettingsCache.OnChangeListener {
-
- protected boolean mDeveloperOptionsEnabled = false;
+ public static class LauncherSettingsFragment extends PreferenceFragmentCompat {
private boolean mRestartOnResume = false;
private String mHighLightKey;
private boolean mPreferenceHighlighted = false;
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- if (BuildConfig.IS_DEBUG_DEVICE) {
- Uri devUri = Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED);
- SettingsCache settingsCache = SettingsCache.INSTANCE.get(getContext());
- mDeveloperOptionsEnabled = settingsCache.getValue(devUri);
- settingsCache.register(devUri, this);
- }
- super.onCreate(savedInstanceState);
- }
-
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
final Bundle args = getArguments();
@@ -190,14 +160,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
setPreferencesFromResource(R.xml.launcher_preferences, rootKey);
- PreferenceScreen screen = getPreferenceScreen();
- for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
- Preference preference = screen.getPreference(i);
- if (!initPreference(preference)) {
- screen.removePreference(preference);
- }
- }
-
if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
getActivity().setTitle(getPreferenceScreen().getTitle());
}
@@ -227,36 +189,6 @@ public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
}
- /**
- * Initializes a preference. This is called for every preference. Returning false here
- * will remove that preference from the list.
- */
- protected boolean initPreference(Preference preference) {
- switch (preference.getKey()) {
- case NOTIFICATION_DOTS_PREFERENCE_KEY:
- return BuildConfig.NOTIFICATION_DOTS_ENABLED;
-
- case ALLOW_ROTATION_PREFERENCE_KEY:
- DisplayController.Info info =
- DisplayController.INSTANCE.get(getContext()).getInfo();
- if (info.isTablet(info.realBounds)) {
- // Launcher supports rotation by default. No need to show this setting.
- return false;
- }
- // Initialize the UI once
- preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
- return true;
-
- case DEVELOPER_OPTIONS_KEY:
- if (IS_STUDIO_BUILD) {
- preference.setOrder(0);
- }
- return mDeveloperOptionsEnabled;
- }
-
- return true;
- }
-
@Override
public void onResume() {
super.onResume();
@@ -274,21 +206,6 @@ public void onResume() {
}
}
- @Override
- public void onSettingsChanged(boolean isEnabled) {
- // Developer options changed, try recreate
- tryRecreateActivity();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (IS_DEBUG_DEVICE) {
- SettingsCache.INSTANCE.get(getContext())
- .unregister(Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED), this);
- }
- }
-
/**
* Tries to recreate the preference
*/
diff --git a/src/com/android/launcher3/settings/SettingsAppDrawer.java b/src/com/android/launcher3/settings/SettingsAppDrawer.java
new file mode 100644
index 00000000000..87347658369
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsAppDrawer.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.core.view.WindowCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class SettingsAppDrawer extends CollapsingToolbarBaseActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+ SharedPreferences.OnSharedPreferenceChangeListener{
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+ public static final String EXTRA_FRAGMENT = ":settings:fragment";
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
+ getString(R.string.app_drawer_settings_fragment_name));
+ f.setArguments(args);
+ // Display the fragment as the main content.
+ fm.beginTransaction().replace(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
+ }
+ LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ switch (key) {
+ case Utilities.KEY_DRAWER_SEARCH:
+ LauncherAppState.getInstance(this).setNeedsRestart();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private boolean startPreference(String fragment, Bundle args, String key) {
+ if (getSupportFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
+ if (f instanceof DialogFragment) {
+ f.setArguments(args);
+ ((DialogFragment) f).show(fm, key);
+ } else {
+ startActivity(new Intent(this, SettingsAppDrawer.class)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragmentCompat preferenceFragment, Preference pref) {
+ return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startPreference(getString(R.string.app_drawer_category_title), args, pref.getKey());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class AppDrawerSettingsFragment extends PreferenceFragmentCompat {
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_app_drawer_preferences, rootKey);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+
+ if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+ getActivity().setTitle(getPreferenceScreen().getTitle());
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ View listView = getListView();
+ final int bottomPadding = listView.getPaddingBottom();
+ listView.setOnApplyWindowInsetsListener((v, insets) -> {
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ bottomPadding + insets.getSystemWindowInsetBottom());
+ return insets.consumeSystemWindowInsets();
+ });
+
+ // Overriding Text Direction in the Androidx preference library to support RTL
+ view.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsHomescreen.java b/src/com/android/launcher3/settings/SettingsHomescreen.java
new file mode 100644
index 00000000000..44687d60bbe
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsHomescreen.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.core.view.WindowCompat;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+import java.util.Collections;
+import java.util.List;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceFragment.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragment.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class SettingsHomescreen extends CollapsingToolbarBaseActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+ SharedPreferences.OnSharedPreferenceChangeListener{
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ Fragment f = Fragment.instantiate(
+ this, getPreferenceFragment(), args);
+
+ getFragmentManager().beginTransaction()
+ .replace(R.id.content_frame, f)
+ .commit();
+ }
+ LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ /**
+ * Obtains the preference fragment to instantiate in this activity.
+ *
+ * @return the preference fragment class
+ * @throws IllegalArgumentException if the fragment is unknown to this activity
+ */
+ private String getPreferenceFragment() {
+ String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT_ARGS);
+ String defaultFragment = getString(R.string.home_screen_settings_fragment_name);
+
+ if (TextUtils.isEmpty(preferenceFragment)) {
+ return defaultFragment;
+ } else if (!preferenceFragment.equals(defaultFragment)) {
+ throw new IllegalArgumentException(
+ "Invalid fragment for this activity: " + preferenceFragment);
+ } else {
+ return preferenceFragment;
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ switch (key) {
+ case Utilities.KEY_DOCK_SEARCH:
+ case Utilities.KEY_SHOW_HOTSEAT_BG:
+ case Utilities.KEY_STATUS_BAR:
+ case Utilities.KEY_SHORT_PARALLAX:
+ case Utilities.KEY_SINGLE_PAGE_CENTER:
+ LauncherAppState.getInstance(this).setNeedsRestart();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private boolean startPreference(String fragment, Bundle args, String key) {
+ if (getFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ Fragment f = Fragment.instantiate(this, fragment, args);
+ if (f instanceof DialogFragment) {
+ ((DialogFragment) f).show(getFragmentManager(), key);
+ } else {
+ startActivity(new Intent(this, SettingsHomescreen.class)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragment preferenceFragment, Preference pref) {
+ return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startPreference(getString(R.string.home_category_title), args, pref.getKey());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class HomescreenSettingsFragment extends PreferenceFragment {
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ private static final String KEY_MINUS_ONE = "pref_enable_minus_one";
+
+ private Preference mShowGoogleAppPref;
+ private Preference mShowGoogleBarPref;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_home_screen_preferences, rootKey);
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+
+ mShowGoogleAppPref = screen.findPreference(KEY_MINUS_ONE);
+ mShowGoogleBarPref = screen.findPreference(Utilities.KEY_DOCK_SEARCH);
+ updateIsGoogleAppEnabled();
+
+ if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+ getActivity().setTitle(getPreferenceScreen().getTitle());
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ View listView = getListView();
+ final int bottomPadding = listView.getPaddingBottom();
+ listView.setOnApplyWindowInsetsListener((v, insets) -> {
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ bottomPadding + insets.getSystemWindowInsetBottom());
+ return insets.consumeSystemWindowInsets();
+ });
+
+ // Overriding Text Direction in the Androidx preference library to support RTL
+ view.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ private void updateIsGoogleAppEnabled() {
+ if (mShowGoogleAppPref != null) {
+ mShowGoogleAppPref.setEnabled(Utilities.isGSAEnabled(getContext()));
+ }
+ if (mShowGoogleBarPref != null) {
+ mShowGoogleBarPref.setEnabled(Utilities.isGSAEnabled(getContext()));
+ }
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ updateIsGoogleAppEnabled();
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsIcons.java b/src/com/android/launcher3/settings/SettingsIcons.java
new file mode 100644
index 00000000000..0fbe9858dfc
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsIcons.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.core.view.WindowCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.BuildConfig;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.model.WidgetsModel;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class SettingsIcons extends CollapsingToolbarBaseActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+ SharedPreferences.OnSharedPreferenceChangeListener{
+
+ private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
+ getString(R.string.icons_settings_fragment_name));
+ f.setArguments(args);
+ // Display the fragment as the main content.
+ fm.beginTransaction().replace(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
+ }
+ LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { }
+
+ private boolean startPreference(String fragment, Bundle args, String key) {
+ if (getSupportFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
+ if (f instanceof DialogFragment) {
+ f.setArguments(args);
+ ((DialogFragment) f).show(fm, key);
+ } else {
+ startActivity(new Intent(this, SettingsIcons.class)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragmentCompat preferenceFragment, Preference pref) {
+ return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startPreference(getString(R.string.icons_category_title), args, pref.getKey());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class IconsSettingsFragment extends PreferenceFragmentCompat implements
+ SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_icons_preferences, rootKey);
+
+ updatePreferences();
+
+ LauncherPrefs.getPrefs(getContext())
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onDestroyView () {
+ LauncherPrefs.getPrefs(getContext())
+ .unregisterOnSharedPreferenceChangeListener(this);
+ super.onDestroyView();
+ }
+
+ private void updatePreferences() {
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+
+ if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+ getActivity().setTitle(getPreferenceScreen().getTitle());
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ View listView = getListView();
+ final int bottomPadding = listView.getPaddingBottom();
+ listView.setOnApplyWindowInsetsListener((v, insets) -> {
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ bottomPadding + insets.getSystemWindowInsetBottom());
+ return insets.consumeSystemWindowInsets();
+ });
+
+ // Overriding Text Direction in the Androidx preference library to support RTL
+ view.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ switch (preference.getKey()) {
+ case NOTIFICATION_DOTS_PREFERENCE_KEY:
+ return BuildConfig.NOTIFICATION_DOTS_ENABLED;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+
+ public interface OnResumePreferenceCallback {
+ void onResume();
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsMisc.java b/src/com/android/launcher3/settings/SettingsMisc.java
new file mode 100644
index 00000000000..d29f2094426
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsMisc.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import static com.android.launcher3.BuildConfig.IS_STUDIO_BUILD;
+import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.view.WindowCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.lineage.trust.TrustAppsActivity;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.SettingsCache;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+import com.android.systemui.shared.system.BlurUtils;
+
+import android.util.Log;
+import java.util.Collections;
+import java.util.List;
+
+public class SettingsMisc extends CollapsingToolbarBaseActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+ SharedPreferences.OnSharedPreferenceChangeListener{
+
+ @VisibleForTesting
+ static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
+ private static final String SUGGESTIONS_KEY = "pref_suggestions";
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+ public static final String EXTRA_FRAGMENT = ":settings:fragment";
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ public static final String KEY_TRUST_APPS = "pref_trust_apps";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
+ getPreferenceFragment());
+ f.setArguments(args);
+ // Display the fragment as the main content.
+ fm.beginTransaction().replace(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
+ }
+ LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ /**
+ * Obtains the preference fragment to instantiate in this activity.
+ *
+ * @return the preference fragment class
+ * @throws IllegalArgumentException if the fragment is unknown to this activity
+ */
+ private String getPreferenceFragment() {
+ String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT);
+ String defaultFragment = getString(R.string.misc_settings_fragment_name);
+
+ if (TextUtils.isEmpty(preferenceFragment)) {
+ return defaultFragment;
+ } else if (!preferenceFragment.equals(defaultFragment)) {
+ throw new IllegalArgumentException(
+ "Invalid fragment for this activity: " + preferenceFragment);
+ } else {
+ return preferenceFragment;
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ switch (key) {
+ case Utilities.KEY_BLUR_DEPTH:
+ LauncherAppState.getInstance(this).setNeedsRestart();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private boolean startPreference(String fragment, Bundle args, String key) {
+ if (getSupportFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
+ if (f instanceof DialogFragment) {
+ f.setArguments(args);
+ ((DialogFragment) f).show(fm, key);
+ } else {
+ startActivity(new Intent(this, SettingsMisc.class)
+ .putExtra(EXTRA_FRAGMENT, fragment)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragmentCompat preferenceFragment, Preference pref) {
+ return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startPreference(getString(R.string.misc_category_title), args, pref.getKey());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class MiscSettingsFragment extends PreferenceFragmentCompat implements
+ SettingsCache.OnChangeListener {
+
+ protected boolean mDeveloperOptionsEnabled = false;
+
+ private boolean mRestartOnResume = false;
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ if (Utilities.IS_DEBUG_DEVICE) {
+ Uri devUri = Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED);
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(getContext());
+ mDeveloperOptionsEnabled = settingsCache.getValue(devUri);
+ settingsCache.register(devUri, this);
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_misc_preferences, rootKey);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+
+ if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+ getActivity().setTitle(getPreferenceScreen().getTitle());
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ View listView = getListView();
+ final int bottomPadding = listView.getPaddingBottom();
+ listView.setOnApplyWindowInsetsListener((v, insets) -> {
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ bottomPadding + insets.getSystemWindowInsetBottom());
+ return insets.consumeSystemWindowInsets();
+ });
+
+ // Overriding Text Direction in the Androidx preference library to support RTL
+ view.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ switch (preference.getKey()) {
+ case ALLOW_ROTATION_PREFERENCE_KEY:
+ DisplayController.Info info =
+ DisplayController.INSTANCE.get(getContext()).getInfo();
+ if (info.isTablet(info.realBounds)) {
+ // Launcher supports rotation by default. No need to show this setting.
+ return false;
+ }
+ if (!getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_supportAutoRotation)) {
+ // Not supported by the device, hide setting.
+ return false;
+ }
+ // Initialize the UI once
+ preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
+ return true;
+
+ case DEVELOPER_OPTIONS_KEY:
+ if (IS_STUDIO_BUILD) {
+ preference.setOrder(0);
+ }
+ return mDeveloperOptionsEnabled;
+
+ case KEY_TRUST_APPS:
+ preference.setOnPreferenceClickListener(p -> {
+ Utilities.showLockScreen(getActivity(),
+ getString(R.string.trust_apps_manager_name), () -> {
+ Intent intent = new Intent(getActivity(), TrustAppsActivity.class);
+ startActivity(intent);
+ });
+ return true;
+ });
+ return true;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+
+ if (mRestartOnResume) {
+ recreateActivityNow();
+ }
+ }
+
+ @Override
+ public void onSettingsChanged(boolean isEnabled) {
+ // Developer options changed, try recreate
+ tryRecreateActivity();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (Utilities.IS_DEBUG_DEVICE) {
+ SettingsCache.INSTANCE.get(getContext())
+ .unregister(Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED), this);
+ }
+ }
+
+ /**
+ * Tries to recreate the preference
+ */
+ protected void tryRecreateActivity() {
+ if (isResumed()) {
+ recreateActivityNow();
+ } else {
+ mRestartOnResume = true;
+ }
+ }
+
+ private void recreateActivityNow() {
+ Activity activity = getActivity();
+ if (activity != null) {
+ activity.recreate();
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsRecents.java b/src/com/android/launcher3/settings/SettingsRecents.java
new file mode 100644
index 00000000000..69925ffbd73
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsRecents.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.core.view.WindowCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class SettingsRecents extends CollapsingToolbarBaseActivity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
+
+ public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
+ // Intent extra to indicate the pref-key to highlighted when opening the settings activity
+ public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key";
+ // Intent extra to indicate the pref-key of the root screen when opening the settings activity
+ public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT;
+
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+
+ setActionBar(findViewById(R.id.action_bar));
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (savedInstanceState == null) {
+ Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+ if (args == null) {
+ args = new Bundle();
+ }
+
+ String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+ if (!TextUtils.isEmpty(highlight)) {
+ args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight);
+ }
+ String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY);
+ if (!TextUtils.isEmpty(root)) {
+ args.putString(EXTRA_FRAGMENT_ROOT_KEY, root);
+ }
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
+ getString(R.string.recents_settings_fragment_name));
+ f.setArguments(args);
+ // Display the fragment as the main content.
+ fm.beginTransaction().replace(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
+ }
+ }
+
+ private boolean startPreference(String fragment, Bundle args, String key) {
+ if (getSupportFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new preferences in that case.
+ return false;
+ }
+ final FragmentManager fm = getSupportFragmentManager();
+ final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
+ if (f instanceof DialogFragment) {
+ f.setArguments(args);
+ ((DialogFragment) f).show(fm, key);
+ } else {
+ startActivity(new Intent(this, SettingsRecents.class)
+ .putExtra(EXTRA_FRAGMENT_ARGS, args));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragmentCompat preferenceFragment, Preference pref) {
+ return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PREFERENCE_ROOT, pref.getKey());
+ return startPreference(getString(R.string.recents_category_title), args, pref.getKey());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class RecentsSettingsFragment extends PreferenceFragmentCompat {
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_recents_preferences, rootKey);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+ if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+ getActivity().setTitle(getPreferenceScreen().getTitle());
+ }
+
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ View listView = getListView();
+ final int bottomPadding = listView.getPaddingBottom();
+ listView.setOnApplyWindowInsetsListener((v, insets) -> {
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ bottomPadding + insets.getSystemWindowInsetBottom());
+ return insets.consumeSystemWindowInsets();
+ });
+
+ // Overriding Text Direction in the Androidx preference library to support RTL
+ view.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(
+ list, position, screen.findPreference(mHighLightKey))
+ : null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/preference/RestartPreference.java b/src/com/android/launcher3/settings/preference/RestartPreference.java
new file mode 100644
index 00000000000..4c2a0c5f929
--- /dev/null
+++ b/src/com/android/launcher3/settings/preference/RestartPreference.java
@@ -0,0 +1,38 @@
+package com.android.launcher3.settings.preference;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.widget.Toast;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import androidx.preference.Preference;
+
+public class RestartPreference extends Preference {
+
+
+ public RestartPreference(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public RestartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public RestartPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public RestartPreference(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onClick() {
+ super.onClick();
+ Toast.makeText(getContext(), R.string.restarting_launcher, Toast.LENGTH_SHORT).show();
+ Utilities.restart(getContext());
+ }
+}
diff --git a/src/com/android/launcher3/settings/preferences/ProperSeekBarPreference.java b/src/com/android/launcher3/settings/preferences/ProperSeekBarPreference.java
new file mode 100644
index 00000000000..8ada011745c
--- /dev/null
+++ b/src/com/android/launcher3/settings/preferences/ProperSeekBarPreference.java
@@ -0,0 +1,370 @@
+package com.android.launcher3.settings.preferences;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.*;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.launcher3.R;
+
+public class ProperSeekBarPreference extends Preference implements SeekBar.OnSeekBarChangeListener {
+ protected final String TAG = getClass().getName();
+ private static final String SETTINGS_NS = "http://schemas.android.com/apk/res/com.android.settings";
+ protected static final String ANDROIDNS = "http://schemas.android.com/apk/res/android";
+
+ protected int mInterval = 1;
+ protected boolean mShowSign = false;
+ protected String mUnits = "";
+ protected boolean mContinuousUpdates = false;
+
+ protected int mMinValue = 0;
+ protected int mMaxValue = 100;
+ protected boolean mDefaultValueExists = false;
+ protected int mDefaultValue;
+ protected boolean mDefaultValueTextExists = false;
+ protected String mDefaultValueText;
+
+ protected int mValue;
+
+ protected TextView mValueTextView;
+ protected ImageView mResetImageView;
+ protected ImageView mMinusImageView;
+ protected ImageView mPlusImageView;
+ protected SeekBar mSeekBar;
+
+ protected boolean mTrackingTouch = false;
+ protected int mTrackingValue;
+
+ public ProperSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProperSeekBarPreference);
+ try {
+ mShowSign = a.getBoolean(R.styleable.ProperSeekBarPreference_showSign, mShowSign);
+ String units = a.getString(R.styleable.ProperSeekBarPreference_units);
+ if (units != null)
+ mUnits = " " + units;
+ mContinuousUpdates = a.getBoolean(R.styleable.ProperSeekBarPreference_continuousUpdates, mContinuousUpdates);
+ String defaultValueText = a.getString(R.styleable.ProperSeekBarPreference_defaultValueText);
+ mDefaultValueTextExists = defaultValueText != null && !defaultValueText.isEmpty();
+ if (mDefaultValueTextExists) {
+ mDefaultValueText = defaultValueText;
+ }
+ } finally {
+ a.recycle();
+ }
+
+ try {
+ String newInterval = attrs.getAttributeValue(SETTINGS_NS, "interval");
+ if (newInterval != null)
+ mInterval = Integer.parseInt(newInterval);
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid interval value", e);
+ }
+ mMinValue = attrs.getAttributeIntValue(SETTINGS_NS, "min", mMinValue);
+ mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", mMaxValue);
+ if (mMaxValue < mMinValue)
+ mMaxValue = mMinValue;
+ String defaultValue = attrs.getAttributeValue(ANDROIDNS, "defaultValue");
+ mDefaultValueExists = defaultValue != null && !defaultValue.isEmpty();
+ if (mDefaultValueExists) {
+ mDefaultValue = getLimitedValue(Integer.parseInt(defaultValue));
+ mValue = mDefaultValue;
+ } else {
+ mValue = mMinValue;
+ }
+
+ mSeekBar = new SeekBar(context, attrs);
+ setLayoutResource(R.layout.preference_proper_seekbar);
+ }
+
+ public ProperSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ProperSeekBarPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context,
+ androidx.preference.R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
+ }
+
+ public ProperSeekBarPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void onDependencyChanged(Preference dependency, boolean disableDependent) {
+ super.onDependencyChanged(dependency, disableDependent);
+ this.setShouldDisableView(true);
+ if (mSeekBar != null)
+ mSeekBar.setEnabled(!disableDependent);
+ if (mResetImageView != null)
+ mResetImageView.setEnabled(!disableDependent);
+ if (mPlusImageView != null)
+ mPlusImageView.setEnabled(!disableDependent);
+ if (mMinusImageView != null)
+ mMinusImageView.setEnabled(!disableDependent);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ try
+ {
+ // move our seekbar to the new view we've been given
+ ViewParent oldContainer = mSeekBar.getParent();
+ ViewGroup newContainer = (ViewGroup) holder.findViewById(R.id.seekbar);
+ if (oldContainer != newContainer) {
+ // remove the seekbar from the old view
+ if (oldContainer != null) {
+ ((ViewGroup) oldContainer).removeView(mSeekBar);
+ }
+ // remove the existing seekbar (there may not be one) and add ours
+ newContainer.removeAllViews();
+ newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Error binding view: " + ex.toString());
+ }
+
+ mSeekBar.setMax(getSeekValue(mMaxValue));
+ mSeekBar.setProgress(getSeekValue(mValue));
+ mSeekBar.setEnabled(isEnabled());
+
+ mValueTextView = (TextView) holder.findViewById(R.id.value);
+ mResetImageView = (ImageView) holder.findViewById(R.id.reset);
+ mMinusImageView = (ImageView) holder.findViewById(R.id.minus);
+ mPlusImageView = (ImageView) holder.findViewById(R.id.plus);
+
+ updateValueViews();
+
+ mSeekBar.setOnSeekBarChangeListener(this);
+ mResetImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Toast.makeText(getContext(), getContext().getString(R.string.proper_seekbar_default_value_to_set, getTextValue(mDefaultValue)),
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ mResetImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mDefaultValue, true);
+ return true;
+ }
+ });
+ mMinusImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ setValue(mValue - mInterval, true);
+ }
+ });
+ mMinusImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue < mValue * 2 ? Math.floorDiv(mMaxValue + mMinValue, 2) : mMinValue, true);
+ return true;
+ }
+ });
+ mPlusImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ setValue(mValue + mInterval, true);
+ }
+ });
+ mPlusImageView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setValue(mMaxValue - mMinValue > mInterval * 2 && mMaxValue + mMinValue > mValue * 2 ? -1 * Math.floorDiv(-1 * (mMaxValue + mMinValue), 2) : mMaxValue, true);
+ return true;
+ }
+ });
+ }
+
+ protected int getLimitedValue(int v) {
+ return v < mMinValue ? mMinValue : (v > mMaxValue ? mMaxValue : v);
+ }
+
+ protected int getSeekValue(int v) {
+ return 0 - Math.floorDiv(mMinValue - v, mInterval);
+ }
+
+ protected String getTextValue(int v) {
+ if (mDefaultValueTextExists && mDefaultValueExists && v == mDefaultValue) {
+ return mDefaultValueText;
+ }
+ return (mShowSign && v > 0 ? "+" : "") + String.valueOf(v) + mUnits;
+ }
+
+ protected void updateValueViews() {
+ if (mValueTextView != null) {
+ if (!mTrackingTouch || mContinuousUpdates) {
+ if (mDefaultValueTextExists && mDefaultValueExists && mValue == mDefaultValue) {
+ mValueTextView.setText(mDefaultValueText + " (" +
+ getContext().getString(R.string.proper_seekbar_default_value) + ")");
+ } else {
+ mValueTextView.setText(getContext().getString(R.string.proper_seekbar_value, getTextValue(mValue)) +
+ (mDefaultValueExists && mValue == mDefaultValue ? " (" +
+ getContext().getString(R.string.proper_seekbar_default_value) + ")" : ""));
+ }
+ } else {
+ if (mDefaultValueTextExists && mDefaultValueExists && mTrackingValue == mDefaultValue) {
+ mValueTextView.setText("[" + mDefaultValueText + "]");
+ } else {
+ mValueTextView.setText(getContext().getString(R.string.proper_seekbar_value, getTextValue(mTrackingValue)));
+ }
+ }
+ }
+ if (mResetImageView != null) {
+ if (!mDefaultValueExists || mValue == mDefaultValue || mTrackingTouch)
+ mResetImageView.setVisibility(View.INVISIBLE);
+ else
+ mResetImageView.setVisibility(View.VISIBLE);
+ }
+ if (mMinusImageView != null) {
+ if (mValue == mMinValue || mTrackingTouch) {
+ mMinusImageView.setClickable(false);
+ mMinusImageView.setColorFilter(getColorAttrDefaultColor(getContext(), android.R.attr.textColorTertiary),
+ PorterDuff.Mode.SRC_IN);
+ } else {
+ mMinusImageView.setClickable(true);
+ mMinusImageView.clearColorFilter();
+ }
+ }
+ if (mPlusImageView != null) {
+ if (mValue == mMaxValue || mTrackingTouch) {
+ mPlusImageView.setClickable(false);
+ mPlusImageView.setColorFilter(getColorAttrDefaultColor(getContext(), android.R.attr.textColorTertiary),
+ PorterDuff.Mode.SRC_IN);
+ } else {
+ mPlusImageView.setClickable(true);
+ mPlusImageView.clearColorFilter();
+ }
+ }
+ }
+
+ @ColorInt
+ public static int getColorAttrDefaultColor(Context context, int attr) {
+ return getColorAttrDefaultColor(context, attr, 0);
+ }
+
+ /**
+ * Get color styled attribute {@code attr}, default to {@code defValue} if not found.
+ */
+ @ColorInt
+ public static int getColorAttrDefaultColor(Context context, int attr, @ColorInt int defValue) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ @ColorInt int colorAccent = ta.getColor(0, defValue);
+ ta.recycle();
+ return colorAccent;
+ }
+
+ protected void changeValue(int newValue) {
+ // for subclasses
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ int newValue = getLimitedValue(mMinValue + (progress * mInterval));
+ if (mTrackingTouch && !mContinuousUpdates) {
+ mTrackingValue = newValue;
+ updateValueViews();
+ } else if (mValue != newValue) {
+ // change rejected, revert to the previous value
+ if (!callChangeListener(newValue)) {
+ mSeekBar.setProgress(getSeekValue(mValue));
+ return;
+ }
+ // change accepted, store it
+ changeValue(newValue);
+ persistInt(newValue);
+
+ mValue = newValue;
+ updateValueViews();
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mTrackingValue = mValue;
+ mTrackingTouch = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mTrackingTouch = false;
+ if (!mContinuousUpdates)
+ onProgressChanged(mSeekBar, getSeekValue(mTrackingValue), false);
+ notifyChanged();
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ if (restoreValue)
+ mValue = getPersistedInt(mValue);
+ }
+
+ @Override
+ public void setDefaultValue(Object defaultValue) {
+ if (defaultValue instanceof Integer)
+ setDefaultValue((Integer) defaultValue, mSeekBar != null);
+ else
+ setDefaultValue(defaultValue == null ? (String) null : defaultValue.toString(), mSeekBar != null);
+ }
+
+ public void setDefaultValue(int newValue, boolean update) {
+ newValue = getLimitedValue(newValue);
+ if (!mDefaultValueExists || mDefaultValue != newValue) {
+ mDefaultValueExists = true;
+ mDefaultValue = newValue;
+ if (update)
+ updateValueViews();
+ }
+ }
+
+ public void setDefaultValue(String newValue, boolean update) {
+ if (mDefaultValueExists && (newValue == null || newValue.isEmpty())) {
+ mDefaultValueExists = false;
+ if (update)
+ updateValueViews();
+ } else if (newValue != null && !newValue.isEmpty()) {
+ setDefaultValue(Integer.parseInt(newValue), update);
+ }
+ }
+
+ public void setValue(int newValue) {
+ mValue = getLimitedValue(newValue);
+ if (mSeekBar != null) mSeekBar.setProgress(getSeekValue(mValue));
+ }
+
+ public void setValue(int newValue, boolean update) {
+ newValue = getLimitedValue(newValue);
+ if (mValue != newValue) {
+ if (update)
+ mSeekBar.setProgress(getSeekValue(newValue));
+ else
+ mValue = newValue;
+ }
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public void refresh(int newValue) {
+ // this will ...
+ setValue(newValue, mSeekBar != null);
+ }
+}
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index bf2fb30841f..f7530e9252b 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -25,6 +25,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
/**
@@ -62,7 +63,7 @@ protected float getDepthUnchecked(Context context) {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
return ColorUtils.setAlphaComponent(
- Themes.getAttrColor(launcher, R.attr.overviewScrimColor), 100);
+ Themes.getAttrColor(launcher, R.attr.overviewScrimColor), 70 * 255 / 100);
}
@Override
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f46dcd328cc..7c5926b0964 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.AlertDialog;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
@@ -50,6 +51,7 @@
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
@@ -439,7 +441,16 @@ private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launch
// Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
}
- launcher.startActivitySafely(v, intent, item);
+
+ TrustDatabaseHelper db = TrustDatabaseHelper.getInstance(launcher);
+ ComponentName cn = item.getTargetComponent();
+ boolean isProtected = cn != null && db.isPackageProtected(cn.getPackageName());
+
+ if (isProtected) {
+ launcher.startActivitySafelyAuth(v, intent, item);
+ } else {
+ launcher.startActivitySafely(v, intent, item);
+ }
}
/**
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 0ff10c26a58..153ab20a39d 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -27,8 +27,10 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_LONGPRESS;
+import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.PowerManager;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
@@ -40,6 +42,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
@@ -47,6 +50,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.TouchUtil;
+import com.android.launcher3.util.VibratorWrapper;
/**
* Helper class to handle touch on empty space in workspace and show options popup on long press
@@ -65,6 +69,8 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
private static final int STATE_PENDING_PARENT_INFORM = 2;
private static final int STATE_COMPLETED = 3;
+ private static final String SLEEP_GESTURE = "pref_sleep_gesture";
+
private final Rect mTempRect = new Rect();
private final Launcher mLauncher;
private final Workspace> mWorkspace;
@@ -73,15 +79,21 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
private int mLongPressState = STATE_CANCELLED;
+ private final PowerManager mPm;
+
private final GestureDetector mGestureDetector;
+ private final Context mContext;
+
public WorkspaceTouchListener(Launcher launcher, Workspace> workspace) {
mLauncher = launcher;
mWorkspace = workspace;
+ mContext = workspace.getContext();
// Use twice the touch slop as we are looking for long press which is more
// likely to cause movement.
mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop();
- mGestureDetector = new GestureDetector(workspace.getContext(), this);
+ mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mGestureDetector = new GestureDetector(mContext, this);
}
@Override
@@ -215,4 +227,13 @@ private void maybeShowMenu() {
}
}
}
+
+ @Override
+ public boolean onDoubleTap(MotionEvent event) {
+ if (LauncherPrefs.getPrefs(mContext).getBoolean(SLEEP_GESTURE, true)) {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(VibratorWrapper.EFFECT_CLICK);
+ mPm.goToSleep(event.getEventTime());
+ }
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
index 095518c8eab..fa622f352cb 100644
--- a/src/com/android/launcher3/util/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -34,6 +34,8 @@
import androidx.annotation.Nullable;
+import com.android.internal.derp.app.ParallelSpaceManager;
+
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -82,6 +84,9 @@ public Map queryAllUsers() {
UserManager um = mContext.getSystemService(UserManager.class);
Map users = new ArrayMap<>();
List usersActual = um.getUserProfiles();
+ List parallelUsers =
+ ParallelSpaceManager.getInstance().getParallelUserHandles();
+ usersActual.addAll(parallelUsers);
if (usersActual != null) {
for (UserHandle user : usersActual) {
long serial = um.getSerialNumberForUser(user);
@@ -89,7 +94,10 @@ public Map queryAllUsers() {
// Simple check to check if the provided user is work profile
// TODO: Migrate to a better platform API
NoopDrawable d = new NoopDrawable();
- boolean isWork = (d != mContext.getPackageManager().getUserBadgedIcon(d, user));
+ // Parallel users also have badge so don't consider it as work profile
+ boolean isParallelUser = parallelUsers.contains(user);
+ boolean isWork = !isParallelUser &&
+ (d != mContext.getPackageManager().getUserBadgedIcon(d, user));
UserIconInfo info = new UserIconInfo(
user,
isWork ? UserIconInfo.TYPE_WORK : UserIconInfo.TYPE_MAIN,
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 063313a8c68..20eafaae243 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -45,6 +45,10 @@ public static Predicate ofUser(UserHandle user) {
return info -> info != null && info.user.equals(user);
}
+ public static Predicate ofUsers(Collection users) {
+ return info -> info != null && users.contains(info.user);
+ }
+
public static Predicate ofComponents(
HashSet components, UserHandle user) {
return info -> info != null && info.user.equals(user)
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 8c5a76e665c..8c641fe463b 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -236,22 +236,34 @@ public static void startDetailsActivityForInfo(Context context, ItemInfo info,
}
}
- public static boolean isSystemApp(@NonNull final Context context,
- @NonNull final Intent intent) {
+ public static boolean isSystemApp(@NonNull final Context context, String pkgName) {
+ return isSystemApp(context, null, pkgName);
+ }
+
+ public static boolean isSystemApp(@NonNull final Context context, Intent intent) {
+ return isSystemApp(context, intent, null);
+ }
+
+ public static boolean isSystemApp(@NonNull final Context context, Intent intent, String pkgName) {
PackageManager pm = context.getPackageManager();
- ComponentName cn = intent.getComponent();
String packageName = null;
- if (cn == null) {
- ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if ((info != null) && (info.activityInfo != null)) {
- packageName = info.activityInfo.packageName;
+ // If the intent is not null, let's get the package name from the intent.
+ if (intent != null) {
+ ComponentName cn = intent.getComponent();
+ if (cn == null) {
+ ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if ((info != null) && (info.activityInfo != null)) {
+ packageName = info.activityInfo.packageName;
+ }
+ } else {
+ packageName = cn.getPackageName();
}
- } else {
- packageName = cn.getPackageName();
}
- if (packageName == null) {
- packageName = intent.getPackage();
+ // Otherwise we have the package name passed from the method.
+ else {
+ packageName = pkgName;
}
+ // Check if the provided package is a system app.
if (packageName != null) {
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 064bcd072fa..cbda741d86c 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -46,6 +46,10 @@ public void register(Context context, String... actions) {
context.registerReceiver(this, getFilter(actions));
}
+ public void register(Context context, int flags, String... actions) {
+ context.registerReceiver(this, getFilter(actions), flags);
+ }
+
/**
* Helper method to register multiple actions associated with a paction
*/
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index b97b8894c71..f217f132499 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -6,6 +6,7 @@
import android.app.WallpaperManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -16,20 +17,21 @@
import androidx.annotation.AnyThread;
import com.android.app.animation.Interpolators;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
/**
* Utility class to handle wallpaper scrolling along with workspace.
*/
-public class WallpaperOffsetInterpolator {
+public class WallpaperOffsetInterpolator implements
+ SharedPreferences.OnSharedPreferenceChangeListener {
private static final int[] sTempInt = new int[2];
private static final String TAG = "WPOffsetInterpolator";
private static final int ANIMATION_DURATION = 250;
- // Don't use all the wallpaper for parallax until you have at least this many pages
- private static final int MIN_PARALLAX_PAGE_SPAN = 4;
+ private static final String KEY_WALLPAPER_SCROLLING = "pref_allow_wallpaper_scrolling";
private final SimpleBroadcastReceiver mWallpaperChangeReceiver =
new SimpleBroadcastReceiver(i -> onWallpaperChanged());
@@ -44,10 +46,15 @@ public class WallpaperOffsetInterpolator {
private boolean mLockedToDefaultPage;
private int mNumScreens;
+ private boolean mAllowScrolling;
+
public WallpaperOffsetInterpolator(Workspace> workspace) {
mWorkspace = workspace;
mIsRtl = Utilities.isRtl(workspace.getResources());
mHandler = new OffsetHandler(workspace.getContext());
+ SharedPreferences prefs = LauncherPrefs.getPrefs(workspace.getContext());
+ mAllowScrolling = prefs.getBoolean(KEY_WALLPAPER_SCROLLING, true);
+ prefs.registerOnSharedPreferenceChangeListener(this);
}
/**
@@ -61,6 +68,17 @@ public boolean isLockedToDefaultPage() {
return mLockedToDefaultPage;
}
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (key.equals(KEY_WALLPAPER_SCROLLING)) {
+ mAllowScrolling = prefs.getBoolean(KEY_WALLPAPER_SCROLLING, true);
+ }
+ }
+
+ private int getMinimumScrollableScreensForParallax() {
+ return Utilities.isSinglePageCentered(mWorkspace.getContext()) ? 0 : 1;
+ }
+
/**
* Computes the wallpaper offset as an int ratio (out[0] / out[1])
*
@@ -71,15 +89,14 @@ private void wallpaperOffsetForScroll(int scroll, int numScrollableScreens, fina
// To match the default wallpaper behavior in the system, we default to either the left
// or right edge on initialization
- if (mLockedToDefaultPage || numScrollableScreens <= 1) {
- out[0] = mIsRtl ? 1 : 0;
+ out[0] = mIsRtl ? 1 : 0;
+ if (!mAllowScrolling || mLockedToDefaultPage || numScrollableScreens <= getMinimumScrollableScreensForParallax()) {
return;
}
- // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace
- // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN)
- int numScreensForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollableScreens :
- Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollableScreens);
+ // Distribute the wallpaper parallax over a minimum of getMinParallaxPageSpan() workspace
+ // screens, not including the custom screen, and empty screens (if > getMinParallaxPageSpan())
+ int numScreensForWallpaperParallax = getNumPagesForWallpaperParallax();
// Offset by the custom screen
@@ -103,7 +120,10 @@ private void wallpaperOffsetForScroll(int scroll, int numScrollableScreens, fina
int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex);
int scrollRange = rightPageScrollX - leftPageScrollX;
if (scrollRange <= 0) {
- out[0] = 0;
+ if (getMinimumScrollableScreensForParallax() == 0) {
+ out[0] = 1;
+ out[1] = 2;
+ }
return;
}
@@ -153,7 +173,7 @@ private int getNumScrollableScreensExcludingEmpty() {
*/
private int getNumPagesExcludingEmpty() {
int numOfPages = mWorkspace.getChildCount();
- if (numOfPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreens()) {
+ if (numOfPages >= getMinParallaxPageSpan() && mWorkspace.hasExtraEmptyScreens()) {
return numOfPages - mWorkspace.getPanelCount();
} else {
return numOfPages;
@@ -181,10 +201,15 @@ public int getNumPagesForWallpaperParallax() {
if (mWallpaperIsLiveWallpaper) {
return mNumScreens;
} else {
- return Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
+ return Math.max(getMinParallaxPageSpan(), mNumScreens);
}
}
+ private int getMinParallaxPageSpan() {
+ // Don't use all the wallpaper for parallax until you have at least this many pages
+ return Utilities.isShortParallax(mWorkspace.getContext()) ? 1 : 4;
+ }
+
@AnyThread
private void updateOffset() {
Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, getNumPagesForWallpaperParallax(), 0,
@@ -309,4 +334,4 @@ private void setOffsetSafely(IBinder token) {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 62eed5c5459..74cf5aa1ccf 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -207,7 +207,7 @@ public static ArrayList getOptions(Launcher launcher) {
R.drawable.ic_palette,
IGNORE,
OptionsPopupView::startWallpaperPicker));
- if (WIDGETS_ENABLED) {
+ if (WIDGETS_ENABLED && Utilities.isWorkspaceEditAllowed(launcher)) {
options.add(new OptionItem(launcher,
R.string.widget_button_text,
R.drawable.ic_widget,
@@ -222,7 +222,7 @@ public static ArrayList getOptions(Launcher launcher) {
OptionsPopupView::enterHomeGardening));
}
options.add(new OptionItem(launcher,
- R.string.settings_button_text,
+ R.string.settings_title,
R.drawable.ic_setting,
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
OptionsPopupView::startSettings));
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 44ab966472c..c42b6722179 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -41,6 +41,7 @@
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -111,6 +112,7 @@ public void setColorResources(@Nullable SparseIntArray colors) {
@Override
public boolean onLongClick(View view) {
+ if (!Utilities.isWorkspaceEditAllowed((Context) mActivityContext)) return true;
if (mIsScrollable) {
mActivityContext.getDragLayer().requestDisallowInterceptTouchEvent(false);
}
diff --git a/src_build_config/com/android/launcher3/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
index 6d4f56d655b..2630ec130f6 100644
--- a/src_build_config/com/android/launcher3/BuildConfig.java
+++ b/src_build_config/com/android/launcher3/BuildConfig.java
@@ -24,7 +24,7 @@ public final class BuildConfig {
* Flag to state if the QSB is on the first screen and placed on the top,
* this can be overwritten in other launchers with a different value, if needed.
*/
- public static final boolean QSB_ON_FIRST_SCREEN = true;
+ public static final boolean QSB_ON_FIRST_SCREEN = false;
/**
* Flag to state if the widget on the top of the first screen should be shown.
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
index 9865516c4ca..ebba29286f6 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,9 +20,12 @@
import android.content.Context;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -102,8 +105,8 @@ public float getVerticalProgress(Launcher launcher) {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- return launcher.getDeviceProfile().isTablet
+ return ColorUtils.setAlphaComponent(launcher.getDeviceProfile().isTablet
? launcher.getResources().getColor(R.color.widgets_picker_scrim)
- : Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+ : Themes.getAttrColor(launcher, R.attr.allAppsScrimColor), Utilities.getAllAppsOpacity(launcher) * 255 / 100);
}
}
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
index 7a228c42b82..f825154dfd2 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -19,9 +19,12 @@
import android.content.Context;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
/**
@@ -62,6 +65,7 @@ public static OverviewState newSplitSelectState(int id) {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+ return ColorUtils.setAlphaComponent(
+ Themes.getAttrColor(launcher, R.attr.overviewScrimColor), 70 * 255 / 100);
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index e51242f48af..7108e922462 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -132,11 +132,11 @@ android_test {
],
use_embedded_native_libs: false,
compile_multilib: "both",
- instrumentation_for: "Launcher3",
+ instrumentation_for: "DerpLauncher",
manifest: "AndroidManifest.xml",
platform_apis: true,
test_config: "Launcher3Tests.xml",
- data: [":Launcher3"],
+ data: [":DerpLauncher"],
test_suites: ["general-tests"],
}
@@ -219,7 +219,7 @@ android_robolectric_test {
"android.test.mock",
"truth",
],
- instrumentation_for: "Launcher3",
+ instrumentation_for: "DerpLauncher",
upstream: true,
strict_mode: false,
}