update to android 15 s1 release version

This commit is contained in:
MoXiang committed 2024-09-26 23:27:06 +08:00
1 parent 7d1211c099
commit 3b9c71a66e
1624 files changed
+105506 -41777

No files matched your search

Vendored
BIN
View File
Binary file not shown.
+112 -148
View File
@@ -17,48 +17,44 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
min_launcher3_sdk_version = "26"
min_launcher3_sdk_version = "30"
// Common source files used to build launcher (java and kotlin)
// All sources are split so they can be reused in many other libraries/apps in other folders
// Main Launcher source, excluding the build config
filegroup {
name: "launcher-src",
srcs: [ "src/**/*.java", "src/**/*.kt" ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
}
// Source code for quickstep build, on top of launcher-src
filegroup {
name: "launcher-quickstep-src",
srcs: [ "quickstep/src/**/*.java", "quickstep/src/**/*.kt" ],
srcs: [
"quickstep/src/**/*.java",
"quickstep/src/**/*.kt",
],
}
// Alternate source when quickstep is not included
filegroup {
name: "launcher-go-src",
srcs: [ "go/src/**/*.java", "go/src/**/*.kt" ],
name: "launcher-src_no_quickstep",
srcs: [
"src_no_quickstep/**/*.java",
"src_no_quickstep/**/*.kt",
],
}
// Default build config for Launcher3
filegroup {
name: "launcher-go-quickstep-src",
srcs: [ "go/quickstep/src/**/*.java", "go/quickstep/src/**/*.kt" ],
}
filegroup {
name: "launcher-src_shortcuts_overrides",
srcs: [ "src_shortcuts_overrides/**/*.java", "src_shortcuts_overrides/**/*.kt" ],
}
filegroup {
name: "launcher-src_ui_overrides",
srcs: [ "src_ui_overrides/**/*.java", "src_ui_overrides/**/*.kt" ],
}
filegroup {
name: "launcher-ext_tests",
srcs: [ "ext_tests/**/*.java", "ext_tests/**/*.kt" ],
}
filegroup {
name: "launcher-quickstep-ext_tests",
srcs: [ "quickstep/ext_tests/**/*.java", "quickstep/ext_tests/**/*.kt" ],
name: "launcher-build-config",
srcs: [
"src_build_config/**/*.java",
],
}
// Proguard files for Launcher3
@@ -79,13 +75,14 @@ android_library {
"androidx.test.uiautomator_uiautomator",
"androidx.preference_preference",
"SystemUISharedLib",
"animationlib",
"//frameworks/libs/systemui:animationlib",
"launcher-testing-shared",
],
srcs: [
"tests/tapl/**/*.java",
"tests/tapl/**/*.kt",
],
resource_dirs: [ ],
resource_dirs: [],
manifest: "tests/tapl/AndroidManifest.xml",
platform_apis: true,
}
@@ -99,7 +96,7 @@ java_library_static {
sdk_version: "current",
proto: {
type: "lite",
local_include_dirs:[
local_include_dirs: [
"protos",
"protos_overrides",
],
@@ -115,14 +112,14 @@ java_library_static {
sdk_version: "current",
proto: {
type: "lite",
local_include_dirs:[
local_include_dirs: [
"quickstep/protos_overrides",
],
},
static_libs: [
"libprotobuf-java-lite",
"launcher_log_protos_lite"
],
"libprotobuf-java-lite",
"launcher_log_protos_lite",
],
}
java_library {
@@ -139,11 +136,12 @@ java_library {
// Library with all the dependencies for building Launcher3
android_library {
name: "Launcher3ResLib",
srcs: [ ],
srcs: [],
resource_dirs: ["res"],
static_libs: [
"LauncherPluginLib",
"launcher_quickstep_log_protos_lite",
"android.os.flags-aconfig-java",
"androidx-constraintlayout_constraintlayout",
"androidx.recyclerview_recyclerview",
"androidx.dynamicanimation_dynamicanimation",
@@ -151,33 +149,28 @@ android_library {
"androidx.preference_preference",
"androidx.slice_slice-view",
"androidx.cardview_cardview",
"androidx.window_window",
"com.google.android.material_material",
"iconloader_base",
"view_capture"
],
manifest: "AndroidManifest-common.xml",
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
lint: {
baseline_filename: "lint-baseline-res-lib.xml",
},
}
//
// Build rule for Launcher3 dependencies lib.
//
android_library {
name: "Launcher3CommonDepsLib",
srcs: ["src_build_config/**/*.java"],
static_libs: [
"Launcher3ResLib",
"//frameworks/libs/systemui:iconloader_base",
"//frameworks/libs/systemui:view_capture",
"//frameworks/libs/systemui:animationlib",
"SystemUI-statsd",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-runtime-ktx",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
"com_android_launcher3_flags_lib",
"com_android_wm_shell_flags_lib",
"android.appwidget.flags-aconfig-java",
"com.android.window.flags.window-aconfig-java",
],
manifest: "AndroidManifest-common.xml",
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
manifest: "AndroidManifest-common.xml",
lint: {
baseline_filename: "lint-baseline-common-deps-lib.xml",
baseline_filename: "lint-baseline.xml",
},
}
@@ -188,17 +181,14 @@ android_app {
name: "Launcher3",
static_libs: [
"Launcher3CommonDepsLib",
"Launcher3ResLib",
],
srcs: [
":launcher-src",
":launcher-src_shortcuts_overrides",
":launcher-src_ui_overrides",
":launcher-ext_tests",
],
resource_dirs: [
"ext_tests/res",
":launcher-src_no_quickstep",
":launcher-build-config",
],
optimize: {
proguard_flags_files: ["proguard.flags"],
// Proguard is disable for testing. Derivarive prjects to keep proguard enabled
@@ -224,14 +214,14 @@ android_app {
"AndroidManifest-common.xml",
],
lint: {
baseline_filename: "lint-baseline-launcher3.xml",
baseline_filename: "lint-baseline.xml",
},
}
// Library with all the dependencies for building quickstep
android_library {
name: "QuickstepResLib",
srcs: [ ],
srcs: [],
resource_dirs: [
"quickstep/res",
],
@@ -242,25 +232,22 @@ android_library {
"Launcher3ResLib",
"lottie",
"SystemUISharedLib",
"SystemUI-statsd",
"animationlib",
"SettingsLibSettingsTheme",
],
manifest: "quickstep/AndroidManifest.xml",
min_sdk_version: "current",
}
// Library with all the dependencies for building Launcher Go
// Library with all the source code and dependencies for building Launcher Go
android_library {
name: "LauncherGoResLib",
name: "Launcher3GoLib",
srcs: [
":launcher-src",
":launcher-quickstep-src",
":launcher-go-src",
":launcher-go-quickstep-src",
"go/quickstep/src/**/*.java",
"go/quickstep/src/**/*.kt",
],
resource_dirs: [
"go/res",
"go/quickstep/res",
],
// Note the ordering here is important when it comes to resource
@@ -268,29 +255,27 @@ android_library {
// in QuickstepResLib to take precendece, so it should be the final
// dependency. See b/205278434 for how this can go wrong.
static_libs: [
"Launcher3CommonDepsLib",
"QuickstepResLib",
"androidx.room_room-runtime",
],
plugins: ["androidx.room_room-compiler-plugin"],
manifest: "quickstep/AndroidManifest-launcher.xml",
manifest: "quickstep/AndroidManifest.xml",
additional_manifests: [
"go/AndroidManifest.xml",
"AndroidManifest-common.xml",
],
min_sdk_version: "current",
lint: {
baseline_filename: "lint-baseline-go-res-lib.xml",
},
// TODO(b/319712088): re-enable use_resource_processor
use_resource_processor: false,
}
// Build rule for Quickstep library
// Library with all the source code and dependencies for building Quickstep
android_library {
name: "Launcher3QuickStepLib",
srcs: [
":launcher-src",
":launcher-quickstep-src",
":launcher-src_shortcuts_overrides",
":launcher-build-config",
],
resource_dirs: [],
libs: [
@@ -302,60 +287,13 @@ android_library {
// dependency. See b/208647810 for how this can go wrong.
static_libs: [
"SystemUI-statsd",
"SystemUISharedLib",
"Launcher3CommonDepsLib",
"QuickstepResLib",
"animationlib",
],
manifest: "quickstep/AndroidManifest.xml",
platform_apis: true,
min_sdk_version: "current",
lint: {
baseline_filename: "lint-baseline-launcher3.xml",
},
}
// Build rule for Launcher3 Go app for Android Go devices.
android_app {
name: "Launcher3Go",
static_libs: ["Launcher3CommonDepsLib"],
srcs: [
":launcher-src",
":launcher-go-src",
":launcher-src_ui_overrides",
],
resource_dirs: ["go/res"],
optimize: {
proguard_flags_files: ["proguard.flags"],
},
sdk_version: "current",
min_sdk_version: "current",
target_sdk_version: "current",
privileged: true,
system_ext_specific: true,
overrides: [
"Home",
"Launcher2",
"Launcher3",
"Launcher3QuickStep",
],
required: ["privapp_whitelist_com.android.launcher3"],
additional_manifests: [
"AndroidManifest.xml",
"AndroidManifest-common.xml",
],
manifest: "go/AndroidManifest.xml",
jacoco: {
include_filter: ["com.android.launcher3.*"],
}
// TODO(b/319712088): re-enable use_resource_processor
use_resource_processor: false,
}
// Build rule for Quickstep app.
@@ -390,32 +328,24 @@ android_app {
manifest: "quickstep/AndroidManifest.xml",
jacoco: {
include_filter: ["com.android.launcher3.*"],
}
},
}
// Build rule for Launcher3 Go app with quickstep for Android Go devices.
android_app {
name: "Launcher3QuickStepGo",
static_libs: [
"SystemUI-statsd",
"SystemUISharedLib",
"LauncherGoResLib",
],
// Build rule for Launcher3 Go app with quickstep for Android Go devices.
// Note that the following two rules are exactly same, and should
// eventually be merged into a single target
android_app {
name: "Launcher3Go",
static_libs: ["Launcher3GoLib"],
resource_dirs: [],
platform_apis: true,
min_sdk_version: "current",
target_sdk_version: "current",
srcs: [ ],
resource_dirs: [
"go/quickstep/res",
"go/res",
"quickstep/res",
],
optimize: {
proguard_flags_files: ["proguard.flags"],
enabled: true,
@@ -440,7 +370,41 @@ android_app {
manifest: "quickstep/AndroidManifest.xml",
jacoco: {
include_filter: ["com.android.launcher3.*"],
}
},
}
android_app {
name: "Launcher3QuickStepGo",
static_libs: ["Launcher3GoLib"],
resource_dirs: [],
platform_apis: true,
min_sdk_version: "current",
target_sdk_version: "current",
optimize: {
proguard_flags_files: ["proguard.flags"],
enabled: true,
},
privileged: true,
system_ext_specific: true,
overrides: [
"Home",
"Launcher2",
"Launcher3",
"Launcher3QuickStep",
],
required: ["privapp_whitelist_com.android.launcher3"],
additional_manifests: [
"go/AndroidManifest.xml",
"go/AndroidManifest-launcher.xml",
"AndroidManifest-common.xml",
],
manifest: "quickstep/AndroidManifest.xml",
jacoco: {
include_filter: ["com.android.launcher3.*"],
},
}
+15 -10
View File
@@ -52,21 +52,22 @@
name in the permissions. eq com.mypackage.permission.READ_SETTINGS
-->
<permission
android:name="${packageName}.permission.READ_SETTINGS"
android:name="${applicationId}.permission.READ_SETTINGS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_read_settings"
android:description="@string/permdesc_read_settings"/>
<permission
android:name="${packageName}.permission.WRITE_SETTINGS"
android:name="${applicationId}.permission.WRITE_SETTINGS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
<uses-permission android:name="${packageName}.permission.READ_SETTINGS" />
<uses-permission android:name="${packageName}.permission.WRITE_SETTINGS" />
<uses-permission android:name="${applicationId}.permission.READ_SETTINGS" />
<uses-permission android:name="${applicationId}.permission.WRITE_SETTINGS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:name="com.android.launcher3.LauncherApplication"
android:fullBackupOnly="true"
android:backupInForeground="true"
android:fullBackupContent="@xml/backupscheme"
@@ -125,10 +126,10 @@
-->
<provider
android:name="com.android.launcher3.LauncherProvider"
android:authorities="${packageName}.settings"
android:authorities="${applicationId}.settings"
android:exported="true"
android:writePermission="${packageName}.permission.WRITE_SETTINGS"
android:readPermission="${packageName}.permission.READ_SETTINGS" />
android:writePermission="${applicationId}.permission.WRITE_SETTINGS"
android:readPermission="${applicationId}.permission.READ_SETTINGS" />
<!--
The content provider for exposing various launcher grid options.
@@ -136,7 +137,7 @@
-->
<provider
android:name="com.android.launcher3.graphics.GridCustomizationsProvider"
android:authorities="${packageName}.grid_control"
android:authorities="${applicationId}.grid_control"
android:exported="true" />
<!--
@@ -156,7 +157,7 @@
<provider
android:name="com.android.launcher3.testing.TestInformationProvider"
android:authorities="${packageName}.TestInfo"
android:authorities="${applicationId}.TestInfo"
android:readPermission="android.permission.WRITE_SECURE_SETTINGS"
android:writePermission="android.permission.WRITE_SECURE_SETTINGS"
android:exported="true"
@@ -178,10 +179,14 @@
</intent-filter>
</activity>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
<!-- Disable eager initialization of Jetpack libraries. See bug 197780098. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
android:value="true" />
</application>
</manifest>
+1 -1
View File
@@ -20,7 +20,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/>
<uses-sdk android:targetSdkVersion="33" android:minSdkVersion="30"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src_build_config" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src_no_quickstep" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src_plugins" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="go" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="go" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="go" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="tests" />
</component>
</module>
+39 -2
View File
@@ -5,14 +5,51 @@
#
adamcohen@google.com
captaincole@google.com
hyunyoungs@google.com
sunnygoyal@google.com
twickham@google.com
vadimt@google.com
winsonc@google.com
jonmiranda@google.com
awickham@google.com
agvard@google.com
# Launcher workspace eng team
captaincole@google.com
sunnygoyal@google.com
charlander@google.com
fbaron@google.com
fengjial@google.com
fransebas@google.com
pinyaoting@google.com
andonian@google.com
sihua@google.com
# Multitasking eng team
tracyzhou@google.com
peanutbutter@google.com
jeremysim@google.com
atsjenk@google.com
brianji@google.com
# Overview eng team
alexchau@google.com
samcackett@google.com
silvajordan@google.com
uwaisashraf@google.com
# Physical Keyboard & Trackpad eng team
patmanning@google.com
helencheuk@google.com
# Widget Picker team
shamalip@google.com
zakcohen@google.com
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
per-file DeviceConfigWrapper.java, globs = set noparent
per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
# Predictive Back
per-file LauncherBackAnimationController.java = shanh@google.com, gallmann@google.com
+10 -1
View File
@@ -1,4 +1,13 @@
[Builtin Hooks]
ktfmt = true
[Builtin Hooks Options]
ktfmt = --kotlinlang-style
[Tool Paths]
ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT}
ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check ${PREUPLOAD_FILES}
flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
+2 -1
View File
@@ -18,7 +18,8 @@
* Launcher3-12(可以编译Quickstep版本)
* Launcher3-13-Qc(高通版本,可以编译Quickstep版本)
* Launcher3-13 (Launcher3-13.0.0_r83(7e9e70085176ea46bf773019a5054942d2fc3811))--Android-13
* master (Launcher3-android14-s2-release)--Android-14
* Launcher3-14-s2-release (android14-s2-release)
* main (android15-s1-release)--Android-15
## 源码参考
https://cs.android.com/android/platform/superproject/+/master:packages/apps/Launcher3/
+30
View File
@@ -0,0 +1,30 @@
// Copyright (C) 2023 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 {
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
aconfig_declarations {
name: "com_android_launcher3_flags",
package: "com.android.launcher3",
container: "system_ext",
srcs: ["**/*.aconfig"],
}
java_aconfig_library {
name: "com_android_launcher3_flags_lib",
aconfig_declarations: "com_android_launcher3_flags",
}
+318
View File
@@ -0,0 +1,318 @@
package: "com.android.launcher3"
container: "system_ext"
flag {
name: "enable_expanding_pause_work_button"
namespace: "launcher"
description: "Expand and collapse pause work button while scrolling."
bug: "270390779"
}
flag {
name: "enable_twoline_allapps"
namespace: "launcher"
description: "Enables two line label inside all apps."
bug: "270390937"
}
flag {
name: "enable_twoline_toggle"
namespace: "launcher"
description: "Enables visibility in home settings to see the toggle to turn on/off two lines in all apps."
bug: "316027081"
}
flag {
name: "enable_grid_only_overview"
namespace: "launcher"
description: "Enable a grid-only overview without a focused task."
bug: "257950105"
}
flag {
name: "enable_cursor_hover_states"
namespace: "launcher"
description: "Enables cursor hover states for certain elements."
bug: "243191650"
}
flag {
name: "enable_responsive_workspace"
namespace: "launcher"
description: "Enables new workspace grid calculations method."
bug: "302189128"
}
flag {
name: "enable_overview_icon_menu"
namespace: "launcher"
description: "Enable updated overview icon and menu within task."
bug: "257950105"
}
flag {
name: "enable_focus_outline"
namespace: "launcher"
description: "Enables focus states outline for launcher."
bug: "310953377"
}
flag {
name: "enable_taskbar_no_recreate"
namespace: "launcher"
description: "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext."
bug: "299193589"
}
flag {
name: "enable_home_transition_listener"
namespace: "launcher"
description: "Enables launcher to listen to all transitions that include home activity"
bug: "306053414"
}
flag {
name: "enable_taskbar_pinning"
namespace: "launcher"
description: "Enables taskbar pinning to allow user to switch between transient and persistent taskbar flavors."
bug: "296231746"
}
flag {
name: "enable_taskbar_customization"
namespace: "launcher"
description: "Enables taskbar customization framework."
bug: "347281365"
}
flag {
name: "enable_unfolded_two_pane_picker"
namespace: "launcher"
description: "Enables two pane widget picker for unfolded foldables"
bug: "313922374"
}
flag {
name: "enable_tablet_two_pane_picker_v2"
namespace: "launcher"
description: "Enables full width two pane widget picker for tablets in landscape and portrait"
bug: "315055849"
}
flag {
name: "enable_two_pane_launcher_settings"
namespace: "launcher"
description: "Enables two panel settings when on large enough displays"
bug: "204463748"
}
flag {
name: "enable_predictive_back_gesture"
namespace: "launcher"
description: "Enable predictive back gesture on Launcher (including all apps and widget picker)."
bug: "238475505"
}
flag {
name: "enable_shortcut_dont_suggest_app"
namespace: "launcher"
description: "Enables don't suggest app shortcut for suggested apps"
bug: "319250810"
}
flag {
name: "enable_support_for_archiving"
namespace: "launcher"
description: "Enables support for archived apps in Launcher3, such as empty progress bar etc."
bug: "210590852"
}
flag {
name: "enable_private_space_install_shortcut"
namespace: "launcher"
description: "Enables long-press shortcut to install a copy of an app to Private space"
bug: "316118005"
}
flag {
name: "enable_launcher_br_metrics_fixed"
namespace: "launcher"
description: "Enables logging of Launcher restore metrics to the Backup & Restore team"
bug: "307527314"
is_fixed_read_only: true
}
flag {
name: "enable_reboot_unlock_animation"
namespace: "launcher"
description: "Enables unlock animation after device reboot"
bug: "298231234"
}
flag {
name: "enable_workspace_inflation"
namespace: "launcher"
description: "Enables asnc inflation of workspace icons"
bug: "318539160"
}
flag {
name: "enable_unfold_state_animation"
namespace: "launcher"
description: "Tie unfold animation with state animation"
bug: "297057373"
}
flag {
name: "enable_generated_previews"
namespace: "launcher"
description: "Enables support for RemoteViews previews in the widget picker."
bug: "306546610"
}
flag {
name: "enable_categorized_widget_suggestions"
namespace: "launcher"
description: "Enables widget suggestions in widget picker to be displayed in categories"
bug: "318410881"
}
flag {
name: "force_monochrome_app_icons"
namespace: "launcher"
description: "Enable the ability to generate monochromatic icons, if it is not provided by the app"
bug: "270396209"
}
flag {
name: "enable_add_app_widget_via_config_activity_v2"
namespace: "launcher"
description: "When adding app widget through config activity, directly add it to workspace to reduce flicker"
bug: "284236964"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "use_activity_overlay"
namespace: "launcher"
description: "Use an activity for home screen overlay"
bug: "273828110"
}
flag {
name: "enable_grid_migration_fix"
namespace: "launcher"
description: "Keep items in place when migrating to a bigger grid"
bug: "325286145"
is_fixed_read_only: true
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_narrow_grid_restore"
namespace: "launcher"
description: "Using only the most recent workspace when restoring to avoid confusion."
is_fixed_read_only: true
bug: "325285743"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_scaling_reveal_home_animation"
namespace: "launcher"
description: "Enables the Home gesture animation"
bug: "308801666"
}
flag {
name: "enable_widget_tap_to_add"
namespace: "launcher"
description: "Enables an add button in the widget picker"
bug: "323886237"
}
flag {
name: "enable_refactor_task_thumbnail"
namespace: "launcher"
description: "Enables rewritten version of TaskThumbnailViews in Overview"
bug: "331753115"
}
flag {
name: "enable_handle_delayed_gesture_callbacks"
namespace: "launcher"
description: "Enables additional handling for delayed mid-gesture callbacks"
bug: "285636175"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_fallback_overview_in_window"
namespace: "launcher"
description: "Enables fallback recents opening inside of a window instead of an activity."
bug: "292269949"
}
flag {
name: "enable_smartspace_as_a_widget"
namespace: "launcher"
description: "Enables smartspace as a widget"
bug: "300140279"
}
flag {
name: "enable_smartspace_removal_toggle"
namespace: "launcher"
description: "Enables smartspace removal toggle"
bug: "303471576"
}
flag {
name: "enable_additional_home_animations"
namespace: "launcher"
description: "Enables custom home animations for non-running tasks"
bug: "237638627"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enabled_folders_in_all_apps"
namespace: "launcher"
description: "Enables folders in all apps"
bug: "341582436"
}
flag {
name: "enable_recents_in_taskbar"
namespace: "launcher"
description: "Replace hybrid hotseat app predictions with strictly Recent Apps"
bug: "315354060"
}
flag {
name: "enable_first_screen_broadcast_archiving_extras"
namespace: "launcher"
description: "adds Extras to first screen broadcast for archived apps"
bug: "322314760"
is_fixed_read_only: true
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "floating_search_bar"
namespace: "launcher"
description: "Search bar persists at the bottom of the screen across Launcher states"
bug: "346408388"
}
+55
View File
@@ -0,0 +1,55 @@
package: "com.android.launcher3"
container: "system_ext"
flag {
name: "enable_private_space"
namespace: "launcher_search"
description: "Enables all Launcher features associated with private space."
bug: "306187906"
}
flag {
name: "private_space_animation"
namespace: "launcher_search"
description: "This flag enables the animation of the Private Space container"
bug: "299294792"
}
flag {
name: "private_space_sys_apps_separation"
namespace: "launcher_search"
description: "This flag enables showing system apps separate in Private Space container."
bug: "308054233"
}
flag {
name: "private_space_app_installer_button"
namespace: "launcher_search"
description: "This flag enables addition of App Installer button in Private Space container."
bug: "308064949"
}
flag {
name: "private_space_restrict_accessibility_drag"
namespace: "launcher_search"
description: "This flag disables accessibility drag for Private Space Apps."
bug: "289223923"
}
flag {
name: "private_space_restrict_item_drag"
namespace: "launcher_search"
description: "This flag disables drag and drop for Private Space Items."
bug: "289223923"
}
flag {
name: "private_space_add_floating_mask_view"
namespace: "launcher_search"
description: "This flag enables the floating mask view as part of the Private Space animation. "
bug: "339850589"
metadata {
purpose: PURPOSE_BUGFIX
}
}
BIN
View File
Binary file not shown.
+88
View File
@@ -0,0 +1,88 @@
// Copyright (C) 2018 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 {
default_team: "trendy_team_motion",
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_library {
name: "animationlib",
manifest: "AndroidManifest.xml",
sdk_version: "system_current",
min_sdk_version: "26",
static_libs: [
"androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.annotation_annotation",
],
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
resource_dirs: [
"res",
],
kotlincflags: ["-Xjvm-default=all"],
}
android_library {
name: "animationlib-tests-base",
libs: [
"android.test.base",
"androidx.test.core",
],
static_libs: [
"animationlib",
"androidx.test.ext.junit",
"androidx.test.rules",
"testables",
],
}
android_app {
name: "TestAnimationLibApp",
platform_apis: true,
static_libs: [
"animationlib-tests-base",
],
}
android_robolectric_test {
enabled: true,
name: "animationlib_robo_tests",
srcs: [
"tests/src/**/*.kt",
"tests/robolectric/src/**/*.kt",
],
java_resource_dirs: ["tests/robolectric/config"],
instrumentation_for: "TestAnimationLibApp",
upstream: true,
strict_mode: false,
}
android_test {
name: "animationlib_tests",
manifest: "tests/AndroidManifest.xml",
static_libs: [
"animationlib-tests-base",
],
srcs: [
"tests/src/**/*.java",
"tests/src/**/*.kt",
],
kotlincflags: ["-Xjvm-default=all"],
test_suites: ["general-tests"],
}
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 The Android Open Source Project
<!--
Copyright (C) 2023 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.
@@ -13,8 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/taskbar_divider_background"/>
<corners android:radius="1dp" />
</shape>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.animation">
</manifest>
+7
View File
@@ -0,0 +1,7 @@
{
"presubmit": [
{
"name": "animationlib_tests"
}
]
}
+52
View File
@@ -0,0 +1,52 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion COMPILE_SDK
namespace = "com.android.app.animation"
testNamespace = "com.android.app.animation.tests"
defaultConfig {
minSdkVersion 26
targetSdkVersion 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
main {
java.srcDirs = ['src']
res.srcDirs = ['res']
manifest.srcFile 'AndroidManifest.xml'
}
androidTest {
java.srcDirs = ["tests/src", "tests/robolectric/src"]
manifest.srcFile 'tests/AndroidManifest.xml'
}
}
lintOptions {
abortOnError false
}
tasks.lint.enabled = false
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs = ["-Xjvm-default=all"]
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0"
implementation "androidx.core:core-animation:1.0.0"
implementation "androidx.core:core-ktx:1.13.1"
// androidTestImplementation libs.robolectric
androidTestImplementation "androidx.test.ext:junit:1.2.1"
androidTestImplementation "androidx.test:rules:1.5.0"
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.3"
android:controlY1="0"
android:controlX2="0.8"
android:controlY2="0.15"/>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.05"
android:controlY1="0.7"
android:controlX2="0.1"
android:controlY2="1"/>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~ Copyright (C) 2023 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.
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~ Copyright (C) 2023 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.
@@ -12,7 +12,7 @@
~ 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.
~ limitations under the License
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~ Copyright (C) 2023 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.
@@ -12,7 +12,7 @@
~ 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.
~ limitations under the License
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.2"
android:controlY1="0"
android:controlX2="0"
android:controlY2="1"/>
@@ -0,0 +1,414 @@
/*
* Copyright (C) 2023 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.app.animation;
import android.graphics.Path;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.PathInterpolator;
/**
* Utility class to receive interpolators from.
*
* Make sure that changes made to this class are also reflected in {@link InterpolatorsAndroidX}.
* Please consider using the androidx dependencies featuring better testability altogether.
*/
public class Interpolators {
/*
* ============================================================================================
* Emphasized interpolators.
* ============================================================================================
*/
/**
* The default emphasized interpolator. Used for hero / emphasized movement of content.
*/
public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
/**
* Complement to {@link #EMPHASIZED}. Used when animating hero movement in two dimensions to
* create a smooth, emphasized, curved movement.
* <br>
* Example usage: Animate y-movement with {@link #EMPHASIZED} and x-movement with this.
*/
public static final Interpolator EMPHASIZED_COMPLEMENT = createEmphasizedComplement();
/**
* The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
* is disappearing e.g. when moving off screen.
*/
public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
0.3f, 0f, 0.8f, 0.15f);
/**
* The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
* is appearing e.g. when coming from off screen
*/
public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
0.05f, 0.7f, 0.1f, 1f);
public static final Interpolator EXAGGERATED_EASE;
static {
Path exaggeratedEase = new Path();
exaggeratedEase.moveTo(0, 0);
exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f);
exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f);
EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
}
public static final Interpolator INSTANT = t -> 1;
/**
* All values of t map to 0 until t == 1. This is primarily useful for setting view visibility,
* which should only happen at the very end of the animation (when it's already hidden).
*/
public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
public static final Interpolator OVERSHOOT_0_75 = new OvershootInterpolator(0.75f);
public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
/*
* ============================================================================================
* Standard interpolators.
* ============================================================================================
*/
/**
* The standard interpolator that should be used on every normal animation
*/
public static final Interpolator STANDARD = new PathInterpolator(
0.2f, 0f, 0f, 1f);
/**
* The standard accelerating interpolator that should be used on every regular movement of
* content that is disappearing e.g. when moving off screen.
*/
public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
0.3f, 0f, 1f, 1f);
/**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
0f, 0f, 0f, 1f);
/*
* ============================================================================================
* Legacy
* ============================================================================================
*/
/**
* The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
*/
public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
/**
* The default legacy accelerating interpolator as defined in Material 1.
* Also known as FAST_OUT_LINEAR_IN.
*/
public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
/**
* The default legacy decelerating interpolator as defined in Material 1.
* Also known as LINEAR_OUT_SLOW_IN.
*/
public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
/**
* Linear interpolator. Often used if the interpolator is for different properties who need
* different interpolations.
*/
public static final Interpolator LINEAR = new LinearInterpolator();
/*
* ============================================================================================
* Custom interpolators
* ============================================================================================
*/
public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
/**
* Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
* goes from 1 to 0 instead of 0 to 1).
*/
public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
new PathInterpolator(0.8f, 0f, 0.6f, 1f);
public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_0_5 = new AccelerateInterpolator(0.5f);
public static final Interpolator ACCELERATE_0_75 = new AccelerateInterpolator(0.75f);
public static final Interpolator ACCELERATE_1_5 = new AccelerateInterpolator(1.5f);
public static final Interpolator ACCELERATE_2 = new AccelerateInterpolator(2);
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE = new DecelerateInterpolator();
public static final Interpolator DECELERATE_1_5 = new DecelerateInterpolator(1.5f);
public static final Interpolator DECELERATE_1_7 = new DecelerateInterpolator(1.7f);
public static final Interpolator DECELERATE_2 = new DecelerateInterpolator(2);
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
public static final Interpolator DECELERATE_3 = new DecelerateInterpolator(3f);
public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
1.1f);
public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
1);
public static final Interpolator BOUNCE = new BounceInterpolator();
/**
* For state transitions on the control panel that lives in GlobalActions.
*/
public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
1.0f);
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
*/
public static final Interpolator TOUCH_RESPONSE =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
/**
* Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
* goes from 1 to 0 instead of 0 to 1).
*/
public static final Interpolator TOUCH_RESPONSE_REVERSE =
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
public static final Interpolator TOUCH_RESPONSE_ACCEL_DEACCEL =
v -> ACCELERATE_DECELERATE.getInterpolation(TOUCH_RESPONSE.getInterpolation(v));
/**
* Inversion of ZOOM_OUT, compounded with an ease-out.
*/
public static final Interpolator ZOOM_IN = new Interpolator() {
@Override
public float getInterpolation(float v) {
return DECELERATE_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v));
}
};
public static final Interpolator ZOOM_OUT = new Interpolator() {
private static final float FOCAL_LENGTH = 0.35f;
@Override
public float getInterpolation(float v) {
return zInterpolate(v);
}
/**
* This interpolator emulates the rate at which the perceived scale of an object changes
* as its distance from a camera increases. When this interpolator is applied to a scale
* animation on a view, it evokes the sense that the object is shrinking due to moving away
* from the camera.
*/
private float zInterpolate(float input) {
return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) /
(1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f));
}
};
public static final Interpolator SCROLL = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t*t*t*t*t + 1;
}
};
public static final Interpolator SCROLL_CUBIC = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t*t*t + 1;
}
};
/**
* Use this interpolator for animating progress values coming from the back callback to get
* the predictive-back-typical decelerate motion.
*
* This interpolator is similar to {@link Interpolators#STANDARD_DECELERATE} but has a slight
* acceleration phase at the start.
*/
public static final Interpolator BACK_GESTURE = new PathInterpolator(0.1f, 0.1f, 0f, 1f);
private static final float FAST_FLING_PX_MS = 10;
/*
* ============================================================================================
* Functions / Utilities
* ============================================================================================
*/
public static Interpolator scrollInterpolatorForVelocity(float velocity) {
return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
}
/**
* Create an OvershootInterpolator with tension directly related to the velocity (in px/ms).
* @param velocity The start velocity of the animation we want to overshoot.
*/
public static Interpolator overshootInterpolatorForVelocity(float velocity) {
return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f));
}
/**
* Calculate the amount of overshoot using an exponential falloff function with desired
* properties, where the overshoot smoothly transitions at the 1.0f boundary into the
* overshoot, retaining its acceleration.
*
* @param progress a progress value going from 0 to 1
* @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
* value of the overall progress will be at 1.1.
* @param overshootStart the point in (0,1] where the result should reach 1
* @return the interpolated overshoot
*/
public static float getOvershootInterpolation(float progress, float overshootAmount,
float overshootStart) {
if (overshootAmount == 0.0f || overshootStart == 0.0f) {
throw new IllegalArgumentException("Invalid values for overshoot");
}
float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
return MathUtils.max(0.0f,
(float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
}
/**
* Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
* starts immediately here, instead of first having a section of non-overshooting
*
* @param progress a progress value going from 0 to 1
*/
public static float getOvershootInterpolation(float progress) {
return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
}
// Create the default emphasized interpolator
private static PathInterpolator createEmphasizedInterpolator() {
Path path = new Path();
// Doing the same as fast_out_extra_slow_in
path.moveTo(0f, 0f);
path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
return new PathInterpolator(path);
}
/**
* Creates a complement to {@link #createEmphasizedInterpolator()} for use when animating in
* two dimensions.
*/
private static PathInterpolator createEmphasizedComplement() {
Path path = new Path();
path.moveTo(0f, 0f);
path.cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f);
path.cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f);
return new PathInterpolator(path);
}
/**
* Returns a function that runs the given interpolator such that the entire progress is set
* between the given bounds. That is, we set the interpolation to 0 until lowerBound and reach
* 1 by upperBound.
*/
public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
if (upperBound < lowerBound) {
throw new IllegalArgumentException(
String.format("upperBound (%f) must be greater than lowerBound (%f)",
upperBound, lowerBound));
}
return t -> clampToProgress(interpolator, t, lowerBound, upperBound);
}
/**
* Returns the progress value's progress between the lower and upper bounds. That is, the
* progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
*
* Between lowerBound and upperBound, the progress value will be interpolated using the provided
* interpolator.
*/
public static float clampToProgress(
Interpolator interpolator, float progress, float lowerBound, float upperBound) {
if (upperBound < lowerBound) {
throw new IllegalArgumentException(
String.format("upperBound (%f) must be greater than lowerBound (%f)",
upperBound, lowerBound));
}
if (progress == lowerBound && progress == upperBound) {
return progress == 0f ? 0 : 1;
}
if (progress < lowerBound) {
return 0;
}
if (progress > upperBound) {
return 1;
}
return interpolator.getInterpolation((progress - lowerBound) / (upperBound - lowerBound));
}
/**
* Returns the progress value's progress between the lower and upper bounds. That is, the
* progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
*/
public static float clampToProgress(float progress, float lowerBound, float upperBound) {
return clampToProgress(Interpolators.LINEAR, progress, lowerBound, upperBound);
}
private static float mapRange(float value, float min, float max) {
return min + (value * (max - min));
}
/**
* Runs the given interpolator such that the interpolated value is mapped to the given range.
* This is useful, for example, if we only use this interpolator for part of the animation,
* such as to take over a user-controlled animation when they let go.
*/
public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
return t -> mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
}
/**
* Returns the reverse of the provided interpolator, following the formula: g(x) = 1 - f(1 - x).
* In practice, this means that if f is an interpolator used to model a value animating between
* m and n, g is the interpolator to use to obtain the specular behavior when animating from n
* to m.
*/
public static Interpolator reverse(Interpolator interpolator) {
return t -> 1 - interpolator.getInterpolation(1 - t);
}
}
@@ -0,0 +1,420 @@
/*
* Copyright (C) 2023 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.app.animation;
import android.graphics.Path;
import androidx.core.animation.AccelerateDecelerateInterpolator;
import androidx.core.animation.AccelerateInterpolator;
import androidx.core.animation.BounceInterpolator;
import androidx.core.animation.DecelerateInterpolator;
import androidx.core.animation.Interpolator;
import androidx.core.animation.LinearInterpolator;
import androidx.core.animation.OvershootInterpolator;
import androidx.core.animation.PathInterpolator;
/**
* Utility class to receive interpolators from. (androidx compatible version)
*
* This is the androidx compatible version of {@link Interpolators}. Make sure that changes made to
* this class are also reflected in {@link Interpolators}.
*
* Using the androidx versions of {@link androidx.core.animation.ValueAnimator} or
* {@link androidx.core.animation.ObjectAnimator} improves animation testability. This file provides
* the androidx compatible versions of the interpolators defined in {@link Interpolators}.
* AnimatorTestRule can be used in Tests to manipulate the animation under test (e.g. artificially
* advancing the time).
*/
public class InterpolatorsAndroidX {
/*
* ============================================================================================
* Emphasized interpolators.
* ============================================================================================
*/
/**
* The default emphasized interpolator. Used for hero / emphasized movement of content.
*/
public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
/**
* Complement to {@link #EMPHASIZED}. Used when animating hero movement in two dimensions to
* create a smooth, emphasized, curved movement.
* <br>
* Example usage: Animate y-movement with {@link #EMPHASIZED} and x-movement with this.
*/
public static final Interpolator EMPHASIZED_COMPLEMENT = createEmphasizedComplement();
/**
* The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
* is disappearing e.g. when moving off screen.
*/
public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
0.3f, 0f, 0.8f, 0.15f);
/**
* The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
* is appearing e.g. when coming from off screen
*/
public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
0.05f, 0.7f, 0.1f, 1f);
public static final Interpolator EXAGGERATED_EASE;
static {
Path exaggeratedEase = new Path();
exaggeratedEase.moveTo(0, 0);
exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f);
exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f);
EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
}
public static final Interpolator INSTANT = t -> 1;
/**
* All values of t map to 0 until t == 1. This is primarily useful for setting view visibility,
* which should only happen at the very end of the animation (when it's already hidden).
*/
public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
public static final Interpolator OVERSHOOT_0_75 = new OvershootInterpolator(0.75f);
public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
/*
* ============================================================================================
* Standard interpolators.
* ============================================================================================
*/
/**
* The standard interpolator that should be used on every normal animation
*/
public static final Interpolator STANDARD = new PathInterpolator(
0.2f, 0f, 0f, 1f);
/**
* The standard accelerating interpolator that should be used on every regular movement of
* content that is disappearing e.g. when moving off screen.
*/
public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
0.3f, 0f, 1f, 1f);
/**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
0f, 0f, 0f, 1f);
/*
* ============================================================================================
* Legacy
* ============================================================================================
*/
/**
* The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
*/
public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
/**
* The default legacy accelerating interpolator as defined in Material 1.
* Also known as FAST_OUT_LINEAR_IN.
*/
public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
/**
* The default legacy decelerating interpolator as defined in Material 1.
* Also known as LINEAR_OUT_SLOW_IN.
*/
public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
/**
* Linear interpolator. Often used if the interpolator is for different properties who need
* different interpolations.
*/
public static final Interpolator LINEAR = new LinearInterpolator();
/*
* ============================================================================================
* Custom interpolators
* ============================================================================================
*/
public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
/**
* Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
* goes from 1 to 0 instead of 0 to 1).
*/
public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
new PathInterpolator(0.8f, 0f, 0.6f, 1f);
public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_0_5 = new AccelerateInterpolator(0.5f);
public static final Interpolator ACCELERATE_0_75 = new AccelerateInterpolator(0.75f);
public static final Interpolator ACCELERATE_1_5 = new AccelerateInterpolator(1.5f);
public static final Interpolator ACCELERATE_2 = new AccelerateInterpolator(2);
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE = new DecelerateInterpolator();
public static final Interpolator DECELERATE_1_5 = new DecelerateInterpolator(1.5f);
public static final Interpolator DECELERATE_1_7 = new DecelerateInterpolator(1.7f);
public static final Interpolator DECELERATE_2 = new DecelerateInterpolator(2);
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
public static final Interpolator DECELERATE_3 = new DecelerateInterpolator(3f);
public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
1.1f);
public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
1);
public static final Interpolator BOUNCE = new BounceInterpolator();
/**
* For state transitions on the control panel that lives in GlobalActions.
*/
public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
1.0f);
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
*/
public static final Interpolator TOUCH_RESPONSE =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
/**
* Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
* goes from 1 to 0 instead of 0 to 1).
*/
public static final Interpolator TOUCH_RESPONSE_REVERSE =
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
public static final Interpolator TOUCH_RESPONSE_ACCEL_DEACCEL =
v -> ACCELERATE_DECELERATE.getInterpolation(TOUCH_RESPONSE.getInterpolation(v));
/**
* Inversion of ZOOM_OUT, compounded with an ease-out.
*/
public static final Interpolator ZOOM_IN = new Interpolator() {
@Override
public float getInterpolation(float v) {
return DECELERATE_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v));
}
};
public static final Interpolator ZOOM_OUT = new Interpolator() {
private static final float FOCAL_LENGTH = 0.35f;
@Override
public float getInterpolation(float v) {
return zInterpolate(v);
}
/**
* This interpolator emulates the rate at which the perceived scale of an object changes
* as its distance from a camera increases. When this interpolator is applied to a scale
* animation on a view, it evokes the sense that the object is shrinking due to moving away
* from the camera.
*/
private float zInterpolate(float input) {
return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) /
(1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f));
}
};
public static final Interpolator SCROLL = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t*t*t*t*t + 1;
}
};
public static final Interpolator SCROLL_CUBIC = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t*t*t + 1;
}
};
/**
* Use this interpolator for animating progress values coming from the back callback to get
* the predictive-back-typical decelerate motion.
*
* This interpolator is similar to {@link Interpolators#STANDARD_DECELERATE} but has a slight
* acceleration phase at the start.
*/
public static final Interpolator BACK_GESTURE = new PathInterpolator(0.1f, 0.1f, 0f, 1f);
private static final float FAST_FLING_PX_MS = 10;
/*
* ============================================================================================
* Functions / Utilities
* ============================================================================================
*/
public static Interpolator scrollInterpolatorForVelocity(float velocity) {
return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
}
/**
* Create an OvershootInterpolator with tension directly related to the velocity (in px/ms).
* @param velocity The start velocity of the animation we want to overshoot.
*/
public static Interpolator overshootInterpolatorForVelocity(float velocity) {
return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f));
}
/**
* Calculate the amount of overshoot using an exponential falloff function with desired
* properties, where the overshoot smoothly transitions at the 1.0f boundary into the
* overshoot, retaining its acceleration.
*
* @param progress a progress value going from 0 to 1
* @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
* value of the overall progress will be at 1.1.
* @param overshootStart the point in (0,1] where the result should reach 1
* @return the interpolated overshoot
*/
public static float getOvershootInterpolation(float progress, float overshootAmount,
float overshootStart) {
if (overshootAmount == 0.0f || overshootStart == 0.0f) {
throw new IllegalArgumentException("Invalid values for overshoot");
}
float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
return MathUtils.max(0.0f,
(float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
}
/**
* Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
* starts immediately here, instead of first having a section of non-overshooting
*
* @param progress a progress value going from 0 to 1
*/
public static float getOvershootInterpolation(float progress) {
return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
}
// Create the default emphasized interpolator
private static PathInterpolator createEmphasizedInterpolator() {
Path path = new Path();
// Doing the same as fast_out_extra_slow_in
path.moveTo(0f, 0f);
path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
return new PathInterpolator(path);
}
/**
* Creates a complement to {@link #createEmphasizedInterpolator()} for use when animating in
* two dimensions.
*/
private static PathInterpolator createEmphasizedComplement() {
Path path = new Path();
path.moveTo(0f, 0f);
path.cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f);
path.cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f);
return new PathInterpolator(path);
}
/**
* Returns a function that runs the given interpolator such that the entire progress is set
* between the given bounds. That is, we set the interpolation to 0 until lowerBound and reach
* 1 by upperBound.
*/
public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
if (upperBound < lowerBound) {
throw new IllegalArgumentException(
String.format("upperBound (%f) must be greater than lowerBound (%f)",
upperBound, lowerBound));
}
return t -> clampToProgress(interpolator, t, lowerBound, upperBound);
}
/**
* Returns the progress value's progress between the lower and upper bounds. That is, the
* progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
*
* Between lowerBound and upperBound, the progress value will be interpolated using the provided
* interpolator.
*/
public static float clampToProgress(
Interpolator interpolator, float progress, float lowerBound, float upperBound) {
if (upperBound < lowerBound) {
throw new IllegalArgumentException(
String.format("upperBound (%f) must be greater than lowerBound (%f)",
upperBound, lowerBound));
}
if (progress == lowerBound && progress == upperBound) {
return progress == 0f ? 0 : 1;
}
if (progress < lowerBound) {
return 0;
}
if (progress > upperBound) {
return 1;
}
return interpolator.getInterpolation((progress - lowerBound) / (upperBound - lowerBound));
}
/**
* Returns the progress value's progress between the lower and upper bounds. That is, the
* progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
*/
public static float clampToProgress(float progress, float lowerBound, float upperBound) {
return clampToProgress(LINEAR, progress, lowerBound, upperBound);
}
private static float mapRange(float value, float min, float max) {
return min + (value * (max - min));
}
/**
* Runs the given interpolator such that the interpolated value is mapped to the given range.
* This is useful, for example, if we only use this interpolator for part of the animation,
* such as to take over a user-controlled animation when they let go.
*/
public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
return t -> mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
}
/**
* Returns the reverse of the provided interpolator, following the formula: g(x) = 1 - f(1 - x).
* In practice, this means that if f is an interpolator used to model a value animating between
* m and n, g is the interpolator to use to obtain the specular behavior when animating from n
* to m.
*/
public static Interpolator reverse(Interpolator interpolator) {
return t -> 1 - interpolator.getInterpolation(1 - t);
}
}
@@ -13,10 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.uioverrides.flags;
/**
* Place holder class for developer options.
*/
public class DeveloperOptionsFragment {
package com.android.app.animation;
public final class MathUtils {
public static float log(float a) {
return (float) Math.log(a);
}
public static float max(float a, float b) {
return a > b ? a : b;
}
}
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2023 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.animation.tests">
<instrumentation
android:name="android.testing.TestableInstrumentation"
android:label="Tests for public Animation Lib"
android:targetPackage="com.android.app.animation.tests"/>
</manifest>
@@ -0,0 +1,2 @@
sdk=NEWEST_SDK
shadows=com.android.app.animation.robolectric.ShadowAnimationUtils2
@@ -0,0 +1,12 @@
package com.android.app.animation.robolectric
import android.view.animation.AnimationUtils
import org.robolectric.annotation.Implements
import org.robolectric.shadows.ShadowAnimationUtils
/**
* This shadow overwrites [ShadowAnimationUtils] and ensures that the real implementation of
* [AnimationUtils] is used in tests.
*/
@Implements(AnimationUtils::class)
class ShadowAnimationUtils2
@@ -0,0 +1,64 @@
package com.android.app.animation
import android.annotation.InterpolatorRes
import android.content.Context
import android.view.animation.AnimationUtils
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class InterpolatorResourcesTest {
private lateinit var context: Context
@Before
fun setup() {
context = InstrumentationRegistry.getInstrumentation().targetContext
}
@Test
fun testResourceInterpolatorsMatchCodeInterpolators() {
var progress = 0f
while (progress < +1f) {
assertEquals(
InterpolatorsAndroidX.EMPHASIZED.getInterpolation(progress),
loadInterpolator(R.interpolator.emphasized_interpolator).getInterpolation(progress)
)
assertEquals(
InterpolatorsAndroidX.EMPHASIZED_ACCELERATE.getInterpolation(progress),
loadInterpolator(R.interpolator.emphasized_accelerate_interpolator)
.getInterpolation(progress)
)
assertEquals(
InterpolatorsAndroidX.EMPHASIZED_DECELERATE.getInterpolation(progress),
loadInterpolator(R.interpolator.emphasized_decelerate_interpolator)
.getInterpolation(progress)
)
assertEquals(
InterpolatorsAndroidX.STANDARD.getInterpolation(progress),
loadInterpolator(R.interpolator.standard_interpolator).getInterpolation(progress)
)
assertEquals(
InterpolatorsAndroidX.STANDARD_ACCELERATE.getInterpolation(progress),
loadInterpolator(R.interpolator.standard_accelerate_interpolator)
.getInterpolation(progress)
)
assertEquals(
InterpolatorsAndroidX.STANDARD_DECELERATE.getInterpolation(progress),
loadInterpolator(R.interpolator.standard_decelerate_interpolator)
.getInterpolation(progress)
)
progress += 0.1f
}
}
private fun loadInterpolator(@InterpolatorRes resourceInt: Int) =
AnimationUtils.loadInterpolator(context, resourceInt)
}
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2023 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.app.animation
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import java.lang.reflect.Modifier
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
private const val ANDROIDX_ANIM_PACKAGE_NAME = "androidx.core.animation."
private const val ANDROID_ANIM_PACKAGE_NAME = "android.view.animation."
@SmallTest
@RunWith(AndroidJUnit4::class)
class InterpolatorsAndroidXTest {
@Test
fun testInterpolatorsAndInterpolatorsAndroidXPublicMethodsAreEqual() {
assertEquals(
Interpolators::class.java.getPublicMethods(),
InterpolatorsAndroidX::class.java.getPublicMethods()
)
}
@Test
fun testInterpolatorsAndInterpolatorsAndroidXPublicFieldsAreEqual() {
assertEquals(
Interpolators::class.java.getPublicFields(),
InterpolatorsAndroidX::class.java.getPublicFields()
)
}
private fun <T> Class<T>.getPublicMethods() =
declaredMethods
.filter { Modifier.isPublic(it.modifiers) }
.map { it.toString().replace(name, "")
.replace(ANDROIDX_ANIM_PACKAGE_NAME, "")
.replace(ANDROID_ANIM_PACKAGE_NAME, "") }
.toSet()
private fun <T> Class<T>.getPublicFields() =
fields.filter { Modifier.isPublic(it.modifiers) }.map { it.name }.toSet()
}
+9 -2
View File
@@ -28,13 +28,16 @@ android {
defaultConfig {
minSdkVersion 26
targetSdkVersion 34
targetSdkVersion 35
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_STUDIO_BUILD", "false"
buildConfigField "boolean", "QSB_ON_FIRST_SCREEN", "true"
buildConfigField "boolean", "IS_DEBUG_DEVICE", "false"
buildConfigField "boolean", "WIDGET_ON_FIRST_SCREEN", "true"
buildConfigField "boolean", "WIDGETS_ENABLED", "true"
buildConfigField "boolean", "NOTIFICATION_DOTS_ENABLED", "true"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -88,7 +91,7 @@ android {
sourceSets {
main {
res.srcDirs = ['res']
java.srcDirs = ['src', 'src_plugins', 'tests/shared']
java.srcDirs = ['src', 'src_plugins', 'tests/shared', 'system_ext', 'tests/multivalentTests/shared', 'systemui_core/shared']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
srcDirs = ['protos/', 'protos_overrides/']
@@ -110,6 +113,7 @@ android {
}
aospWithoutQuickstep {
java.srcDirs = ['src_no_quickstep']
manifest.srcFile "AndroidManifest.xml"
}
@@ -171,6 +175,7 @@ dependencies {
implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
implementation project(':IconLoader')
implementation project(':systemUIPluginCore')
implementation project(':animationlib')
// implementation project(':UiTestsLibLauncher')
// withQuickstepImplementation project(':SharedLibWrapper')
implementation 'com.google.protobuf:protobuf-javalite:4.26.1'
@@ -195,6 +200,8 @@ dependencies {
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.slice:slice-builders:1.0.0'
implementation "androidx.window:window:1.3.0"
}
def protocVersion = '4.26.1'//'3.8.0'
-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="test_information_handler_class" translatable="false">com.android.launcher3.testing.DebugTestInformationHandler</string>
</resources>
@@ -1,241 +0,0 @@
/*
* Copyright (C) 2020 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.testing;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.system.Os;
import android.util.Log;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.testing.shared.TestProtocol;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Class to handle requests from tests, including debug ones.
*/
public class DebugTestInformationHandler extends TestInformationHandler {
private static Collection<String> sEvents;
private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
private static final Map<Activity, Boolean> sActivities =
Collections.synchronizedMap(new WeakHashMap<>());
private static int sActivitiesCreatedCount = 0;
public DebugTestInformationHandler(Context context) {
init(context);
if (sActivityLifecycleCallbacks == null) {
sActivityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
sActivities.put(activity, true);
++sActivitiesCreatedCount;
Log.d(TestProtocol.FLAKY_ACTIVITY_COUNT, "onActivityCreated", new Exception());
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
};
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
}
}
private static void runGcAndFinalizersSync() {
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
final CountDownLatch fence = new CountDownLatch(1);
createFinalizationObserver(fence);
try {
do {
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
} while (!fence.await(100, TimeUnit.MILLISECONDS));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
// Create the observer in the scope of a method to minimize the chance that
// it remains live in a DEX/machine register at the point of the fence guard.
// This must be kept to avoid R8 inlining it.
@Keep
private static void createFinalizationObserver(CountDownLatch fence) {
new Object() {
@Override
protected void finalize() throws Throwable {
try {
fence.countDown();
} finally {
super.finalize();
}
}
};
}
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
final Bundle response = new Bundle();
switch (method) {
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
return getLauncherUIProperty(Bundle::putInt,
l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
}
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
TestProtocol.sDebugTracing = true;
return response;
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
TestProtocol.sDebugTracing = false;
return response;
case TestProtocol.REQUEST_PID: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
return response;
}
case TestProtocol.REQUEST_FORCE_GC: {
runGcAndFinalizersSync();
return response;
}
case TestProtocol.REQUEST_START_EVENT_LOGGING: {
sEvents = new ArrayList<>();
TestLogging.setEventConsumer(
(sequence, event) -> {
final Collection<String> events = sEvents;
if (events != null) {
synchronized (events) {
events.add(sequence + '/' + event);
}
}
});
return response;
}
case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
TestLogging.setEventConsumer(null);
sEvents = null;
return response;
}
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
if (sEvents == null) {
// sEvents can be null if Launcher died and restarted after
// REQUEST_START_EVENT_LOGGING.
return response;
}
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
}
return response;
}
case TestProtocol.REQUEST_CLEAR_DATA: {
final long identity = Binder.clearCallingIdentity();
try {
LauncherSettings.Settings.call(mContext.getContentResolver(),
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
MAIN_EXECUTOR.submit(() ->
LauncherAppState.getInstance(mContext).getModel().forceReload());
return response;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
ShortcutAndWidgetContainer hotseatIconsContainer =
l.getHotseat().getShortcutsAndWidgets();
ArrayList<String> hotseatIconNames = new ArrayList<>();
for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
// Use unchecked cast to catch changes in hotseat layout
BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
hotseatIconNames.add((String) icon.getText());
}
return hotseatIconNames;
});
}
case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
return response;
}
case TestProtocol.REQUEST_GET_ACTIVITIES: {
response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD,
sActivities.keySet().stream().map(
a -> a.getClass().getSimpleName() + " ("
+ (a.isDestroyed() ? "destroyed" : "current") + ")")
.toArray(String[]::new));
return response;
}
case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
default:
return super.call(method, arg, extras);
}
}
}
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/quickstep/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="multivalentTestsForDevice" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="tests1" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="multivalentTests1" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="multivalentTestsForDevice" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="tests1" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="multivalentTests1" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="quickstep" />
<orderEntry type="module" module-name="multivalentTestsForDevice" />
<orderEntry type="module" module-name="multivalentTests1" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="tests1" />
</component>
</module>
@@ -120,6 +120,19 @@
android:layout_height="1dp"
android:layout_weight="1"
android:visibility="gone" />
</LinearLayout>
<!-- Unused. Included only for compatibility with parent class. -->
<Button
android:id="@+id/action_save_app_pair"
style="@style/GoOverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:drawableStart="@drawable/ic_save_app_pair_up_down"
android:text="@string/action_save_app_pair"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
</com.android.quickstep.views.GoOverviewActionsView>
+2 -2
View File
@@ -14,7 +14,7 @@
<string name="assistant_not_selected_text" msgid="3244613673884359276">"Για να ακούσετε ή να μεταφράσετε κείμενο στην οθόνη σας, επιλέξτε μια εφαρμογή ψηφιακού βοηθού στις Ρυθμίσεις."</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"Αλλάξτε τον βοηθό σας για να χρησιμοποιήσετε αυτήν τη λειτουργία"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"Για να ακούσετε ή να μεταφράσετε κείμενο στην οθόνη σας, αλλάξτε την εφαρμογή ψηφιακού βοηθού στις Ρυθμίσεις."</string>
<string name="tooltip_listen" msgid="7634466447860989102">"Πατήστε εδώ για να ακούσετε το κείμενο σε αυτήν την οθόνη"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"Πατήστε εδώ για να μεταφράσετε το κείμενο σε αυτήν την οθόνη"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"Πατήστε εδώ για να ακούσετε το κείμενο σε αυτή την οθόνη"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"Πατήστε εδώ για να μεταφράσετε το κείμενο σε αυτή την οθόνη"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"Δεν είναι δυνατή η κοινή χρήση της εφαρμογής"</string>
</resources>
+2 -2
View File
@@ -14,7 +14,7 @@
<string name="assistant_not_selected_text" msgid="3244613673884359276">"برای گوش کردن به نوشتار در صفحه‌نمایش‌تان یا ترجمه کردن آن، یکی از برنامه‌های دستیار دیجیتالی را در «تنظیمات» انتخاب کنید"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"برای استفاده از این ویژگی، دستیارتان را تغییر دهید"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"برای گوش کردن به نوشتار در صفحه‌نمایش‌تان یا ترجمه کردن آن، برنامه دستیار دیجیتالی‌تان را در «تنظیمات» تغییر دهید"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"برای گوش کردن به نوشتار در این صفحه، اینجا ضربه بزنید"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"برای ترجمه نوشتار در این صفحه، اینجا ضربه بزنید"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"برای گوش کردن به نوشتار در این صفحه، اینجا تک‌ضرب بزنید"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"برای ترجمه نوشتار در این صفحه، اینجا تک‌ضرب بزنید"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"نمی‌توان این برنامه را هم‌رسانی کرد"</string>
</resources>
+5 -5
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_share_drop_target_label" msgid="5804774105974539508">"Partager application"</string>
<string name="app_share_drop_target_label" msgid="5804774105974539508">"Partager appli"</string>
<string name="action_listen" msgid="2370304050784689486">"Écouter"</string>
<string name="action_translate" msgid="8028378961867277746">"Traduire"</string>
<string name="action_search" msgid="6269564710943755464">"Lentille"</string>
@@ -9,12 +9,12 @@
<string name="dialog_cancel" msgid="6464336969134856366">"ANNULER"</string>
<string name="dialog_settings" msgid="6564397136021186148">"PARAMÈTRES"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Traduire ou écouter le texte à l\'écran"</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Des renseignements comme du texte sur votre écran, des adresses Web et des captures d\'écran peuvent être partagés avec Google.\n\nPour modifier les renseignements que vous partagez, accédez à "<b>"Paramètres &gt; Applications &gt; Applications par défaut &gt; Application d\'assistant numérique"</b>"."</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Des renseignements comme du texte sur votre écran, des adresses Web et des captures d\'écran peuvent être partagés avec Google.\n\nPour modifier les renseignements que vous partagez, accédez à "<b>"Paramètres &gt; Applis &gt; Applis par défaut &gt; Appli d\'assistant numérique"</b>"."</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"Choisir un assistant pour utiliser cette fonctionnalité"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"Pour écouter ou traduire le texte affiché sur votre écran, choisissez l\'application d\'un assistant numérique dans les paramètres"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"Pour écouter ou traduire le texte affiché sur votre écran, choisissez l\'appli d\'un assistant numérique dans les paramètres"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"Modifier votre assistant pour utiliser cette fonctionnalité"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"Pour écouter ou traduire le texte affiché sur votre écran, modifiez l\'application de votre assistant numérique dans les paramètres"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"Pour écouter ou traduire le texte affiché sur votre écran, modifiez l\'appli de votre assistant numérique dans les paramètres"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"Touchez ce bouton pour écouter le texte affiché sur cet écran"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"Touchez ce bouton pour traduire le texte affiché sur cet écran"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"Cette application ne peut pas être partagée"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"Cette appli ne peut pas être partagée"</string>
</resources>
+3 -3
View File
@@ -9,11 +9,11 @@
<string name="dialog_cancel" msgid="6464336969134856366">"မလုပ်တော့"</string>
<string name="dialog_settings" msgid="6564397136021186148">"ဆက်တင်များ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ပါ (သို့) နားထောင်ပါ"</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"သင့်ဖန်သားပြင်ပေါ်ရှိ စာသား၊ ဝဘ်လိပ်စာနှင့် ဖန်သားပြင်ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များကို Google နှင့် မျှဝေနိုင်သည်။\n\nသင်မျှဝေသည့် အချက်အလက်များကို ပြောင်းရန် "<b>"ဆက်တင်များ &gt; အက်ပ်များ &gt; မူရင်းအက်ပ်များ &gt; ဒစ်ဂျစ်တယ် Assistant အက်ပ်"</b>" သို့ သွားပါ။"</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"သင့်ဖန်သားပြင်ပေါ်ရှိ စာသား၊ ဝဘ်လိပ်စာနှင့် ဖန်သားပြင်ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များကို Google နှင့် မျှဝေနိုင်သည်။\n\nသင်မျှဝေသည့် အချက်အလက်များကို ပြောင်းရန် "<b>"ဆက်တင်များ &gt; အက်ပ်များ &gt; မူရင်းအက်ပ်များ &gt; ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်"</b>" သို့ သွားပါ။"</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"ဤဝန်ဆောင်မှုကို အသုံးပြုရန် assistant ရွေးပါ"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ် assistant အက်ပ် ရွေးပါ"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ် ရွေးပါ"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"ဤဝန်ဆောင်မှုကို သုံးရန် assistant ကို ပြောင်းပါ"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ် assistant အက်ပ်ကို ပြောင်းပါ"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်ကို ပြောင်းပါ"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"ဤဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် ဤနေရာကို တို့ပါ"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"ဤဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ဆိုရန် ဤနေရာကို တို့ပါ"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"ဤအက်ပ်ကို မျှဝေ၍မရပါ"</string>
+1 -1
View File
@@ -9,7 +9,7 @@
<string name="dialog_cancel" msgid="6464336969134856366">"AVBRYT"</string>
<string name="dialog_settings" msgid="6564397136021186148">"INNSTILLINGER"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Oversett eller lytt til tekst på skjermen"</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Informasjon som tekst på skjermen, nettadresser og skjermdumper kan deles med Google.\n\nFor å endre hvilken informasjon du deler, gå til "<b>"Innstillinger &gt; Apper &gt; Standardapper &gt; Digital assistent-app"</b>"."</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Informasjon som tekst på skjermen, nettadresser og skjermbilder kan deles med Google.\n\nFor å endre hvilken informasjon du deler, gå til "<b>"Innstillinger &gt; Apper &gt; Standardapper &gt; Digital assistent-app"</b>"."</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"Velg en assistent for å bruke denne funksjonen"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"For å høre eller oversette tekst på skjermen, velg en digital assistent-app i innstillingene"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"Endre assistenten for å bruke denne funksjonen"</string>
+1 -1
View File
@@ -8,7 +8,7 @@
<string name="dialog_acknowledge" msgid="2804025517675853172">"बुझेँ"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"रद्द गर्नुहोस्"</string>
<string name="dialog_settings" msgid="6564397136021186148">"सेटिङ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"स्क्रिनमा देखिने पाठ अनुवाद गरियोस् वा पढेर सुनाइयोस्"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"स्क्रिनमा देखिने पाठ अनुवाद गर्नुहोस् वा पढेर सुनाउनुहोस्"</string>
<string name="niu_actions_confirmation_text" msgid="2105271481950866089">"तपाईंको स्क्रिनमा देखिने पाठ, वेब ठेगाना र स्क्रिनसटलगायतका जानकारी Google सँग सेयर गर्न सकिन्छ।\n\nकुन कुन जानकारी सेयर गर्न दिने भन्ने सेटिङ बदल्न "<b>"सेटिङ &gt; एप &gt; डिफल्ट एप &gt; डिजिटल सहायक एप"</b>" मा जानुहोस्।"</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"तपाईं यो सुविधा चलाउन चाहनुहुन्छ भने कुनै सहायक छनौट गर्नुहोस्"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"तपाईं आफ्नो स्क्रिनमा देखिने पाठ सुन्न वा अनुवाद गर्न चाहनुहुन्छ भने सेटिङमा गई कुनै डिजिटल सहायक एप छनौट गर्नुहोस्"</string>
+3
View File
@@ -21,4 +21,7 @@
<bool name="enable_niu_actions">true</bool>
<string name="task_overlay_factory_class" translatable="false">com.android.quickstep.TaskOverlayFactoryGo</string>
<!-- String representing the intent to delete a package. -->
<string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
</resources>
File renamed without changes.
@@ -27,9 +27,12 @@ import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.FileProvider;
@@ -44,6 +47,9 @@ import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.views.ActivityContext;
import java.io.File;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Defines the Share system shortcut and its factory.
@@ -64,14 +70,17 @@ public final class AppSharing {
private static final String TAG = "AppSharing";
private static final String FILE_PROVIDER_SUFFIX = ".overview.fileprovider";
private static final String APP_EXSTENSION = ".apk";
private static final String APP_EXTENSION = ".apk";
private static final String APP_MIME_TYPE = "application/application";
private final String mSharingComponent;
private AppShareabilityManager mShareabilityMgr;
private AppSharing(Launcher launcher) {
mSharingComponent = launcher.getText(R.string.app_sharing_component).toString();
String sharingComponent = Settings.Secure.getString(launcher.getContentResolver(),
Settings.Secure.NEARBY_SHARING_COMPONENT);
mSharingComponent = TextUtils.isEmpty(sharingComponent) ? launcher.getText(
R.string.app_sharing_component).toString() : sharingComponent;
}
private Uri getShareableUri(Context context, String path, String displayName) {
@@ -108,6 +117,9 @@ public final class AppSharing {
private final PopupDataProvider mPopupDataProvider;
private final boolean mSharingEnabledForUser;
private final Set<View> mBoundViews = Collections.newSetFromMap(new WeakHashMap<>());
private boolean mIsEnabled = true;
public Share(Launcher target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo,
originalView);
@@ -123,11 +135,24 @@ public final class AppSharing {
}
}
@Override
public void setIconAndLabelFor(View iconView, TextView labelView) {
super.setIconAndLabelFor(iconView, labelView);
mBoundViews.add(iconView);
mBoundViews.add(labelView);
}
@Override
public void setIconAndContentDescriptionFor(ImageView view) {
super.setIconAndContentDescriptionFor(view);
mBoundViews.add(view);
}
@Override
public void onClick(View view) {
ActivityContext.lookupContext(view.getContext())
.getStatsLogManager().logger().log(LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP);
if (!isEnabled()) {
if (!mIsEnabled) {
showCannotShareToast(view.getContext());
return;
}
@@ -147,7 +172,7 @@ public final class AppSharing {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
sourceDir = packageInfo.applicationInfo.sourceDir;
appLabel = packageManager.getApplicationLabel(packageInfo.applicationInfo)
.toString() + APP_EXSTENSION;
+ APP_EXTENSION;
} catch (Exception e) {
Log.e(TAG, "Could not find info for package \"" + packageName + "\"");
return;
@@ -175,9 +200,6 @@ public final class AppSharing {
return;
}
checkShareability(/* requestUpdateIfUnknown */ false);
mTarget.runOnUiThread(() -> {
mPopupDataProvider.redrawSystemShortcuts();
});
}
private void checkShareability(boolean requestUpdateIfUnknown) {
@@ -207,6 +229,17 @@ public final class AppSharing {
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}
public void setEnabled(boolean isEnabled) {
if (mIsEnabled != isEnabled) {
mIsEnabled = isEnabled;
mBoundViews.forEach(v -> v.setEnabled(isEnabled));
}
}
public boolean isEnabled() {
return mIsEnabled;
}
}
/**
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2018 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;
public final class BuildConfig {
public static final String APPLICATION_ID = "com.android.launcher3";
public static final boolean IS_STUDIO_BUILD = false;
/**
* 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;
/**
* Flag to state if the widget on the top of the first screen should be shown.
*/
public static final boolean WIDGET_ON_FIRST_SCREEN = false;
/**
* Flag to control various developer centric features
*/
public static final boolean IS_DEBUG_DEVICE = false;
// Flag to control widgets support in Launcher
public static final boolean WIDGETS_ENABLED = false;
// Flag to control notification dots support in Launcher
public static final boolean NOTIFICATION_DOTS_ENABLED = false;
}
@@ -35,6 +35,7 @@ import androidx.room.Room;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.model.AppShareabilityDatabase.ShareabilityDao;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -47,7 +48,7 @@ import java.util.function.Consumer;
* Each app's status is retrieved from the Play Store's API. Statuses are cached in order
* to limit extraneous calls to that API (which can be time-consuming).
*/
public class AppShareabilityManager {
public class AppShareabilityManager implements SafeCloseable {
@Retention(SOURCE)
@IntDef({
ShareabilityStatus.UNKNOWN,
@@ -194,6 +195,11 @@ public class AppShareabilityManager {
}
}
@Override
public void close() {
mDatabase.close();
}
/**
* Provides a testable instance of this class
* This instance allows database queries on the main thread
@@ -56,7 +56,7 @@ import com.android.launcher3.views.ArrowTipView;
import com.android.quickstep.util.AssistContentRequester;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.GoOverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView.TaskContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -101,8 +101,8 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
/**
* Create a new overlay instance for the given View
*/
public TaskOverlayGo createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlayGo(thumbnailView, mContentRequester);
public TaskOverlayGo createOverlay(TaskContainer taskContainer) {
return new TaskOverlayGo(taskContainer, mContentRequester);
}
/**
@@ -120,9 +120,9 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
private OverlayDialogGo mDialog;
private ArrowTipView mArrowTipView;
private TaskOverlayGo(TaskThumbnailView taskThumbnailView,
private TaskOverlayGo(TaskContainer taskContainer,
AssistContentRequester assistContentRequester) {
super(taskThumbnailView);
super(taskContainer);
mFactoryContentRequester = assistContentRequester;
mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
}
@@ -148,7 +148,8 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
// Disable Overview Actions for Work Profile apps
boolean isManagedProfileTask =
UserManager.get(mApplicationContext).isManagedProfile(task.key.userId);
boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot() && !isManagedProfileTask;
boolean isAllowedByPolicy = mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()
&& !isManagedProfileTask;
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
mTaskPackageName = task.key.getPackageName();
mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
@@ -162,8 +163,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
int taskId = task.key.id;
mFactoryContentRequester.requestAssistContent(taskId, this::onAssistContentReceived);
RecentsOrientedState orientedState =
mThumbnailView.getTaskView().getRecentsView().getPagedViewOrientedState();
RecentsOrientedState orientedState = mTaskContainer.getTaskView().getOrientedState();
boolean isInLandscape = orientedState.getDisplayRotation() != ROTATION_0;
// show tooltips in portrait mode only
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* Copyright 2017, 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.
*/
-->
<resources>
<bool name="notification_dots_enabled">false</bool>
</resources>
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2018 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.model;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.BgDataModel.Callbacks;
/**
* Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
public class LauncherBinder extends BaseLauncherBinder {
public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
@Override
public void bindDeepShortcuts() {
}
@Override
public void bindWidgets() {
}
}
@@ -1,91 +0,0 @@
/*
* Copyright (C) 2018 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.model;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
* <p> The widgets and shortcuts are organized using package name as its index.
*/
public class WidgetsModel {
// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = true;
public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
/**
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
* sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
* not sorted. This list is sorted at the UI when using
* {@link com.android.launcher3.widget.picker.WidgetsDiffReporter}
*
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
return EMPTY_WIDGET_LIST;
}
/** Returns a mapping of packages to their widgets without static shortcuts. */
public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
return Map.of();
}
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
@Nullable PackageUserKey packageUser) {
return Collections.emptyList();
}
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
LauncherAppState app) {
}
public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName, UserHandle user) {
return null;
}
/** Returns {@link PackageItemInfo} of a pending widget. */
public static PackageItemInfo newPendingItemInfo(
Context context, ComponentName provider, UserHandle userHandle) {
return new PackageItemInfo(provider.getPackageName(), userHandle);
}
}
@@ -1,56 +0,0 @@
/*
* Copyright (C) 2022 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.util;
/**
* Defines method to find the next vacant cell on a grid.
* This uses the default top-down, left-right approach and can be over-written through
* code swaps in different launchers.
*/
public abstract class AbsGridOccupancy {
/**
* Find the first vacant cell, if there is one.
*
* @param vacantOut Holds the x and y coordinate of the vacant cell
* @param spanX Horizontal cell span.
* @param spanY Vertical cell span.
*
* @return true if a vacant cell was found
*/
protected boolean findVacantCell(int[] vacantOut, boolean[][] cells, int countX, int countY,
int spanX, int spanY) {
for (int y = 0; (y + spanY) <= countY; y++) {
for (int x = 0; (x + spanX) <= countX; x++) {
boolean available = !cells[x][y];
out:
for (int i = x; i < x + spanX; i++) {
for (int j = y; j < y + spanY; j++) {
available = available && !cells[i][j];
if (!available) break out;
}
}
if (available) {
vacantOut[0] = x;
vacantOut[1] = y;
return true;
}
}
}
return false;
}
}
+1 -1
View File
@@ -16,6 +16,6 @@ PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.9.4
PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.1.0
BUILD_TOOLS_VERSION=28.0.3
COMPILE_SDK=android-34
COMPILE_SDK=android-35
#KOTLIN=org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10
KOTLIN=org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0
+7 -9
View File
@@ -3,19 +3,14 @@ plugins {
}
android {
compileSdkVersion COMPILE_SDK
namespace = "com.android.launcher3.icons"
compileSdkVersion COMPILE_SDK
defaultConfig {
minSdkVersion 26
targetSdkVersion 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
targetSdkVersion 35
}
sourceSets {
main {
java.srcDirs = ['src', 'src_full_lib']
@@ -26,6 +21,10 @@ android {
lint {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
@@ -34,5 +33,4 @@ android {
dependencies {
implementation "androidx.core:core:1.13.1"
implementation "androidx.annotation:annotation:1.8.1"
}
@@ -17,27 +17,16 @@
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#11000000"
android:pathData="M.5,12.25
A11.5,11.5 0 1,1 23.5,12.25
A11.5,11.5 0 1,1 .5,12.25" />
<path
android:fillColor="@android:color/white"
android:pathData="M1,12
A11,11 0 1,1 23,12
A11,11 0 1,1 1,12" />
android:viewportHeight="24"
android:tint="@color/badge_tint_clone">
<group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
<path
android:pathData="M22,9.5C22,13.642 18.642,17 14.5,17C10.358,17 7,13.642 7,9.5C7,5.358 10.358,2 14.5,2C18.642,2 22,5.358 22,9.5Z"
android:fillColor="#ff3C4043"/>
<path
android:fillColor="#FFFFFFFF"/>
<path
android:pathData="M9.5,20.333C12.722,20.333 15.333,17.722 15.333,14.5C15.333,11.278 12.722,8.667 9.5,8.667C6.278,8.667 3.667,11.278 3.667,14.5C3.667,17.722 6.278,20.333 9.5,20.333ZM9.5,22C13.642,22 17,18.642 17,14.5C17,10.358 13.642,7 9.5,7C5.358,7 2,10.358 2,14.5C2,18.642 5.358,22 9.5,22Z"
android:fillColor="#ff3C4043"
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"/>
</group>
</vector>
@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#11000000"
android:pathData="M.5,12.25
A11.5,11.5 0 1,1 23.5,12.25
A11.5,11.5 0 1,1 .5,12.25" />
<path
android:fillColor="@color/themed_icon_background_color"
android:pathData="M1,12
A11,11 0 1,1 23,12
A11,11 0 1,1 1,12" />
<group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
<path
android:pathData="M22,9.5C22,13.642 18.642,17 14.5,17C10.358,17 7,13.642 7,9.5C7,5.358 10.358,2 14.5,2C18.642,2 22,5.358 22,9.5Z"
android:fillColor="@color/themed_badge_icon_color"/>
<path
android:pathData="M9.5,20.333C12.722,20.333 15.333,17.722 15.333,14.5C15.333,11.278 12.722,8.667 9.5,8.667C6.278,8.667 3.667,11.278 3.667,14.5C3.667,17.722 6.278,20.333 9.5,20.333ZM9.5,22C13.642,22 17,18.642 17,14.5C17,10.358 13.642,7 9.5,7C5.358,7 2,10.358 2,14.5C2,18.642 5.358,22 9.5,22Z"
android:fillColor="@color/themed_badge_icon_color"
android:fillType="evenOdd"/>
</group>
</vector>
@@ -17,14 +17,11 @@
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="18"
android:viewportHeight="18">
android:viewportHeight="18"
android:tint="@color/badge_tint_instant">
<path
android:fillColor="@android:color/white"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@android:color/black"
android:fillColor="#FFFFFFFF"
android:strokeWidth="1"
android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
</vector>
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:fillColor="@color/themed_badge_icon_background_color"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@color/themed_badge_icon_color"
android:strokeWidth="1"
android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
</vector>
@@ -0,0 +1,32 @@
<!--
~ Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/badge_tint_private">
<group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
<path
android:pathData="M12,2L4,5V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5L12,2ZM15,15V17H13V18H11V12.84C9.56,12.41 8.5,11.09 8.5,9.5C8.5,7.57 10.07,6 12,6C13.93,6 15.5,7.57 15.5,9.5C15.5,11.08 14.44,12.41 13,12.84V15H15Z"
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"/>
<path
android:pathData="M12,11C12.828,11 13.5,10.328 13.5,9.5C13.5,8.672 12.828,8 12,8C11.172,8 10.5,8.672 10.5,9.5C10.5,10.328 11.172,11 12,11Z"
android:fillColor="#FFFFFFFF"/>
</group>
</vector>
@@ -17,23 +17,12 @@
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#11000000"
android:pathData="M.5,12.25
A11.5,11.5 0 1,1 23.5,12.25
A11.5,11.5 0 1,1 .5,12.25" />
<path
android:fillColor="@android:color/white"
android:pathData="M1,12
A11,11 0 1,1 23,12
A11,11 0 1,1 1,12" />
android:viewportHeight="24"
android:tint="@color/badge_tint_work">
<group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
<path
android:fillColor="#1A73E8"
android:fillColor="#FFFFFFFF"
android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" />
</group>
</vector>
@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/profile_badge_size"
android:height="@dimen/profile_badge_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#11000000"
android:pathData="M.5,12.25
A11.5,11.5 0 1,1 23.5,12.25
A11.5,11.5 0 1,1 .5,12.25" />
<path
android:fillColor="@color/themed_badge_icon_background_color"
android:pathData="M1,12
A11,11 0 1,1 23,12
A11,11 0 1,1 1,12" />
<group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
<path
android:fillColor="@color/themed_badge_icon_color"
android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" />
</group>
</vector>
+5 -1
View File
@@ -21,7 +21,11 @@
<color name="themed_icon_background_color">#D3E3FD</color>
<color name="themed_badge_icon_color">#0842A0</color>
<color name="themed_badge_icon_background_color">#D3E3FD</color>
<color name="legacy_icon_background">#FFFFFF</color>
<color name="badge_tint_instant">@android:color/black</color>
<color name="badge_tint_work">#1A73E8</color>
<color name="badge_tint_private">#3C4043</color>
<color name="badge_tint_clone">#ff3C4043</color>
<!-- Yellow 600, used for highlighting "important" conversations in settings & notifications -->
<color name="important_conversation">#f9ab00</color>
@@ -5,9 +5,7 @@ import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
import static com.android.launcher3.icons.BitmapInfo.FLAG_CLONE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_INSTANT;
import static com.android.launcher3.icons.BitmapInfo.FLAG_WORK;
import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -29,10 +27,11 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.os.UserHandle;
import android.util.SparseBooleanArray;
import android.util.SparseArray;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
@@ -41,6 +40,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo.Extender;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.UserIconInfo;
import java.lang.annotation.Retention;
import java.util.Objects;
@@ -52,6 +52,7 @@ import java.util.Objects;
public class BaseIconFactory implements AutoCloseable {
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
private static final float LEGACY_ICON_SCALE = .7f * (1f / (1 + 2 * getExtraInsetFraction()));
public static final int MODE_DEFAULT = 0;
public static final int MODE_ALPHA = 1;
@@ -61,7 +62,8 @@ public class BaseIconFactory implements AutoCloseable {
@Retention(SOURCE)
@IntDef({MODE_DEFAULT, MODE_ALPHA, MODE_WITH_SHADOW, MODE_HARDWARE_WITH_SHADOW, MODE_HARDWARE})
@interface BitmapGenerationMode {}
@interface BitmapGenerationMode {
}
private static final float ICON_BADGE_SCALE = 0.444f;
@@ -69,7 +71,7 @@ public class BaseIconFactory implements AutoCloseable {
private final Rect mOldBounds = new Rect();
@NonNull
private final SparseBooleanArray mIsUserBadged = new SparseBooleanArray();
private final SparseArray<UserIconInfo> mCachedUserInfo = new SparseArray<>();
@NonNull
protected final Context mContext;
@@ -162,8 +164,7 @@ public class BaseIconFactory implements AutoCloseable {
* Create a placeholder icon using the passed in text.
*
* @param placeholder used for foreground element in the icon bitmap
* @param color used for the foreground text color
* @return
* @param color used for the foreground text color
*/
public BitmapInfo createIconBitmap(String placeholder, int color) {
AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
@@ -203,27 +204,26 @@ public class BaseIconFactory implements AutoCloseable {
* Creates bitmap using the source drawable and various parameters.
* The bitmap is visually normalized with other icons and has enough spacing to add shadow.
*
* @param icon source of the icon
* @param icon source of the icon
* @return a bitmap suitable for disaplaying as an icon at various system UIs.
*/
@TargetApi(Build.VERSION_CODES.TIRAMISU)
@NonNull
public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon,
@Nullable IconOptions options) {
boolean shrinkNonAdaptiveIcons = options == null || options.mShrinkNonAdaptiveIcons;
float[] scale = new float[1];
icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale);
Bitmap bitmap = createIconBitmap(icon, scale[0],
AdaptiveIconDrawable adaptiveIcon = normalizeAndWrapToAdaptiveIcon(icon, null, scale);
Bitmap bitmap = createIconBitmap(adaptiveIcon, scale[0],
options == null ? MODE_WITH_SHADOW : options.mGenerationMode);
int color = (options != null && options.mExtractedColor != null)
? options.mExtractedColor : mColorExtractor.findDominantColorByHue(bitmap);
BitmapInfo info = BitmapInfo.of(bitmap, color);
if (icon instanceof BitmapInfo.Extender) {
info = ((BitmapInfo.Extender) icon).getExtendedInfo(bitmap, color, this, scale[0]);
if (adaptiveIcon instanceof BitmapInfo.Extender extender) {
info = extender.getExtendedInfo(bitmap, color, this, scale[0]);
} else if (IconProvider.ATLEAST_T && mMonoIconEnabled) {
Drawable mono = getMonochromeDrawable(icon);
Drawable mono = getMonochromeDrawable(adaptiveIcon);
if (mono != null) {
info.setMonoIcon(createIconBitmap(mono, scale[0], MODE_ALPHA), this);
}
@@ -234,15 +234,14 @@ public class BaseIconFactory implements AutoCloseable {
/**
* Returns a monochromatic version of the given drawable or null, if it is not supported
*
* @param base the original icon
*/
@TargetApi(Build.VERSION_CODES.TIRAMISU)
protected Drawable getMonochromeDrawable(Drawable base) {
if (base instanceof AdaptiveIconDrawable) {
Drawable mono = ((AdaptiveIconDrawable) base).getMonochrome();
if (mono != null) {
return new ClippedMonoDrawable(mono);
}
protected Drawable getMonochromeDrawable(AdaptiveIconDrawable base) {
Drawable mono = base.getMonochrome();
if (mono != null) {
return new ClippedMonoDrawable(mono);
}
return null;
}
@@ -255,27 +254,35 @@ public class BaseIconFactory implements AutoCloseable {
op = op.addFlag(FLAG_INSTANT);
}
if (options.mUserHandle != null) {
int key = options.mUserHandle.hashCode();
boolean isBadged;
int index;
if ((index = mIsUserBadged.indexOfKey(key)) >= 0) {
isBadged = mIsUserBadged.valueAt(index);
} else {
// Check packageManager if the provided user needs a badge
NoopDrawable d = new NoopDrawable();
isBadged = (d != mPm.getUserBadgedIcon(d, options.mUserHandle));
mIsUserBadged.put(key, isBadged);
}
// Set the clone profile badge flag in case it is present.
op = op.setFlag(FLAG_CLONE, isBadged && options.mIsCloneProfile);
// Set the Work profile badge for all other cases.
op = op.setFlag(FLAG_WORK, isBadged && !options.mIsCloneProfile);
UserIconInfo info = options.mUserIconInfo;
if (info == null && options.mUserHandle != null) {
info = getUserInfo(options.mUserHandle);
}
if (info != null) {
op = info.applyBitmapInfoFlags(op);
}
}
return op;
}
@NonNull
protected UserIconInfo getUserInfo(@NonNull UserHandle user) {
int key = user.hashCode();
UserIconInfo info = mCachedUserInfo.get(key);
/*
* We do not have the ability to distinguish between different badged users here.
* As such all badged users will have the work profile badge applied.
*/
if (info == null) {
// Simple check to check if the provided user is work profile or not based on badging
NoopDrawable d = new NoopDrawable();
boolean isWork = (d != mPm.getUserBadgedIcon(d, user));
info = new UserIconInfo(user, isWork ? UserIconInfo.TYPE_WORK : UserIconInfo.TYPE_MAIN);
mCachedUserInfo.put(key, info);
}
return info;
}
@NonNull
public Bitmap getWhiteShadowLayer() {
if (mWhiteShadowLayer == null) {
@@ -290,7 +297,7 @@ public class BaseIconFactory implements AutoCloseable {
public Bitmap createScaledBitmap(@NonNull Drawable icon, @BitmapGenerationMode int mode) {
RectF iconBounds = new RectF();
float[] scale = new float[1];
icon = normalizeAndWrapToAdaptiveIcon(icon, true, iconBounds, scale);
icon = normalizeAndWrapToAdaptiveIcon(icon, iconBounds, scale);
return createIconBitmap(icon,
Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)), mode);
}
@@ -303,37 +310,59 @@ public class BaseIconFactory implements AutoCloseable {
}
@Nullable
protected Drawable normalizeAndWrapToAdaptiveIcon(@Nullable Drawable icon,
final boolean shrinkNonAdaptiveIcons, @Nullable final RectF outIconBounds,
@NonNull final float[] outScale) {
protected AdaptiveIconDrawable normalizeAndWrapToAdaptiveIcon(@Nullable Drawable icon,
@Nullable final RectF outIconBounds, @NonNull final float[] outScale) {
if (icon == null) {
return null;
}
float scale = 1f;
if (shrinkNonAdaptiveIcons && !(icon instanceof AdaptiveIconDrawable)) {
if (mWrapperIcon == null) {
mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
.mutate();
}
AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
AdaptiveIconDrawable adaptiveIcon;
float scale;
adaptiveIcon = wrapToAdaptiveIcon(icon, outIconBounds);
scale = getNormalizer().getScale(adaptiveIcon, outIconBounds, null, null);
outScale[0] = scale;
return adaptiveIcon;
}
/**
* Returns a drawable which draws the original drawable at a fixed scale
*/
private Drawable createScaledDrawable(@NonNull Drawable main, float scale) {
float h = main.getIntrinsicHeight();
float w = main.getIntrinsicWidth();
float scaleX = scale;
float scaleY = scale;
if (h > w && w > 0) {
scaleX *= w / h;
} else if (w > h && h > 0) {
scaleY *= h / w;
}
scaleX = (1 - scaleX) / 2;
scaleY = (1 - scaleY) / 2;
return new InsetDrawable(main, scaleX, scaleY, scaleX, scaleY);
}
/**
* Wraps the provided icon in an adaptive icon drawable
*/
public AdaptiveIconDrawable wrapToAdaptiveIcon(@NonNull Drawable icon,
@Nullable final RectF outIconBounds) {
if (icon instanceof AdaptiveIconDrawable aid) {
return aid;
} else {
EmptyWrapper foreground = new EmptyWrapper();
AdaptiveIconDrawable dr = new AdaptiveIconDrawable(
new ColorDrawable(mWrapperBackgroundColor), foreground);
dr.setBounds(0, 0, 1, 1);
boolean[] outShape = new boolean[1];
scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
float scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
if (!outShape[0]) {
FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
fsd.setDrawable(icon);
fsd.setScale(scale);
icon = dr;
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
foreground.setDrawable(createScaledDrawable(icon, scale * LEGACY_ICON_SCALE));
} else {
foreground.setDrawable(createScaledDrawable(icon, 1 - getExtraInsetFraction()));
}
} else {
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
return dr;
}
outScale[0] = scale;
return icon;
}
@NonNull
@@ -376,6 +405,8 @@ public class BaseIconFactory implements AutoCloseable {
mOldBounds.set(icon.getBounds());
if (icon instanceof AdaptiveIconDrawable) {
// We are ignoring KEY_SHADOW_DISTANCE because regular icons ignore this at the
// moment b/298203449
int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
Math.round(size * (1 - scale) / 2));
// b/211896569: AdaptiveIconDrawable do not work properly for non top-left bounds
@@ -464,28 +495,20 @@ public class BaseIconFactory implements AutoCloseable {
public static class IconOptions {
boolean mShrinkNonAdaptiveIcons = true;
boolean mIsInstantApp;
boolean mIsCloneProfile;
@BitmapGenerationMode
int mGenerationMode = MODE_WITH_SHADOW;
@Nullable UserHandle mUserHandle;
@Nullable
UserHandle mUserHandle;
@Nullable
UserIconInfo mUserIconInfo;
@ColorInt
@Nullable Integer mExtractedColor;
@Nullable
Integer mExtractedColor;
/**
* Set to false if non-adaptive icons should not be treated
*/
@NonNull
public IconOptions setShrinkNonAdaptiveIcons(final boolean shrink) {
mShrinkNonAdaptiveIcons = shrink;
return this;
}
/**
* User for this icon, in case of badging
@@ -496,6 +519,15 @@ public class BaseIconFactory implements AutoCloseable {
return this;
}
/**
* User for this icon, in case of badging
*/
@NonNull
public IconOptions setUser(@Nullable final UserIconInfo user) {
mUserIconInfo = user;
return this;
}
/**
* If this icon represents an instant app
*/
@@ -523,15 +555,6 @@ public class BaseIconFactory implements AutoCloseable {
mGenerationMode = generationMode;
return this;
}
/**
* Used to determine the badge type for this icon.
*/
@NonNull
public IconOptions setIsCloneProfile(boolean isCloneProfile) {
mIsCloneProfile = isCloneProfile;
return this;
}
}
/**
@@ -615,4 +638,17 @@ public class BaseIconFactory implements AutoCloseable {
mTextPaint);
}
}
private static class EmptyWrapper extends DrawableWrapper {
EmptyWrapper() {
super(new ColorDrawable());
}
@Override
public ConstantState getConstantState() {
Drawable d = getDrawable();
return d == null ? null : d.getConstantState();
}
}
}
@@ -16,10 +16,10 @@
package com.android.launcher3.icons;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -29,21 +29,25 @@ import com.android.launcher3.util.FlagOp;
public class BitmapInfo {
static final int FLAG_WORK = 1 << 0;
static final int FLAG_INSTANT = 1 << 1;
static final int FLAG_CLONE = 1 << 2;
public static final int FLAG_WORK = 1 << 0;
public static final int FLAG_INSTANT = 1 << 1;
public static final int FLAG_CLONE = 1 << 2;
public static final int FLAG_PRIVATE = 1 << 3;
@IntDef(flag = true, value = {
FLAG_WORK,
FLAG_INSTANT,
FLAG_CLONE
FLAG_CLONE,
FLAG_PRIVATE
})
@interface BitmapInfoFlags {}
public static final int FLAG_THEMED = 1 << 0;
public static final int FLAG_NO_BADGE = 1 << 1;
public static final int FLAG_SKIP_USER_BADGE = 1 << 2;
@IntDef(flag = true, value = {
FLAG_THEMED,
FLAG_NO_BADGE,
FLAG_SKIP_USER_BADGE,
})
public @interface DrawableCreationFlags {}
@@ -151,25 +155,50 @@ public class BitmapInfo {
protected void applyFlags(Context context, FastBitmapDrawable drawable,
@DrawableCreationFlags int creationFlags) {
drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f);
drawable.mCreationFlags = creationFlags;
if ((creationFlags & FLAG_NO_BADGE) == 0) {
if (badgeInfo != null) {
drawable.setBadge(badgeInfo.newIcon(context, creationFlags));
} else if ((flags & FLAG_INSTANT) != 0) {
drawable.setBadge(context.getDrawable(drawable.isThemed()
? R.drawable.ic_instant_app_badge_themed
: R.drawable.ic_instant_app_badge));
} else if ((flags & FLAG_WORK) != 0) {
drawable.setBadge(context.getDrawable(drawable.isThemed()
? R.drawable.ic_work_app_badge_themed
: R.drawable.ic_work_app_badge));
} else if ((flags & FLAG_CLONE) != 0) {
drawable.setBadge(context.getDrawable(drawable.isThemed()
? R.drawable.ic_clone_app_badge_themed
: R.drawable.ic_clone_app_badge));
Drawable badge = getBadgeDrawable(context, (creationFlags & FLAG_THEMED) != 0,
(creationFlags & FLAG_SKIP_USER_BADGE) != 0);
if (badge != null) {
drawable.setBadge(badge);
}
}
}
public Drawable getBadgeDrawable(Context context, boolean isThemed) {
return getBadgeDrawable(context, isThemed, false);
}
/**
* Returns a drawable representing the badge for this info
*/
@Nullable
private Drawable getBadgeDrawable(Context context, boolean isThemed, boolean skipUserBadge) {
if (badgeInfo != null) {
int creationFlag = isThemed ? FLAG_THEMED : 0;
if (skipUserBadge) {
creationFlag |= FLAG_SKIP_USER_BADGE;
}
return badgeInfo.newIcon(context, creationFlag);
}
if (skipUserBadge) {
return null;
} else if ((flags & FLAG_INSTANT) != 0) {
return new UserBadgeDrawable(context, R.drawable.ic_instant_app_badge,
R.color.badge_tint_instant, isThemed);
} else if ((flags & FLAG_WORK) != 0) {
return new UserBadgeDrawable(context, R.drawable.ic_work_app_badge,
R.color.badge_tint_work, isThemed);
} else if ((flags & FLAG_CLONE) != 0) {
return new UserBadgeDrawable(context, R.drawable.ic_clone_app_badge,
R.color.badge_tint_clone, isThemed);
} else if ((flags & FLAG_PRIVATE) != 0) {
return new UserBadgeDrawable(context, R.drawable.ic_private_profile_app_badge,
R.color.badge_tint_private, isThemed);
}
return null;
}
public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
return of(bitmap, 0);
}
@@ -78,7 +78,6 @@ public class BubbleIconFactory extends BaseIconFactory {
outScale = new float[1];
}
icon = normalizeAndWrapToAdaptiveIcon(icon,
true /* shrinkNonAdaptiveIcons */,
null /* outscale */,
outScale);
return createIconBitmap(icon, outScale[0], MODE_WITH_SHADOW);
@@ -56,6 +56,8 @@ import java.util.function.IntFunction;
@TargetApi(Build.VERSION_CODES.O)
public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender {
public static boolean sRunningInTest = false;
private static final String TAG = "ClockDrawableWrapper";
private static final boolean DISABLE_SECONDS = true;
@@ -477,7 +479,6 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap
if (!isVisible()) {
return;
}
unscheduleSelf(this);
final long upTime = SystemClock.uptimeMillis();
final long step = TICK_MS; /* tick every 200 ms */
@@ -17,6 +17,8 @@
package com.android.launcher3.icons;
import static com.android.launcher3.icons.BaseIconFactory.getBadgeSizeForIconSize;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
@@ -34,16 +36,23 @@ import android.util.FloatProperty;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
private static final Interpolator ACCEL = new AccelerateInterpolator();
private static final Interpolator DEACCEL = new DecelerateInterpolator();
private static final Interpolator HOVER_EMPHASIZED_DECELERATE_INTERPOLATOR =
new PathInterpolator(0.05f, 0.7f, 0.1f, 1.0f);
private static final float PRESSED_SCALE = 1.1f;
@VisibleForTesting protected static final float PRESSED_SCALE = 1.1f;
@VisibleForTesting protected static final float HOVERED_SCALE = 1.1f;
public static final int WHITE_SCRIM_ALPHA = 138;
private static final float DISABLED_DESATURATION = 1f;
@@ -51,6 +60,9 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
protected static final int FULLY_OPAQUE = 255;
public static final int CLICK_FEEDBACK_DURATION = 200;
public static final int HOVER_FEEDBACK_DURATION = 300;
private static boolean sFlagHoverEnabled = false;
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
protected final Bitmap mBitmap;
@@ -58,12 +70,15 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
@Nullable private ColorFilter mColorFilter;
private boolean mIsPressed;
@VisibleForTesting protected boolean mIsPressed;
@VisibleForTesting protected boolean mIsHovered;
protected boolean mIsDisabled;
float mDisabledAlpha = 1f;
@DrawableCreationFlags int mCreationFlags = 0;
// Animator and properties for the fast bitmap drawable's scale
private static final FloatProperty<FastBitmapDrawable> SCALE
@VisibleForTesting protected static final FloatProperty<FastBitmapDrawable> SCALE
= new FloatProperty<FastBitmapDrawable>("scale") {
@Override
public Float get(FastBitmapDrawable fastBitmapDrawable) {
@@ -76,7 +91,7 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
fastBitmapDrawable.invalidateSelf();
}
};
private ObjectAnimator mScaleAnimation;
@VisibleForTesting protected ObjectAnimator mScaleAnimation;
private float mScale = 1;
private int mAlpha = 255;
@@ -146,6 +161,14 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
return false;
}
/**
* Returns true if the drawable was created with theme, even if it doesn't
* support theming itself.
*/
public boolean isCreatedForTheme() {
return isThemed() || (mCreationFlags & FLAG_THEMED) != 0;
}
@Override
public void setColorFilter(ColorFilter cf) {
mColorFilter = cf;
@@ -163,6 +186,9 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
mAlpha = alpha;
mPaint.setAlpha(alpha);
invalidateSelf();
if (mBadge != null) {
mBadge.setAlpha(alpha);
}
}
}
@@ -223,37 +249,41 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
@Override
protected boolean onStateChange(int[] state) {
boolean isPressed = false;
boolean isHovered = false;
for (int s : state) {
if (s == android.R.attr.state_pressed) {
isPressed = true;
break;
} else if (sFlagHoverEnabled && s == android.R.attr.state_hovered) {
isHovered = true;
// Do not break on hovered state, as pressed state should take precedence.
}
}
if (mIsPressed != isPressed) {
mIsPressed = isPressed;
if (mIsPressed != isPressed || mIsHovered != isHovered) {
if (mScaleAnimation != null) {
mScaleAnimation.cancel();
mScaleAnimation = null;
}
if (mIsPressed) {
// Animate when going to pressed state
mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
mScaleAnimation.setInterpolator(ACCEL);
mScaleAnimation.start();
} else {
float endScale = isPressed ? PRESSED_SCALE : (isHovered ? HOVERED_SCALE : 1f);
if (mScale != endScale) {
if (isVisible()) {
mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
mScaleAnimation.setInterpolator(DEACCEL);
Interpolator interpolator =
isPressed != mIsPressed ? (isPressed ? ACCEL : DEACCEL)
: HOVER_EMPHASIZED_DECELERATE_INTERPOLATOR;
int duration =
isPressed != mIsPressed ? CLICK_FEEDBACK_DURATION
: HOVER_FEEDBACK_DURATION;
mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, endScale);
mScaleAnimation.setDuration(duration);
mScaleAnimation.setInterpolator(interpolator);
mScaleAnimation.start();
} else {
mScale = 1f;
mScale = endScale;
invalidateSelf();
}
}
mIsPressed = isPressed;
mIsHovered = isHovered;
return true;
}
return false;
@@ -282,6 +312,11 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
updateFilter();
}
@VisibleForTesting
public Drawable getBadge() {
return mBadge;
}
/**
* Updates the paint to reflect the current brightness and saturation.
*/
@@ -304,6 +339,7 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
if (mBadge != null) {
cs.mBadgeConstantState = mBadge.getConstantState();
}
cs.mCreationFlags = mCreationFlags;
return cs;
}
@@ -311,6 +347,11 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
return getDisabledColorFilter(1);
}
// Returns if the FastBitmapDrawable contains a badge.
public boolean hasBadge() {
return (mCreationFlags & FLAG_NO_BADGE) == 0;
}
private static ColorFilter getDisabledColorFilter(float disabledAlpha) {
ColorMatrix tempBrightnessMatrix = new ColorMatrix();
ColorMatrix tempFilterMatrix = new ColorMatrix();
@@ -366,6 +407,13 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
unscheduleSelf(what);
}
/**
* Sets whether hover state functionality is enabled.
*/
public static void setFlagHoverEnabled(boolean isFlagHoverEnabled) {
sFlagHoverEnabled = isFlagHoverEnabled;
}
protected static class FastBitmapConstantState extends ConstantState {
protected final Bitmap mBitmap;
protected final int mIconColor;
@@ -375,6 +423,8 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
protected boolean mIsDisabled;
private ConstantState mBadgeConstantState;
@DrawableCreationFlags int mCreationFlags = 0;
public FastBitmapConstantState(Bitmap bitmap, int color) {
mBitmap = bitmap;
mIconColor = color;
@@ -391,6 +441,7 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback {
if (mBadgeConstantState != null) {
drawable.setBadge(mBadgeConstantState.newDrawable());
}
drawable.mCreationFlags = mCreationFlags;
return drawable;
}
@@ -1,53 +0,0 @@
package com.android.launcher3.icons;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.DrawableWrapper;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
/**
* Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
*/
public class FixedScaleDrawable extends DrawableWrapper {
// TODO b/33553066 use the constant defined in MaskableIconDrawable
private static final float LEGACY_ICON_SCALE = .7f * .6667f;
private float mScaleX, mScaleY;
public FixedScaleDrawable() {
super(new ColorDrawable());
mScaleX = LEGACY_ICON_SCALE;
mScaleY = LEGACY_ICON_SCALE;
}
@Override
public void draw(Canvas canvas) {
int saveCount = canvas.save();
canvas.scale(mScaleX, mScaleY,
getBounds().exactCenterX(), getBounds().exactCenterY());
super.draw(canvas);
canvas.restoreToCount(saveCount);
}
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { }
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
public void setScale(float scale) {
float h = getIntrinsicHeight();
float w = getIntrinsicWidth();
mScaleX = scale * LEGACY_ICON_SCALE;
mScaleY = scale * LEGACY_ICON_SCALE;
if (h > w && w > 0) {
mScaleX *= w / h;
} else if (w > h && h > 0) {
mScaleY *= h / w;
}
}
}
@@ -109,7 +109,8 @@ public class ShadowGenerator {
scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide);
}
float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE;
// We are ignoring KEY_SHADOW_DISTANCE because regular icons ignore this at the moment b/298203449
float bottomSpace = BLUR_FACTOR;
if (bounds.bottom < bottomSpace) {
scale = Math.min(scale,
(HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom));
@@ -15,16 +15,12 @@
*/
package com.android.launcher3.icons;
import static android.content.res.Configuration.UI_MODE_NIGHT_MASK;
import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -93,13 +89,6 @@ public class ThemedIconDrawable extends FastBitmapDrawable {
return new ThemedConstantState(bitmapInfo, colorBg, colorFg);
}
public void changeBackgroundColor(int colorBg) {
if (mIsDisabled) return;
mBgPaint.setColorFilter(new BlendModeColorFilter(colorBg, BlendMode.SRC_IN));
invalidateSelf();
}
static class ThemedConstantState extends FastBitmapConstantState {
final BitmapInfo bitmapInfo;
@@ -0,0 +1,191 @@
/*
* Copyright (C) 2023 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.icons;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
/**
* A drawable used for drawing user badge. It draws a circle around the actual badge,
* and has support for theming.
*/
public class UserBadgeDrawable extends DrawableWrapper {
private static final float VIEWPORT_SIZE = 24;
private static final float CENTER = VIEWPORT_SIZE / 2;
private static final float BG_RADIUS = 11;
private static final float SHADOW_RADIUS = 11.5f;
private static final float SHADOW_OFFSET_Y = 0.25f;
@VisibleForTesting
static final int SHADOW_COLOR = 0x11000000;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int mBaseColor;
private final int mBgColor;
private boolean mShouldDrawBackground = true;
@VisibleForTesting
public final boolean mIsThemed;
public UserBadgeDrawable(Context context, int badgeRes, int colorRes, boolean isThemed) {
super(context.getDrawable(badgeRes));
mIsThemed = isThemed;
if (isThemed) {
mutate();
mBaseColor = context.getColor(R.color.themed_badge_icon_color);
mBgColor = context.getColor(R.color.themed_badge_icon_background_color);
} else {
mBaseColor = context.getColor(colorRes);
mBgColor = Color.WHITE;
}
setTint(mBaseColor);
}
private UserBadgeDrawable(Drawable base, int bgColor, int baseColor,
boolean shouldDrawBackground) {
super(base);
mIsThemed = false;
mBgColor = bgColor;
mBaseColor = baseColor;
mShouldDrawBackground = shouldDrawBackground;
}
@Override
public void draw(@NonNull Canvas canvas) {
if (mShouldDrawBackground) {
Rect b = getBounds();
int saveCount = canvas.save();
canvas.translate(b.left, b.top);
canvas.scale(b.width() / VIEWPORT_SIZE, b.height() / VIEWPORT_SIZE);
mPaint.setColor(blendDrawableAlpha(SHADOW_COLOR));
canvas.drawCircle(CENTER, CENTER + SHADOW_OFFSET_Y, SHADOW_RADIUS, mPaint);
mPaint.setColor(blendDrawableAlpha(mBgColor));
canvas.drawCircle(CENTER, CENTER, BG_RADIUS, mPaint);
canvas.restoreToCount(saveCount);
}
super.draw(canvas);
}
private @ColorInt int blendDrawableAlpha(@ColorInt int color) {
int alpha = (int) (Color.valueOf(color).alpha() * getAlpha());
return ColorUtils.setAlphaComponent(color, alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter filter) {
if (filter == null) {
super.setTint(mBaseColor);
} else if (filter instanceof ColorMatrixColorFilter cf) {
ColorMatrix cm = new ColorMatrix();
cf.getColorMatrix(cm);
ColorMatrix cm2 = new ColorMatrix();
float[] base = cm2.getArray();
base[0] = Color.red(mBaseColor) / 255f;
base[6] = Color.green(mBaseColor) / 255f;
base[12] = Color.blue(mBaseColor) / 255f;
base[18] = Color.alpha(mBaseColor) / 255f;
cm2.postConcat(cm);
super.setColorFilter(new ColorMatrixColorFilter(cm2));
} else {
// fail safe
Paint p = new Paint();
p.setColorFilter(filter);
Bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
new Canvas(b).drawPaint(p);
super.setTint(b.getPixel(0, 0));
}
}
public void setShouldDrawBackground(boolean shouldDrawBackground) {
mutate();
mShouldDrawBackground = shouldDrawBackground;
}
@Override
public ConstantState getConstantState() {
return new MyConstantState(
getDrawable().getConstantState(), mBgColor, mBaseColor, mShouldDrawBackground);
}
private static class MyConstantState extends ConstantState {
private final ConstantState mBase;
private final int mBgColor;
private final int mBaseColor;
private final boolean mShouldDrawBackground;
MyConstantState(ConstantState base, int bgColor, int baseColor,
boolean shouldDrawBackground) {
mBase = base;
mBgColor = bgColor;
mBaseColor = baseColor;
mShouldDrawBackground = shouldDrawBackground;
}
@Override
public int getChangingConfigurations() {
return mBase.getChangingConfigurations();
}
@Override
@NonNull
public Drawable newDrawable() {
return new UserBadgeDrawable(
mBase.newDrawable(), mBgColor, mBaseColor, mShouldDrawBackground);
}
@Override
@NonNull
public Drawable newDrawable(Resources res) {
return new UserBadgeDrawable(
mBase.newDrawable(res), mBgColor, mBaseColor, mShouldDrawBackground);
}
@Override
@NonNull
public Drawable newDrawable(Resources res, Theme theme) {
return new UserBadgeDrawable(
mBase.newDrawable(res, theme), mBgColor, mBaseColor, mShouldDrawBackground);
}
}
}
@@ -45,7 +45,6 @@ import android.os.Handler;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -133,6 +132,8 @@ public abstract class BaseIconCache {
@NonNull
private final Looper mBgLooper;
private volatile boolean mIconUpdateInProgress = false;
public BaseIconCache(@NonNull final Context context, @Nullable final String dbFileName,
@NonNull final Looper bgLooper, final int iconDpi, final int iconPixelSize,
final boolean inMemoryCache) {
@@ -200,7 +201,8 @@ public abstract class BaseIconCache {
if (resources != null && iconId != 0) {
try {
return resources.getDrawableForDensity(iconId, mIconDpi);
} catch (Resources.NotFoundException e) { }
} catch (Resources.NotFoundException e) {
}
}
return getFullResDefaultActivityIcon(mIconDpi);
}
@@ -209,7 +211,8 @@ public abstract class BaseIconCache {
public Drawable getFullResIcon(@NonNull final String packageName, final int iconId) {
try {
return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId);
} catch (PackageManager.NameNotFoundException e) { }
} catch (PackageManager.NameNotFoundException e) {
}
return getFullResDefaultActivityIcon(mIconDpi);
}
@@ -218,10 +221,19 @@ public abstract class BaseIconCache {
try {
return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo),
info.getIconResource());
} catch (PackageManager.NameNotFoundException e) { }
} catch (PackageManager.NameNotFoundException e) {
}
return getFullResDefaultActivityIcon(mIconDpi);
}
public void setIconUpdateInProgress(boolean updating) {
mIconUpdateInProgress = updating;
}
public boolean isIconUpdateInProgress() {
return mIconUpdateInProgress;
}
/**
* Remove any records for the supplied ComponentName.
*/
@@ -236,13 +248,13 @@ public abstract class BaseIconCache {
private void removeFromMemCacheLocked(@Nullable final String packageName,
@Nullable final UserHandle user) {
HashSet<ComponentKey> forDeletion = new HashSet<>();
for (ComponentKey key: mCache.keySet()) {
for (ComponentKey key : mCache.keySet()) {
if (key.componentName.getPackageName().equals(packageName)
&& key.user.equals(user)) {
forDeletion.add(key);
}
}
for (ComponentKey condemned: forDeletion) {
for (ComponentKey condemned : forDeletion) {
mCache.remove(condemned);
}
}
@@ -299,6 +311,7 @@ public abstract class BaseIconCache {
/**
* Adds an entry into the DB and the in-memory cache.
*
* @param replaceExisting if true, it will recreate the bitmap even if it already exists in
* the memory. This is useful then the previous bitmap was created using
* old data.
@@ -329,9 +342,11 @@ public abstract class BaseIconCache {
if (entry.bitmap.isNullOrLowRes()) return;
CharSequence entryTitle = cachingLogic.getLabel(object);
if (entryTitle == null) {
Log.wtf(TAG, "No label returned from caching logic instance: " + cachingLogic);
entryTitle = "";
if (TextUtils.isEmpty(entryTitle)) {
if (entryTitle == null) {
Log.wtf(TAG, "No label returned from caching logic instance: " + cachingLogic);
}
entryTitle = componentName.getPackageName();
}
entry.title = entryTitle;
@@ -346,6 +361,7 @@ public abstract class BaseIconCache {
/**
* Updates {@param values} to contain versioning information and adds it to the DB.
*
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(@NonNull final ContentValues values, @NonNull final ComponentName key,
@@ -466,8 +482,10 @@ public abstract class BaseIconCache {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
componentName.getPackageName(), user, false);
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
if (DEBUG) {
Log.d(TAG, "using package default icon for "
+ componentName.toShortString());
}
entry.bitmap = packageEntry.bitmap;
entry.contentDescription = packageEntry.contentDescription;
@@ -490,13 +508,16 @@ public abstract class BaseIconCache {
@NonNull final T object, @NonNull final CacheEntry entry,
@NonNull final CachingLogic<T> cachingLogic, @NonNull final UserHandle user) {
entry.title = cachingLogic.getLabel(object);
if (TextUtils.isEmpty(entry.title)) {
entry.title = cachingLogic.getComponent(object).getPackageName();
}
entry.contentDescription = getUserBadgedLabel(
cachingLogic.getDescription(object, entry.title), user);
}
public synchronized void clear() {
public synchronized void clearMemoryCache() {
assertWorkerThread();
mIconDb.clear();
mCache.clear();
}
/**
@@ -541,6 +562,7 @@ public abstract class BaseIconCache {
*/
@WorkerThread
@NonNull
@SuppressWarnings("NewApi")
protected CacheEntry getEntryForPackageLocked(@NonNull final String packageName,
@NonNull final UserHandle user, final boolean useLowResIcon) {
assertWorkerThread();
@@ -554,26 +576,39 @@ public abstract class BaseIconCache {
// Check the DB first.
if (!getEntryFromDBLocked(cacheKey, entry, useLowResIcon)) {
try {
int flags = Process.myUserHandle().equals(user) ? 0 :
long flags = Process.myUserHandle().equals(user) ? 0 :
PackageManager.GET_UNINSTALLED_PACKAGES;
PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
flags |= PackageManager.MATCH_ARCHIVED_PACKAGES;
PackageInfo info = mPackageManager.getPackageInfo(packageName,
PackageManager.PackageInfoFlags.of(flags));
ApplicationInfo appInfo = info.applicationInfo;
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
NameNotFoundException e = new NameNotFoundException(
"ApplicationInfo is null");
logdPersistently(TAG,
String.format("ApplicationInfo is null for %s", packageName),
e);
throw e;
}
BaseIconFactory li = getIconFactory();
// Load the full res icon for the application, but if useLowResIcon is set, then
// only keep the low resolution icon instead of the larger full-sized icon
BitmapInfo iconInfo = li.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager),
Drawable appIcon = appInfo.loadIcon(mPackageManager);
if (mPackageManager.isDefaultApplicationIcon(appIcon)) {
logdPersistently(TAG,
String.format("Default icon returned for %s", packageName),
null);
}
BitmapInfo iconInfo = li.createBadgedIconBitmap(appIcon,
new IconOptions().setUser(user).setInstantApp(isInstantApp(appInfo)));
li.close();
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = getUserBadgedLabel(entry.title, user);
entry.bitmap = BitmapInfo.of(
useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color);
entry.bitmap = useLowResIcon
? BitmapInfo.of(LOW_RES_ICON, iconInfo.color)
: iconInfo;
// Add the icon in the DB here, since these do not get written during
// package updates.
@@ -700,17 +735,19 @@ public abstract class BaseIconCache {
public static final String COLUMN_SYSTEM_STATE = "system_state";
public static final String COLUMN_KEYWORDS = "keywords";
public static final String[] COLUMNS_LOW_RES = new String[] {
public static final String[] COLUMNS_LOW_RES = new String[]{
COLUMN_COMPONENT,
COLUMN_LABEL,
COLUMN_ICON_COLOR,
COLUMN_FLAGS};
public static final String[] COLUMNS_HIGH_RES = Arrays.copyOf(COLUMNS_LOW_RES,
COLUMNS_LOW_RES.length + 2, String[].class);
static {
COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length] = COLUMN_ICON;
COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length + 1] = COLUMN_MONO_ICON;
}
private static final int INDEX_TITLE = Arrays.asList(COLUMNS_LOW_RES).indexOf(COLUMN_LABEL);
private static final int INDEX_COLOR = Arrays.asList(COLUMNS_LOW_RES)
.indexOf(COLUMN_ICON_COLOR);
@@ -778,4 +815,9 @@ public abstract class BaseIconCache {
throw new IllegalStateException("Cache accessed on wrong thread " + Looper.myLooper());
}
}
/** Log to Log.d. Subclasses can override this method to log persistently for debugging. */
protected void logdPersistently(String tag, String message, @Nullable Exception e) {
Log.d(tag, message, e);
}
}
@@ -1,79 +0,0 @@
/*
* Copyright (C) 2018 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.icons.cache;
import android.os.Handler;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* A runnable that can be posted to a {@link Handler} which can be canceled.
*/
public class HandlerRunnable<T> implements Runnable {
private final Handler mWorkerHandler;
private final Supplier<T> mTask;
private final Executor mCallbackExecutor;
private final Consumer<T> mCallback;
private final Runnable mEndRunnable;
private boolean mEnded = false;
private boolean mCanceled = false;
public HandlerRunnable(Handler workerHandler, Supplier<T> task, Executor callbackExecutor,
Consumer<T> callback) {
this(workerHandler, task, callbackExecutor, callback, () -> { });
}
public HandlerRunnable(Handler workerHandler, Supplier<T> task, Executor callbackExecutor,
Consumer<T> callback, Runnable endRunnable) {
mWorkerHandler = workerHandler;
mTask = task;
mCallbackExecutor = callbackExecutor;
mCallback = callback;
mEndRunnable = endRunnable;
}
/**
* Cancels this runnable from being run, only if it has not already run.
*/
public void cancel() {
mWorkerHandler.removeCallbacks(this);
mCanceled = true;
mCallbackExecutor.execute(this::onEnd);
}
@Override
public void run() {
T value = mTask.get();
mCallbackExecutor.execute(() -> {
if (!mCanceled) {
mCallback.accept(value);
}
onEnd();
});
}
private void onEnd() {
if (!mEnded) {
mEnded = true;
mEndRunnable.run();
}
}
}
@@ -30,13 +30,13 @@ import android.util.SparseBooleanArray;
import com.android.launcher3.icons.cache.BaseIconCache.IconDB;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
/**
* Utility class to handle updating the Icon cache
@@ -57,7 +57,7 @@ public class IconCacheUpdateHandler {
*/
private static final boolean MODE_CLEAR_VALID_ITEMS = false;
private static final Object ICON_UPDATE_TOKEN = new Object();
static final Object ICON_UPDATE_TOKEN = new Object();
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final BaseIconCache mIconCache;
@@ -101,6 +101,7 @@ public class IconCacheUpdateHandler {
/**
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
* the DB and are updated.
*
* @return The set of packages for which icons have updated.
*/
public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
@@ -130,6 +131,7 @@ public class IconCacheUpdateHandler {
/**
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
* the DB and are updated.
*
* @return The set of packages for which icons have updated.
*/
@SuppressWarnings("unchecked")
@@ -141,7 +143,7 @@ public class IconCacheUpdateHandler {
}
long userSerial = mIconCache.getSerialNumberForUser(user);
Stack<T> appsToUpdate = new Stack<>();
ArrayDeque<T> appsToUpdate = new ArrayDeque<>();
try (Cursor c = mIconCache.mIconDb.query(
new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
@@ -183,7 +185,7 @@ public class IconCacheUpdateHandler {
if (version == info.versionCode
&& updateTime == cachingLogic.getLastUpdatedTime(app, info)
&& TextUtils.equals(c.getString(systemStateIndex),
mIconCache.getIconSystemState(info.packageName))) {
mIconCache.getIconSystemState(info.packageName))) {
if (mFilterMode == MODE_CLEAR_VALID_ITEMS) {
mItemsToDelete.put(rowId, false);
@@ -207,8 +209,9 @@ public class IconCacheUpdateHandler {
// Insert remaining apps.
if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
Stack<T> appsToAdd = new Stack<>();
ArrayDeque<T> appsToAdd = new ArrayDeque<>();
appsToAdd.addAll(componentMap.values());
mIconCache.setIconUpdateInProgress(true);
new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
onUpdateCallback).scheduleNext();
}
@@ -226,7 +229,7 @@ public class IconCacheUpdateHandler {
.append(" IN (");
int count = mItemsToDelete.size();
for (int i = 0; i < count; i++) {
for (int i = 0; i < count; i++) {
if (mItemsToDelete.valueAt(i)) {
if (deleteCount > 0) {
queryBuilder.append(", ");
@@ -250,14 +253,14 @@ public class IconCacheUpdateHandler {
private class SerializedIconUpdateTask<T> implements Runnable {
private final long mUserSerial;
private final UserHandle mUserHandle;
private final Stack<T> mAppsToAdd;
private final Stack<T> mAppsToUpdate;
private final ArrayDeque<T> mAppsToAdd;
private final ArrayDeque<T> mAppsToUpdate;
private final CachingLogic<T> mCachingLogic;
private final HashSet<String> mUpdatedPackages = new HashSet<>();
private final OnUpdateCallback mOnUpdateCallback;
SerializedIconUpdateTask(long userSerial, UserHandle userHandle,
Stack<T> appsToAdd, Stack<T> appsToUpdate, CachingLogic<T> cachingLogic,
ArrayDeque<T> appsToAdd, ArrayDeque<T> appsToUpdate, CachingLogic<T> cachingLogic,
OnUpdateCallback onUpdateCallback) {
mUserHandle = userHandle;
mUserSerial = userSerial;
@@ -270,7 +273,7 @@ public class IconCacheUpdateHandler {
@Override
public void run() {
if (!mAppsToUpdate.isEmpty()) {
T app = mAppsToUpdate.pop();
T app = mAppsToUpdate.removeLast();
String pkg = mCachingLogic.getComponent(app).getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
@@ -286,8 +289,9 @@ public class IconCacheUpdateHandler {
// Let it run one more time.
scheduleNext();
} else if (!mAppsToAdd.isEmpty()) {
T app = mAppsToAdd.pop();
PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
T app = mAppsToAdd.removeLast();
PackageInfo info = mPkgInfoMap.get(
mCachingLogic.getComponent(app).getPackageName());
// We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
// app should have package info, this is not guaranteed by the api
if (info != null) {
@@ -297,6 +301,10 @@ public class IconCacheUpdateHandler {
if (!mAppsToAdd.isEmpty()) {
scheduleNext();
} else if (!mIconCache.mWorkerHandler.hasMessages(0, ICON_UPDATE_TOKEN)) {
// This checks if there is a second icon update process happening
// before notifying BaseIconCache that the updates are over
mIconCache.setIconUpdateInProgress(false);
}
}
}
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2013 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.util;
import static com.android.launcher3.icons.BitmapInfo.FLAG_CLONE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_PRIVATE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_WORK;
import android.os.UserHandle;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
/**
* Data class which stores various properties of a {@link android.os.UserHandle}
* which affects rendering
*/
public class UserIconInfo {
public static final int TYPE_MAIN = 0;
public static final int TYPE_WORK = 1;
public static final int TYPE_CLONED = 2;
public static final int TYPE_PRIVATE = 3;
@IntDef({TYPE_MAIN, TYPE_WORK, TYPE_CLONED, TYPE_PRIVATE})
public @interface UserType { }
public final UserHandle user;
@UserType
public final int type;
public final long userSerial;
public UserIconInfo(UserHandle user, @UserType int type) {
this(user, type, user != null ? user.hashCode() : 0);
}
public UserIconInfo(UserHandle user, @UserType int type, long userSerial) {
this.user = user;
this.type = type;
this.userSerial = userSerial;
}
public boolean isMain() {
return type == TYPE_MAIN;
}
public boolean isWork() {
return type == TYPE_WORK;
}
public boolean isCloned() {
return type == TYPE_CLONED;
}
public boolean isPrivate() {
return type == TYPE_PRIVATE;
}
@NonNull
public FlagOp applyBitmapInfoFlags(@NonNull FlagOp op) {
return op.setFlag(FLAG_WORK, isWork())
.setFlag(FLAG_CLONE, isCloned())
.setFlag(FLAG_PRIVATE, isPrivate());
}
}
@@ -68,14 +68,14 @@ public class SimpleIconCache extends BaseIconCache {
@Override
protected long getSerialNumberForUser(@NonNull UserHandle user) {
synchronized (mUserSerialMap) {
int index = mUserSerialMap.indexOfKey(user.hashCode());
// int index = mUserSerialMap.indexOfKey(user.getIdentifier());
int index = mUserSerialMap.indexOfKey(0);
if (index >= 0) {
return mUserSerialMap.valueAt(index);
}
long serial = mUserManager.getSerialNumberForUser(user);
mUserSerialMap.put(user.hashCode(), serial);
// mUserSerialMap.put(user.getIdentifier(), serial);
mUserSerialMap.put(0, serial);
return serial;
}
}
@@ -88,8 +88,8 @@ public class SimpleIconCache extends BaseIconCache {
@Override
protected boolean isInstantApp(@NonNull ApplicationInfo info) {
// return info.isInstantApp();
return false;
// return info.isInstantApp();
}
@NonNull
+28 -139
View File
@@ -1,148 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
id="NewApi"
message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
line="72"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/util/DynamicResource.java"
line="73"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.graphics.Outline#setPath`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
line="114"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.View#getWindowInsetsController`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="902"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.WindowInsets.Type#ime`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="902"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.WindowInsetsController#hide`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="902"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#resetColorResources`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
line="114"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 31 (current min is 30): `android.appwidget.AppWidgetHostView#setColorResources`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
line="415"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
line="93"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1814"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1824"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1815"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1823"/>
</issue>
<issue
id="NewApi"
message="Method reference requires API level 28 (current min is 26): `Person::getKey`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
line="195"/>
</issue>
<issue
id="NewApi"
message="Method reference requires API level 28 (current min is 26): `Person::getKey`">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
line="73"/>
</issue>
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)">
<location
file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
line="6"/>
</issue>
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)">
<location
file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
line="7"/>
</issue>
<issue
id="NewApi"
message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)">
message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
errorLine1=' &lt;corners android:radius="@android:dimen/system_app_widget_background_radius" /&gt;'
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
line="20"/>
line="20"
column="14"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#resetColorResources`"
errorLine1=" resetColorResources();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
line="117"
column="13"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 31 (current min is 30): `android.appwidget.AppWidgetHostView#setColorResources`"
errorLine1=" view.setColorResources(mWallpaperColorResources);"
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
line="433"
column="18"/>
</issue>
</issues>
+24 -3
View File
@@ -38,7 +38,7 @@ message ItemInfo {
optional int32 rank = 5;
// Stores whether the Item belows to non primary user
optional bool is_work = 6;
optional bool is_work = 6 [deprecated = true];
// Item can be child node to parent container or parent containers (nested)
optional ContainerInfo container_info = 7;
@@ -48,6 +48,9 @@ message ItemInfo {
// Stores whether the navigation bar is in kids mode.
optional bool is_kids_mode = 13;
// Stores type of the user the Item belong to
optional int32 user_type = 14;
}
message LauncherAttributes{
@@ -118,6 +121,20 @@ message SettingsContainer {
}
message TaskSwitcherContainer {
/**
* Indicates the current OrientationHandler in use in Overview.
* In fake landscape, the value will be
* {@link com.android.quickstep.orientation.LandscapePagedViewHandler} and in real landscape,
* the value will be {@link com.android.quickstep.orientation.PortraitPagedViewHandler} for
* example.
*/
optional OrientationHandler orientation_handler = 1;
enum OrientationHandler {
PORTRAIT = 0;
LANDSCAPE = 1;
SEASCAPE = 2;
}
}
// Container for taskbar.
@@ -135,7 +152,7 @@ message TaskBarContainer {
}
}
// Next value 51
// Next value 54
enum Attribute {
option allow_alias = true;
@@ -180,13 +197,16 @@ enum Attribute {
ALL_APPS_SEARCH_RESULT_CHROMETAB = 24;
ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25 [deprecated = true];
ALL_APPS_SEARCH_RESULT_TIPS = 26;
ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27;
ALL_APPS_SEARCH_RESULT_QS_TILE = 27;
ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27 [deprecated = true];
ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
ALL_APPS_SEARCH_RESULT_ASSISTANT_MEMORY = 31;
ALL_APPS_SEARCH_RESULT_VIDEO = 41;
ALL_APPS_SEARCH_RESULT_SYSTEM_POINTER = 42;
ALL_APPS_SEARCH_RESULT_EDUCARD = 43;
ALL_APPS_SEARCH_RESULT_LOCATION = 50;
ALL_APPS_SEARCH_RESULT_TEXT_HEADER = 51;
ALL_APPS_SEARCH_RESULT_NO_FULFILLMENT = 52;
// Result sources
DATA_SOURCE_APPSEARCH_APP_PREVIEW = 45;
@@ -194,6 +214,7 @@ enum Attribute {
DATA_SOURCE_APPSEARCH_CATEGORY_SRP_PREVIEW = 48;
DATA_SOURCE_APPSEARCH_ENTITY_SRP_PREVIEW = 49;
DATA_SOURCE_AIAI_SEARCH_ROOT = 47;
DATA_SOURCE_LAUNCHER = 53;
// Web suggestions provided by AGA
ALL_APPS_SEARCH_RESULT_WEB_SUGGEST = 39;
BIN
View File
Binary file not shown.
+16 -5
View File
@@ -23,15 +23,23 @@ filegroup {
}
filegroup {
name: "launcher3-quickstep-robolectric-src",
path: "robolectric_tests",
srcs: ["robolectric_tests/src/**/*.java"],
name: "launcher3-quickstep-robo-src",
path: "tests/multivalentTests",
srcs: [
"tests/multivalentTests/src/**/*.java",
"tests/multivalentTests/src/**/*.kt",
],
}
filegroup {
name: "launcher3-quickstep-tests-src",
path: "tests",
srcs: ["tests/src/**/*.java", "tests/src/**/*.kt"],
srcs: [
"tests/multivalentTests/src/**/*.java",
"tests/multivalentTests/src/**/*.kt",
"tests/src/**/*.java",
"tests/src/**/*.kt",
],
}
filegroup {
@@ -41,6 +49,9 @@ filegroup {
"tests/src/com/android/quickstep/TaskbarModeSwitchRule.java",
"tests/src/com/android/quickstep/NavigationModeSwitchRule.java",
"tests/src/com/android/quickstep/AbstractQuickStepTest.java",
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
]
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
"tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java"
],
}
-1
View File
@@ -20,7 +20,6 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
+35 -7
View File
@@ -30,7 +30,9 @@
<uses-permission android:name="android.permission.REMOVE_TASKS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
<uses-permission android:name="android.permission.STATUS_BAR"/>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
<uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
<uses-permission android:name="android.permission.SET_ORIENTATION"/>
<uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
@@ -41,6 +43,16 @@
<uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
<!--
Permission required to access profiles which are otherwise hidden
from being visible via APIs, e.g. private profile.
-->
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" />
<!-- Permission required to start a WidgetPickerActivity. -->
<permission android:name="${applicationId}.permission.START_WIDGET_PICKER_ACTIVITY"
android:protectionLevel="signature|privileged" />
<application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
@@ -67,15 +79,16 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
android:screenOrientation="unspecified"
android:screenOrientation="behind"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:enableOnBackInvokedCallback="false"
android:taskAffinity=""/>
<!-- Content provider to settings search. The autority should be same as the packageName -->
<provider android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
android:authorities="${packageName}"
android:authorities="${applicationId}"
android:grantUriPermissions="true"
android:multiprocess="true"
android:permission="android.permission.READ_SEARCH_INDEXABLES"
@@ -87,7 +100,7 @@
<!-- FileProvider used for sharing images. -->
<provider android:name="androidx.core.content.FileProvider"
android:authorities="${packageName}.overview.fileprovider"
android:authorities="${applicationId}.overview.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
@@ -95,10 +108,11 @@
</provider>
<activity android:name="com.android.launcher3.proxy.ProxyActivityStarter"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:exported="false"/>
android:theme="@style/ProxyActivityStarterTheme"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:exported="false"
/>
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
@@ -130,6 +144,20 @@
</intent-filter>
</activity>
<activity android:name="com.android.launcher3.WidgetPickerActivity"
android:theme="@style/WidgetPickerActivityTheme"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
android:showOnLockScreen="true"
android:launchMode="singleTop"
android:exported="true"
android:permission="android.permission.START_WIDGET_PICKER_ACTIVITY">
<intent-filter>
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2022 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.quickstep;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.android.launcher3.testing.DebugTestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
/**
* Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
*/
public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
private final DebugTestInformationHandler mDebugTestInformationHandler;
public DebugQuickstepTestInformationHandler(Context context) {
super(context);
mDebugTestInformationHandler = new DebugTestInformationHandler(context);
}
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
Bundle response = new Bundle();
if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) {
// Allow null-pointer to catch illegal states.
runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
return response;
}
response = super.call(method, arg, extras);
if (response != null) return response;
return mDebugTestInformationHandler.call(method, arg, extras);
}
}
@@ -41,6 +41,7 @@ message DeviceSearchResultContainer{
// For application, it will be the column number in the apps row.
optional int32 grid_x = 3;
// LINT.IfChange
// Next ID = 4
message SearchAttributes{
@@ -61,6 +62,13 @@ message DeviceSearchResultContainer{
// User entered by tapping on QSB bar on homescreen.
QSB = 2;
// User entered by swiping up from overview (using Rocket Gesture).
OVERVIEW = 3;
// User entered by tapping the search box in the Taskbar AllApps drawer.
TASKBAR = 4;
}
}
// LINT.ThenChange(//quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java)
}
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="tests" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="multivalentTests" />
<orderEntry type="module" module-name="multivalentTestsForDevice1" />
<orderEntry type="module" module-name="Launcher3-refs_heads_android15-s1-release" />
<orderEntry type="module" module-name="tests" />
</component>
</module>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:alpha="0.35" android:color="?androidprv:attr/materialColorPrimaryContainer" />
</selector>
@@ -16,7 +16,8 @@
android:width="84dp"
android:height="208dp"
android:viewportWidth="84"
android:viewportHeight="208">
android:viewportHeight="208"
android:autoMirrored="true">
<path
android:pathData="M24.53,169.2L32.09,165.56C77.7,143.55 77.7,64.45 32.09,42.35L24.53,38.71C14.55,33.95 6.06,25.56 0,14.92V193.08C6.06,182.44 14.55,174.05 24.53,169.2Z"
android:fillColor="?attr/onSurfaceBack"/>
@@ -16,7 +16,8 @@
android:width="122dp"
android:height="303dp"
android:viewportWidth="122"
android:viewportHeight="303">
android:viewportHeight="303"
android:autoMirrored="true">
<path
android:pathData="M35.65,245.9L46.63,240.61C112.92,208.62 112.92,93.67 46.63,61.54L35.65,56.26C21.15,49.34 8.81,37.14 0,21.69V280.6C8.81,265.15 21.15,252.95 35.65,245.9Z"
android:fillColor="?attr/onSurfaceBack"/>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/bubblebar_drop_target_corner_radius" />
<solid android:color="@color/bubblebar_drop_target_bg_color" />
<stroke
android:width="1dp"
android:color="?androidprv:attr/materialColorPrimaryContainer" />
</shape>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke
android:width="2dp"
android:color="@android:color/system_accent1_600" />
<solid android:color="@android:color/system_accent1_600" />
</shape>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:inset="@dimen/bubble_expanded_view_drop_target_padding">
<shape
android:shape="rectangle">
<corners android:radius="@dimen/bubble_expanded_view_drop_target_corner_radius" />
<solid android:color="@color/bubblebar_drop_target_bg_color" />
<stroke
android:width="1dp"
android:color="?androidprv:attr/materialColorPrimaryContainer" />
</shape>
</inset>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2023 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.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/rounded_button_radius" />
<solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
</shape>
@@ -20,7 +20,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?colorButtonNormal">
<corners android:radius="24dp" />
<corners android:radius="@dimen/recents_clear_all_outline_radius" />
<solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
</shape>
</item>
@@ -20,7 +20,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp"/>
<corners android:radius="28dp"/>
<solid android:color="@color/taskbar_edu_button_color"/>
</shape>
</item>
@@ -16,7 +16,8 @@
android:width="83dp"
android:height="208dp"
android:viewportWidth="83"
android:viewportHeight="208">
android:viewportHeight="208"
android:autoMirrored="true">
<path
android:pathData="M23.53,169.2L31.09,165.56C76.7,143.55 76.7,64.45 31.09,42.35L23.53,38.71C13.55,33.95 5.06,25.56 -1,14.92V193.08C5.06,182.44 13.55,174.05 23.53,169.2Z"
android:fillColor="?attr/onSurfaceBack"/>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
android:fillColor="@android:color/system_neutral1_50"/>
</vector>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="48dp"
android:height="48dp"
android:autoMirrored="true"
android:tint="?androidprv:attr/materialColorOnSurface"
android:viewportHeight="48"
android:viewportWidth="48">
<group
android:name="scaleGroup"
android:pivotX="24"
android:pivotY="24"
android:rotation="90">
<path
android:fillColor="@android:color/white"
android:pathData="M18.75,36 L16.6,33.85 26.5,23.95 16.6,14.05 18.75,11.9 30.8,23.95Z" />
</group>
</vector>
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M13.329,2.305H4.242C2.751,2.305 1.542,3.514 1.542,5.005V13.005C1.542,14.496 2.751,15.705 4.242,15.705H7.875V19.011C7.875,20.502 9.084,21.711 10.575,21.711H19.662C21.153,21.711 22.362,20.502 22.362,19.011V10.011C22.362,8.52 21.153,7.311 19.662,7.311H16.029V5.005C16.029,3.514 14.821,2.305 13.329,2.305ZM14.329,7.311V5.005C14.329,4.452 13.882,4.005 13.329,4.005H4.242C3.69,4.005 3.242,4.452 3.242,5.005V13.005C3.242,13.557 3.69,14.005 4.242,14.005H7.875V10.011C7.875,8.52 9.084,7.311 10.575,7.311H14.329ZM9.575,14.005V10.011C9.575,9.611 9.81,9.266 10.15,9.106C10.285,9.037 10.438,8.999 10.6,8.999H19.687C20.239,8.999 20.687,9.447 20.687,9.999V18.999C20.687,19.399 20.452,19.744 20.113,19.904C19.977,19.972 19.824,20.011 19.662,20.011H10.575C10.023,20.011 9.575,19.563 9.575,19.011V15.705H9.6V14.005H9.575ZM15.542,11.996V14H17.588V15H15.542V16.996H14.542V15H12.464V14H14.542V11.996H15.542Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M8.5,4.5H3.5C2.4,4.5 1.5,5.4 1.5,6.5V18.5C1.5,19.6 2.4,20.5 3.5,20.5H8.5C9.6,20.5 10.5,19.6 10.5,18.5V6.5C10.5,5.4 9.6,4.5 8.5,4.5ZM8.5,18.5H3.5V6.5H8.5V18.5ZM14.5,6.5H19.5V13.5H21.5V6.5C21.5,5.4 20.6,4.5 19.5,4.5H14.5C13.4,4.5 12.5,5.4 12.5,6.5V18.5C12.5,19.6 13.4,20.5 14.5,20.5H15.5V18.5H14.5V6.5ZM20.5,14.5V16.5H22.5V18.5H20.5V20.5H18.5V18.5H16.5V16.5H18.5V14.5H20.5Z"
android:fillColor="#48473A"
android:fillType="evenOdd"/>
</vector>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,2L6,2C4.9,2 4,2.9 4,4L4,9C4,10.1 4.9,11 6,11L18,11C19.1,11 20,10.1 20,9L20,4C20,2.9 19.1,2 18,2ZM18,9L6,9L6,4L18,4L18,9ZM18,13L6,13C4.9,13 4,13.9 4,15L4,20C4,21.1 4.9,22 6,22L13,22L13,20L6,20L6,15L18,15L18,16L20,16L20,15C20,13.9 19.1,13 18,13ZM16,17L18,17L18,19L20,19L20,21L18,21L18,23L16,23L16,21L14,21L14,19L16,19L16,17Z"
android:fillColor="#48473A"
android:fillType="evenOdd"/>
</vector>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 The Android Open Source Project
<!-- Copyright (C) 2024 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.
@@ -13,11 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.popup.PopupContainerWithArrow
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"/>
android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="@dimen/keyboard_quick_switch_task_view_radius" />
</shape>
Loaded 100 of 1624 files, more files were not shown because too many files have changed in this diff. Show more