diff --git a/Android.bp b/Android.bp index b22b21451acb8ab96f267e0ba419d87a30a0413b..4a436da6c5a68fab231b80318e34fbb1ccccaacf 100644 --- a/Android.bp +++ b/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_visibility: [":__subpackages__"], } apex { @@ -53,11 +54,6 @@ android_app_certificate { certificate: "com.android.permission", } -filegroup { - name: "permission-jarjar-rules", - srcs: ["jarjar-rules.txt"], -} - sdk { name: "permission-module-sdk", apexes: [ @@ -109,8 +105,9 @@ bootclasspath_fragment { // result in a build failure due to inconsistent flags. package_prefixes: [ "android.app.role", + "android.permission.jarjar", "android.safetycenter", - "com.android.permission", + "android.safetylabel", ], }, } diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index fc93cc5050d024e165b148ecb8cfab72460dc806..925bbf9d79e483cc56ede1975abaa3f1ce30e33d 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -4,4 +4,5 @@ commit_msg_changeid_field = true [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} -ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES} +ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/packages/modules/Permission/ktfmt_includes.txt ${PREUPLOAD_FILES} +ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp index 74a47f288c04909f0b169071a045c7da0c7d3b26..cd0ceab01015dcf3ff74d91bcceaabefd976c4cb 100644 --- a/PermissionController/Android.bp +++ b/PermissionController/Android.bp @@ -78,7 +78,6 @@ android_app { // artifact. See also b/209458854. sdk_version: "system_current", min_sdk_version: "30", - target_sdk_version: "32", updatable: true, privileged: true, certificate: "platform", @@ -99,10 +98,11 @@ android_app { // Soong fails to automatically add this dependency because all the // *.kt sources are inside a filegroup. "kotlin-annotations", + "safety-center-annotations", ], static_libs: [ - "iconloader", + "iconloader_sc_mainline_prod", "com.google.android.material_material", "androidx.transition_transition", "androidx-constraintlayout_constraintlayout", @@ -134,20 +134,29 @@ android_app { "SettingsLibActionBarShadow", "SettingsLibProgressBar", "SettingsLibCollapsingToolbarBaseActivity", + "SettingsLibActivityEmbedding", "SettingsLibSettingsTheme", "SettingsLibFooterPreference", "SettingsLibSelectorWithWidgetPreference", "SettingsLibTwoTargetPreference", + "SettingsLibIllustrationPreference", "androidx.annotation_annotation", "permissioncontroller-statsd", "car-ui-lib", "libprotobuf-java-lite", + "safety-center-internal-data", + "safety-center-pending-intents", "SettingsLibUtils", "modules-utils-build_system", + "safety-center-resources-lib", + "lottie", + "safety-label", + "role-controller", ], proto: { type: "lite", + include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"], }, lint: { diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml index e3490b10502b38536d523201484224dbda8e7f16..874ba35d6750dd5f7b5d9cd195eae55f15c36cd9 100644 --- a/PermissionController/AndroidManifest.xml +++ b/PermissionController/AndroidManifest.xml @@ -61,7 +61,11 @@ + + + + + - + + + + + + + + + + + + + + + + @@ -101,8 +128,33 @@ - + + + + + + + + + + + + + + + @@ -110,21 +162,112 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -199,11 +368,6 @@ - - + android:permission="android.permission.GRANT_RUNTIME_PERMISSIONS" > @@ -329,6 +493,18 @@ + + + + + + @@ -352,6 +528,18 @@ + + + + + + - @@ -372,7 +560,7 @@ - @@ -389,9 +577,10 @@ + android:theme="@style/Theme.SafetyCenter"> + + + + + + + + diff --git a/PermissionController/OWNERS b/PermissionController/OWNERS index 8f3889b9d74aeac0e34fe98a56464bc2609280ec..66cd836f2bea2ad24473b01871f85c7c125aa91c 100644 --- a/PermissionController/OWNERS +++ b/PermissionController/OWNERS @@ -1,8 +1,5 @@ include platform/frameworks/base:/core/java/android/permission/OWNERS -toddke@google.com -patb@google.com - # For automotive related changes stenning@google.com @@ -13,6 +10,9 @@ robhor@google.com # For incident report related changes joeo@google.com +# for SafetyCenter UI changes +per-file res/** = file:platform/packages/modules/Permission:/SafetyCenter/OWNERS + # For Wear related changes per-file WEAR_OWNERS = file:/PermissionController/WEAR_OWNERS per-file src/com/android/permissioncontroller/permission/ui/wear/** = file:/PermissionController/WEAR_OWNERS diff --git a/PermissionController/TEST_MAPPING b/PermissionController/TEST_MAPPING index 0911e0d910e03d9204c538f34654c3793913eab6..0ae3818fd7c53c15c38a148f7fd268cb5500f282 100644 --- a/PermissionController/TEST_MAPPING +++ b/PermissionController/TEST_MAPPING @@ -11,6 +11,74 @@ } ], "file_patterns": ["res/xml/roles\\.xml"] + }, + { + "name": "PermissionUiTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ], + "presubmit-large": [ + { + "name": "CtsPermission3TestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ], + "mainline-presubmit": [ + { + "name": "CtsRoleTestCases[com.google.android.permission.apex]", + "options": [ + // TODO(b/238677748): These two tests currently fails on R base image + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList" + }, + { + "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ], + "file_patterns": ["res/xml/roles\\.xml"] + }, + { + "name": "PermissionUiTestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + // TODO(b/238773220): These tests currently fails on R base image + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsGranted" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsRevoked" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenAppGetsInstalled" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstalled" + }, + { + "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenUserGetsUninstalled" + } + ] + }, + { + "name": "CtsPermission3TestCases[com.google.android.permission.apex]", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] } ], "imports": [ diff --git a/PermissionController/iconloaderlib/.gitignore b/PermissionController/iconloaderlib/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6213826ab5439d99a236ed9afd4dfd29dbb199a9 --- /dev/null +++ b/PermissionController/iconloaderlib/.gitignore @@ -0,0 +1,13 @@ +*.iml +.project +.classpath +.project.properties +gen/ +bin/ +.idea/ +.gradle/ +local.properties +gradle/ +build/ +gradlew* +.DS_Store diff --git a/PermissionController/iconloaderlib/Android.bp b/PermissionController/iconloaderlib/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..2bc3de43592cf49b73e46e0134edbd3139b55a80 --- /dev/null +++ b/PermissionController/iconloaderlib/Android.bp @@ -0,0 +1,52 @@ +// 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_applicable_licenses: ["Android-Apache-2.0"], +} + +android_library { + name: "iconloader_base_sc_mainline_prod", + sdk_version: "current", + min_sdk_version: "26", + static_libs: [ + "androidx.core_core", + ], + resource_dirs: [ + "res", + ], + srcs: [ + "src/**/*.java", + ], +} + +android_library { + name: "iconloader_sc_mainline_prod", + sdk_version: "system_current", + min_sdk_version: "26", + static_libs: [ + "androidx.core_core", + ], + resource_dirs: [ + "res", + ], + srcs: [ + "src/**/*.java", + "src_full_lib/**/*.java", + ], + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], +} diff --git a/PermissionController/iconloaderlib/AndroidManifest.xml b/PermissionController/iconloaderlib/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..b30258da2a30956e9f8bb0046d3bc33740d2d3d4 --- /dev/null +++ b/PermissionController/iconloaderlib/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/PermissionController/iconloaderlib/build.gradle b/PermissionController/iconloaderlib/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..35ca8dee2d3bb2eef262113a3bc6c343b973c057 --- /dev/null +++ b/PermissionController/iconloaderlib/build.gradle @@ -0,0 +1,38 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion COMPILE_SDK + buildToolsVersion BUILD_TOOLS_VERSION + + defaultConfig { + minSdkVersion 26 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + + sourceSets { + main { + java.srcDirs = ['src', 'src_full_lib'] + manifest.srcFile 'AndroidManifest.xml' + res.srcDirs = ['res'] + } + } + + lintOptions { + abortOnError false + } + + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation "androidx.core:core" +} diff --git a/PermissionController/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/PermissionController/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f13cf57196f5985b338f8c290a62b10859d1105 --- /dev/null +++ b/PermissionController/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/PermissionController/iconloaderlib/res/drawable/ic_instant_app_badge.xml b/PermissionController/iconloaderlib/res/drawable/ic_instant_app_badge.xml new file mode 100644 index 0000000000000000000000000000000000000000..b74317e5f2be33a41de4823d960f14079f15879a --- /dev/null +++ b/PermissionController/iconloaderlib/res/drawable/ic_instant_app_badge.xml @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/PermissionController/iconloaderlib/res/values/attrs.xml b/PermissionController/iconloaderlib/res/values/attrs.xml new file mode 100644 index 0000000000000000000000000000000000000000..8f0bd2c1ac5812d135e0fe3b5a802dce35caa3bf --- /dev/null +++ b/PermissionController/iconloaderlib/res/values/attrs.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/PermissionController/iconloaderlib/res/values/colors.xml b/PermissionController/iconloaderlib/res/values/colors.xml new file mode 100644 index 0000000000000000000000000000000000000000..70582c2e2524e0377c908daee98d554a05774bc1 --- /dev/null +++ b/PermissionController/iconloaderlib/res/values/colors.xml @@ -0,0 +1,24 @@ + + + + #FFFFFF + + + #f9ab00 + diff --git a/PermissionController/iconloaderlib/res/values/config.xml b/PermissionController/iconloaderlib/res/values/config.xml new file mode 100644 index 0000000000000000000000000000000000000000..893f955c24b7b43762b0b7fc6809ec3d3574dd43 --- /dev/null +++ b/PermissionController/iconloaderlib/res/values/config.xml @@ -0,0 +1,30 @@ + + + + + + + 56dp + false + app_icons.db + + + + + \ No newline at end of file diff --git a/PermissionController/iconloaderlib/res/values/dimens.xml b/PermissionController/iconloaderlib/res/values/dimens.xml new file mode 100644 index 0000000000000000000000000000000000000000..e8c0c44f72199e6f354ee5d57acd67ccbef14bc6 --- /dev/null +++ b/PermissionController/iconloaderlib/res/values/dimens.xml @@ -0,0 +1,19 @@ + + + + + 24dp + diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9ce99758784286356fc34ad3930a8c0ba1fffeb8 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -0,0 +1,466 @@ +package com.android.launcher3.icons; + +import static android.graphics.Paint.DITHER_FLAG; +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + +import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.os.Build; +import android.os.Process; +import android.os.UserHandle; + +import androidx.annotation.NonNull; + +import com.android.launcher3.icons.BitmapInfo.Extender; + +/** + * This class will be moved to androidx library. There shouldn't be any dependency outside + * this package. + */ +public class BaseIconFactory implements AutoCloseable { + + private static final String TAG = "BaseIconFactory"; + private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; + static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + + private static final float ICON_BADGE_SCALE = 0.444f; + + private final Rect mOldBounds = new Rect(); + protected final Context mContext; + private final Canvas mCanvas; + private final PackageManager mPm; + private final ColorExtractor mColorExtractor; + private boolean mDisableColorExtractor; + private boolean mBadgeOnLeft = false; + + protected final int mFillResIconDpi; + protected final int mIconBitmapSize; + + private IconNormalizer mNormalizer; + private ShadowGenerator mShadowGenerator; + private final boolean mShapeDetection; + + private Drawable mWrapperIcon; + private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; + private Bitmap mUserBadgeBitmap; + + private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private static final float PLACEHOLDER_TEXT_SIZE = 20f; + private static int PLACEHOLDER_BACKGROUND_COLOR = Color.rgb(240, 240, 240); + + protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, + boolean shapeDetection) { + mContext = context.getApplicationContext(); + mShapeDetection = shapeDetection; + mFillResIconDpi = fillResIconDpi; + mIconBitmapSize = iconBitmapSize; + + mPm = mContext.getPackageManager(); + mColorExtractor = new ColorExtractor(); + + mCanvas = new Canvas(); + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG)); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setColor(PLACEHOLDER_BACKGROUND_COLOR); + mTextPaint.setTextSize(context.getResources().getDisplayMetrics().density * + PLACEHOLDER_TEXT_SIZE); + clear(); + } + + public BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) { + this(context, fillResIconDpi, iconBitmapSize, false); + } + + protected void clear() { + mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; + mDisableColorExtractor = false; + mBadgeOnLeft = false; + } + + public ShadowGenerator getShadowGenerator() { + if (mShadowGenerator == null) { + mShadowGenerator = new ShadowGenerator(mIconBitmapSize); + } + return mShadowGenerator; + } + + public IconNormalizer getNormalizer() { + if (mNormalizer == null) { + mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection); + } + return mNormalizer; + } + + @SuppressWarnings("deprecation") + public BitmapInfo createIconBitmap(Intent.ShortcutIconResource iconRes) { + try { + Resources resources = mPm.getResourcesForApplication(iconRes.packageName); + if (resources != null) { + final int id = resources.getIdentifier(iconRes.resourceName, null, null); + // do not stamp old legacy shortcuts as the app may have already forgotten about it + return createBadgedIconBitmap( + resources.getDrawableForDensity(id, mFillResIconDpi), + Process.myUserHandle() /* only available on primary user */, + false /* do not apply legacy treatment */); + } + } catch (Exception e) { + // Icon not found. + } + return null; + } + + /** + * 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 + */ + public BitmapInfo createIconBitmap(String placeholder, int color) { + if (!ATLEAST_OREO) return null; + + Bitmap placeholderBitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize, + Bitmap.Config.ARGB_8888); + mTextPaint.setColor(color); + Canvas canvas = new Canvas(placeholderBitmap); + canvas.drawText(placeholder, mIconBitmapSize / 2, mIconBitmapSize * 5 / 8, mTextPaint); + AdaptiveIconDrawable drawable = new AdaptiveIconDrawable( + new ColorDrawable(PLACEHOLDER_BACKGROUND_COLOR), + new BitmapDrawable(mContext.getResources(), placeholderBitmap)); + Bitmap icon = createIconBitmap(drawable, 1f); + return BitmapInfo.of(icon, extractColor(icon)); + } + + public BitmapInfo createIconBitmap(Bitmap icon) { + if (mIconBitmapSize != icon.getWidth() || mIconBitmapSize != icon.getHeight()) { + icon = createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f); + } + + return BitmapInfo.of(icon, extractColor(icon)); + } + + /** + * Creates an icon from the bitmap cropped to the current device icon shape + */ + public BitmapInfo createShapedIconBitmap(Bitmap icon, UserHandle user) { + Drawable d = new FixedSizeBitmapDrawable(icon); + if (ATLEAST_OREO) { + float inset = AdaptiveIconDrawable.getExtraInsetFraction(); + inset = inset / (1 + 2 * inset); + d = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), + new InsetDrawable(d, inset, inset, inset, inset)); + } + return createBadgedIconBitmap(d, user, true); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + boolean shrinkNonAdaptiveIcons) { + return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, false, null); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk) { + return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk, boolean isInstantApp) { + return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk, boolean isInstantApp, float[] scale) { + boolean shrinkNonAdaptiveIcons = ATLEAST_P || + (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); + return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, scale); + } + + public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) { + boolean shrinkNonAdaptiveIcons = ATLEAST_P || + (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); + return createScaledBitmapWithoutShadow(icon, shrinkNonAdaptiveIcons); + } + + /** + * 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 user info can be used for a badge + * @param shrinkNonAdaptiveIcons {@code true} if non adaptive icons should be treated + * @param isInstantApp info can be used for a badge + * @param scale returns the scale result from normalization + * @return a bitmap suitable for disaplaying as an icon at various system UIs. + */ + public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, UserHandle user, + boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) { + if (scale == null) { + scale = new float[1]; + } + icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale); + Bitmap bitmap = createIconBitmap(icon, scale[0]); + if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { + mCanvas.setBitmap(bitmap); + getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); + mCanvas.setBitmap(null); + } + + if (isInstantApp) { + badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge)); + } + if (user != null) { + BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); + Drawable badged = mPm.getUserBadgedIcon(drawable, user); + if (badged instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) badged).getBitmap(); + } else { + bitmap = createIconBitmap(badged, 1f); + } + } + int color = extractColor(bitmap); + return icon instanceof BitmapInfo.Extender + ? ((BitmapInfo.Extender) icon).getExtendedInfo(bitmap, color, this, scale[0], user) + : BitmapInfo.of(bitmap, color); + } + + public Bitmap getUserBadgeBitmap(UserHandle user) { + if (mUserBadgeBitmap == null) { + Bitmap bitmap = Bitmap.createBitmap( + mIconBitmapSize, mIconBitmapSize, Bitmap.Config.ARGB_8888); + Drawable badgedDrawable = mPm.getUserBadgedIcon( + new FixedSizeBitmapDrawable(bitmap), user); + if (badgedDrawable instanceof BitmapDrawable) { + mUserBadgeBitmap = ((BitmapDrawable) badgedDrawable).getBitmap(); + } else { + badgedDrawable.setBounds(0, 0, mIconBitmapSize, mIconBitmapSize); + mUserBadgeBitmap = BitmapRenderer.createSoftwareBitmap( + mIconBitmapSize, mIconBitmapSize, badgedDrawable::draw); + } + } + return mUserBadgeBitmap; + } + + public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) { + RectF iconBounds = new RectF(); + float[] scale = new float[1]; + icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, iconBounds, scale); + return createIconBitmap(icon, + Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds))); + } + + /** + * Switches badging to left/right + */ + public void setBadgeOnLeft(boolean badgeOnLeft) { + mBadgeOnLeft = badgeOnLeft; + } + + /** + * Sets the background color used for wrapped adaptive icon + */ + public void setWrapperBackgroundColor(int color) { + mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color; + } + + /** + * Disables the dominant color extraction for all icons loaded. + */ + public void disableColorExtraction() { + mDisableColorExtractor = true; + } + + private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon, + boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) { + if (icon == null) { + return null; + } + float scale = 1f; + + if (shrinkNonAdaptiveIcons && ATLEAST_OREO) { + if (mWrapperIcon == null) { + mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper) + .mutate(); + } + AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon; + dr.setBounds(0, 0, 1, 1); + boolean[] outShape = new boolean[1]; + scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape); + if (!(icon instanceof AdaptiveIconDrawable) && !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); + } + } else { + scale = getNormalizer().getScale(icon, outIconBounds, null, null); + } + + outScale[0] = scale; + return icon; + } + + /** + * Adds the {@param badge} on top of {@param target} using the badge dimensions. + */ + public void badgeWithDrawable(Bitmap target, Drawable badge) { + mCanvas.setBitmap(target); + badgeWithDrawable(mCanvas, badge); + mCanvas.setBitmap(null); + } + + /** + * Adds the {@param badge} on top of {@param target} using the badge dimensions. + */ + public void badgeWithDrawable(Canvas target, Drawable badge) { + int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize); + if (mBadgeOnLeft) { + badge.setBounds(0, mIconBitmapSize - badgeSize, badgeSize, mIconBitmapSize); + } else { + badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize, + mIconBitmapSize, mIconBitmapSize); + } + badge.draw(target); + } + + private Bitmap createIconBitmap(Drawable icon, float scale) { + return createIconBitmap(icon, scale, mIconBitmapSize); + } + + /** + * @param icon drawable that should be flattened to a bitmap + * @param scale the scale to apply before drawing {@param icon} on the canvas + */ + public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) { + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + if (icon == null) { + return bitmap; + } + mCanvas.setBitmap(bitmap); + mOldBounds.set(icon.getBounds()); + + if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { + int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), + Math.round(size * (1 - scale) / 2 )); + icon.setBounds(offset, offset, size - offset, size - offset); + if (icon instanceof BitmapInfo.Extender) { + ((Extender) icon).drawForPersistence(mCanvas); + } else { + icon.draw(mCanvas); + } + } else { + if (icon instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap b = bitmapDrawable.getBitmap(); + if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics()); + } + } + int width = size; + int height = size; + + int intrinsicWidth = icon.getIntrinsicWidth(); + int intrinsicHeight = icon.getIntrinsicHeight(); + if (intrinsicWidth > 0 && intrinsicHeight > 0) { + // Scale the icon proportionally to the icon dimensions + final float ratio = (float) intrinsicWidth / intrinsicHeight; + if (intrinsicWidth > intrinsicHeight) { + height = (int) (width / ratio); + } else if (intrinsicHeight > intrinsicWidth) { + width = (int) (height * ratio); + } + } + final int left = (size - width) / 2; + final int top = (size - height) / 2; + icon.setBounds(left, top, left + width, top + height); + mCanvas.save(); + mCanvas.scale(scale, scale, size / 2, size / 2); + icon.draw(mCanvas); + mCanvas.restore(); + + } + icon.setBounds(mOldBounds); + mCanvas.setBitmap(null); + return bitmap; + } + + @Override + public void close() { + clear(); + } + + public BitmapInfo makeDefaultIcon(UserHandle user) { + return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi), + user, Build.VERSION.SDK_INT); + } + + public static Drawable getFullResDefaultActivityIcon(int iconDpi) { + return Resources.getSystem().getDrawableForDensity( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon, + iconDpi); + } + + /** + * Badges the provided source with the badge info + */ + public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) { + Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> { + getShadowGenerator().recreateIcon(source, c); + badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon)); + }); + return BitmapInfo.of(icon, badgeInfo.color); + } + + private int extractColor(Bitmap bitmap) { + return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap); + } + + /** + * Returns the correct badge size given an icon size + */ + public static int getBadgeSizeForIconSize(int iconSize) { + return (int) (ICON_BADGE_SCALE * iconSize); + } + + /** + * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. + * This allows the badging to be done based on the action bitmap size rather than + * the scaled bitmap size. + */ + private static class FixedSizeBitmapDrawable extends BitmapDrawable { + + public FixedSizeBitmapDrawable(Bitmap bitmap) { + super(null, bitmap); + } + + @Override + public int getIntrinsicHeight() { + return getBitmap().getWidth(); + } + + @Override + public int getIntrinsicWidth() { + return getBitmap().getWidth(); + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..06b39b8f10bb7b3084d11c4f97c4a9e474824fe7 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java @@ -0,0 +1,163 @@ +/* + * 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. + */ +package com.android.launcher3.icons; + +import static com.android.launcher3.icons.GraphicsUtils.getExpectedBitmapSize; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.UserHandle; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.icons.ThemedIconDrawable.ThemedBitmapInfo; +import com.android.launcher3.icons.cache.BaseIconCache; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class BitmapInfo { + + public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8); + public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON); + + public static final String TAG = "BitmapInfo"; + + protected static final byte TYPE_DEFAULT = 1; + protected static final byte TYPE_THEMED = 2; + + public final Bitmap icon; + public final int color; + + public BitmapInfo(Bitmap icon, int color) { + this.icon = icon; + this.color = color; + } + + /** + * Ideally icon should not be null, except in cases when generating hardware bitmap failed + */ + public final boolean isNullOrLowRes() { + return icon == null || icon == LOW_RES_ICON; + } + + public final boolean isLowRes() { + return LOW_RES_ICON == icon; + } + + /** + * Returns a serialized version of BitmapInfo + */ + @Nullable + public byte[] toByteArray() { + if (isNullOrLowRes()) { + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(getExpectedBitmapSize(icon) + 1); + try { + out.write(TYPE_DEFAULT); + icon.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + + /** + * Returns a new icon based on the theme of the context + */ + public FastBitmapDrawable newThemedIcon(Context context) { + return newIcon(context); + } + + /** + * Creates a drawable for the provided BitmapInfo + */ + public FastBitmapDrawable newIcon(Context context) { + FastBitmapDrawable drawable = isLowRes() + ? new PlaceHolderIconDrawable(this, context) + : new FastBitmapDrawable(this); + drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return drawable; + } + + /** + * Returns a BitmapInfo previously serialized using {@link #toByteArray()}; + */ + @NonNull + public static BitmapInfo fromByteArray(byte[] data, int color, UserHandle user, + BaseIconCache iconCache, Context context) { + if (data == null) { + return null; + } + BitmapFactory.Options decodeOptions; + if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + decodeOptions = new BitmapFactory.Options(); + decodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE; + } else { + decodeOptions = null; + } + if (data[0] == TYPE_DEFAULT) { + return BitmapInfo.of( + BitmapFactory.decodeByteArray(data, 1, data.length - 1, decodeOptions), + color); + } else if (data[0] == TYPE_THEMED) { + return ThemedBitmapInfo.decode(data, color, decodeOptions, user, iconCache, context); + } else { + return null; + } + } + + public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) { + return of(bitmap, 0); + } + + public static BitmapInfo of(@NonNull Bitmap bitmap, int color) { + return new BitmapInfo(bitmap, color); + } + + /** + * Interface to be implemented by drawables to provide a custom BitmapInfo + */ + public interface Extender { + + /** + * Called for creating a custom BitmapInfo + */ + BitmapInfo getExtendedInfo(Bitmap bitmap, int color, + BaseIconFactory iconFactory, float normalizationScale, UserHandle user); + + /** + * Called to draw the UI independent of any runtime configurations like time or theme + */ + void drawForPersistence(Canvas canvas); + + /** + * Returns a new icon with theme applied + */ + Drawable getThemedDrawable(Context context); + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..5751ed95cb644291fa9233655e5a08d145785895 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java @@ -0,0 +1,70 @@ +/* + * 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. + */ +package com.android.launcher3.icons; + +import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Picture; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Build.VERSION_CODES; + +/** + * Interface representing a bitmap draw operation. + */ +public interface BitmapRenderer { + + boolean USE_HARDWARE_BITMAP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + + static Bitmap createSoftwareBitmap(int width, int height, BitmapRenderer renderer) { + GraphicsUtils.noteNewBitmapCreated(); + Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + renderer.draw(new Canvas(result)); + return result; + } + + @TargetApi(Build.VERSION_CODES.P) + static Bitmap createHardwareBitmap(int width, int height, BitmapRenderer renderer) { + if (!USE_HARDWARE_BITMAP) { + return createSoftwareBitmap(width, height, renderer); + } + + GraphicsUtils.noteNewBitmapCreated(); + Picture picture = new Picture(); + renderer.draw(picture.beginRecording(width, height)); + picture.endRecording(); + return Bitmap.createBitmap(picture); + } + + /** + * Returns a bitmap from subset of the source bitmap. The new bitmap may be the + * same object as source, or a copy may have been made. + */ + static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) { + if (Build.VERSION.SDK_INT >= VERSION_CODES.O && source.getConfig() == Config.HARDWARE) { + return createHardwareBitmap(width, height, c -> c.drawBitmap(source, + new Rect(x, y, x + width, y + height), new RectF(0, 0, width, height), null)); + } else { + GraphicsUtils.noteNewBitmapCreated(); + return Bitmap.createBitmap(source, x, y, width, height); + } + } + + void draw(Canvas out); +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a7894c9918506a18d7a13853b1635f17f181eb12 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2019 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 static com.android.launcher3.icons.ThemedIconDrawable.getColors; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Process; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Log; +import android.util.TypedValue; + +import androidx.annotation.Nullable; + +import com.android.launcher3.icons.ThemedIconDrawable.ThemeData; + +import java.util.Calendar; +import java.util.concurrent.TimeUnit; +import java.util.function.IntFunction; + +/** + * Wrapper over {@link AdaptiveIconDrawable} to intercept icon flattening logic for dynamic + * clock icons + */ +@TargetApi(Build.VERSION_CODES.O) +public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender { + + private static final String TAG = "ClockDrawableWrapper"; + + private static final boolean DISABLE_SECONDS = true; + + // Time after which the clock icon should check for an update. The actual invalidate + // will only happen in case of any change. + public static final long TICK_MS = DISABLE_SECONDS ? TimeUnit.MINUTES.toMillis(1) : 200L; + + private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; + private static final String ROUND_ICON_METADATA_KEY = LAUNCHER_PACKAGE + + ".LEVEL_PER_TICK_ICON_ROUND"; + private static final String HOUR_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + ".HOUR_LAYER_INDEX"; + private static final String MINUTE_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + + ".MINUTE_LAYER_INDEX"; + private static final String SECOND_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + + ".SECOND_LAYER_INDEX"; + private static final String DEFAULT_HOUR_METADATA_KEY = LAUNCHER_PACKAGE + + ".DEFAULT_HOUR"; + private static final String DEFAULT_MINUTE_METADATA_KEY = LAUNCHER_PACKAGE + + ".DEFAULT_MINUTE"; + private static final String DEFAULT_SECOND_METADATA_KEY = LAUNCHER_PACKAGE + + ".DEFAULT_SECOND"; + + /* Number of levels to jump per second for the second hand */ + private static final int LEVELS_PER_SECOND = 10; + + public static final int INVALID_VALUE = -1; + + private final AnimationInfo mAnimationInfo = new AnimationInfo(); + private int mTargetSdkVersion; + protected ThemeData mThemeData; + + public ClockDrawableWrapper(AdaptiveIconDrawable base) { + super(base.getBackground(), base.getForeground()); + } + + /** + * Loads and returns the wrapper from the provided package, or returns null + * if it is unable to load. + */ + public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) { + try { + PackageManager pm = context.getPackageManager(); + ApplicationInfo appInfo = pm.getApplicationInfo(pkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); + Resources res = pm.getResourcesForApplication(appInfo); + return forExtras(appInfo, appInfo.metaData, + resId -> res.getDrawableForDensity(resId, iconDpi)); + } catch (Exception e) { + Log.d(TAG, "Unable to load clock drawable info", e); + } + return null; + } + + private static ClockDrawableWrapper fromThemeData(Context context, ThemeData themeData) { + try { + TypedArray ta = themeData.mResources.obtainTypedArray(themeData.mResID); + int count = ta.length(); + Bundle extras = new Bundle(); + for (int i = 0; i < count; i += 2) { + TypedValue v = ta.peekValue(i + 1); + extras.putInt(ta.getString(i), v.type >= TypedValue.TYPE_FIRST_INT + && v.type <= TypedValue.TYPE_LAST_INT + ? v.data : v.resourceId); + } + ta.recycle(); + ClockDrawableWrapper drawable = ClockDrawableWrapper.forExtras( + context.getApplicationInfo(), extras, resId -> { + int[] colors = getColors(context); + Drawable bg = new ColorDrawable(colors[0]); + Drawable fg = themeData.mResources.getDrawable(resId).mutate(); + fg.setTint(colors[1]); + return new AdaptiveIconDrawable(bg, fg); + }); + if (drawable != null) { + return drawable; + } + } catch (Exception e) { + Log.e(TAG, "Error loading themed clock", e); + } + return null; + } + + private static ClockDrawableWrapper forExtras(ApplicationInfo appInfo, Bundle metadata, + IntFunction drawableProvider) { + if (metadata == null) { + return null; + } + int drawableId = metadata.getInt(ROUND_ICON_METADATA_KEY, 0); + if (drawableId == 0) { + return null; + } + + Drawable drawable = drawableProvider.apply(drawableId).mutate(); + if (!(drawable instanceof AdaptiveIconDrawable)) { + return null; + } + + ClockDrawableWrapper wrapper = + new ClockDrawableWrapper((AdaptiveIconDrawable) drawable); + wrapper.mTargetSdkVersion = appInfo.targetSdkVersion; + AnimationInfo info = wrapper.mAnimationInfo; + + info.baseDrawableState = drawable.getConstantState(); + + info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE); + info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE); + info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE); + + info.defaultHour = metadata.getInt(DEFAULT_HOUR_METADATA_KEY, 0); + info.defaultMinute = metadata.getInt(DEFAULT_MINUTE_METADATA_KEY, 0); + info.defaultSecond = metadata.getInt(DEFAULT_SECOND_METADATA_KEY, 0); + + LayerDrawable foreground = (LayerDrawable) wrapper.getForeground(); + int layerCount = foreground.getNumberOfLayers(); + if (info.hourLayerIndex < 0 || info.hourLayerIndex >= layerCount) { + info.hourLayerIndex = INVALID_VALUE; + } + if (info.minuteLayerIndex < 0 || info.minuteLayerIndex >= layerCount) { + info.minuteLayerIndex = INVALID_VALUE; + } + if (info.secondLayerIndex < 0 || info.secondLayerIndex >= layerCount) { + info.secondLayerIndex = INVALID_VALUE; + } else if (DISABLE_SECONDS) { + foreground.setDrawable(info.secondLayerIndex, null); + info.secondLayerIndex = INVALID_VALUE; + } + info.applyTime(Calendar.getInstance(), foreground); + return wrapper; + } + + @Override + public ClockBitmapInfo getExtendedInfo(Bitmap bitmap, int color, + BaseIconFactory iconFactory, float normalizationScale, UserHandle user) { + iconFactory.disableColorExtraction(); + AdaptiveIconDrawable background = new AdaptiveIconDrawable( + getBackground().getConstantState().newDrawable(), null); + BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background, + Process.myUserHandle(), mTargetSdkVersion, false); + + return new ClockBitmapInfo(bitmap, color, normalizationScale, + mAnimationInfo, bitmapInfo.icon, mThemeData); + } + + @Override + public void drawForPersistence(Canvas canvas) { + LayerDrawable foreground = (LayerDrawable) getForeground(); + resetLevel(foreground, mAnimationInfo.hourLayerIndex); + resetLevel(foreground, mAnimationInfo.minuteLayerIndex); + resetLevel(foreground, mAnimationInfo.secondLayerIndex); + draw(canvas); + mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground()); + } + + @Override + public Drawable getThemedDrawable(Context context) { + if (mThemeData != null) { + ClockDrawableWrapper drawable = fromThemeData(context, mThemeData); + return drawable == null ? this : drawable; + } + return this; + } + + private void resetLevel(LayerDrawable drawable, int index) { + if (index != INVALID_VALUE) { + drawable.getDrawable(index).setLevel(0); + } + } + + private static class AnimationInfo { + + public ConstantState baseDrawableState; + + public int hourLayerIndex; + public int minuteLayerIndex; + public int secondLayerIndex; + public int defaultHour; + public int defaultMinute; + public int defaultSecond; + + boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) { + time.setTimeInMillis(System.currentTimeMillis()); + + // We need to rotate by the difference from the default time if one is specified. + int convertedHour = (time.get(Calendar.HOUR) + (12 - defaultHour)) % 12; + int convertedMinute = (time.get(Calendar.MINUTE) + (60 - defaultMinute)) % 60; + int convertedSecond = (time.get(Calendar.SECOND) + (60 - defaultSecond)) % 60; + + boolean invalidate = false; + if (hourLayerIndex != INVALID_VALUE) { + final Drawable hour = foregroundDrawable.getDrawable(hourLayerIndex); + if (hour.setLevel(convertedHour * 60 + time.get(Calendar.MINUTE))) { + invalidate = true; + } + } + + if (minuteLayerIndex != INVALID_VALUE) { + final Drawable minute = foregroundDrawable.getDrawable(minuteLayerIndex); + if (minute.setLevel(time.get(Calendar.HOUR) * 60 + convertedMinute)) { + invalidate = true; + } + } + + if (secondLayerIndex != INVALID_VALUE) { + final Drawable second = foregroundDrawable.getDrawable(secondLayerIndex); + if (second.setLevel(convertedSecond * LEVELS_PER_SECOND)) { + invalidate = true; + } + } + + return invalidate; + } + } + + static class ClockBitmapInfo extends BitmapInfo { + + public final float scale; + public final int offset; + public final AnimationInfo animInfo; + public final Bitmap mFlattenedBackground; + + public final ThemeData themeData; + public final ColorFilter bgFilter; + + ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo, + Bitmap background, ThemeData themeData) { + this(icon, color, scale, animInfo, background, themeData, null); + } + + ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo, + Bitmap background, ThemeData themeData, ColorFilter bgFilter) { + super(icon, color); + this.scale = scale; + this.animInfo = animInfo; + this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth()); + this.mFlattenedBackground = background; + this.themeData = themeData; + this.bgFilter = bgFilter; + } + + @Override + public FastBitmapDrawable newThemedIcon(Context context) { + if (themeData != null) { + ClockDrawableWrapper wrapper = fromThemeData(context, themeData); + if (wrapper != null) { + int[] colors = getColors(context); + ColorFilter bgFilter = new PorterDuffColorFilter(colors[0], Mode.SRC_ATOP); + return new ClockBitmapInfo(icon, colors[1], scale, + wrapper.mAnimationInfo, mFlattenedBackground, themeData, bgFilter) + .newIcon(context); + } + } + return super.newThemedIcon(context); + } + + @Override + public FastBitmapDrawable newIcon(Context context) { + ClockIconDrawable d = new ClockIconDrawable(this); + d.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return d; + } + + @Nullable + @Override + public byte[] toByteArray() { + return null; + } + + void drawBackground(Canvas canvas, Rect bounds, Paint paint) { + // draw the background that is already flattened to a bitmap + ColorFilter oldFilter = paint.getColorFilter(); + if (bgFilter != null) { + paint.setColorFilter(bgFilter); + } + canvas.drawBitmap(mFlattenedBackground, null, bounds, paint); + paint.setColorFilter(oldFilter); + } + } + + private static class ClockIconDrawable extends FastBitmapDrawable implements Runnable { + + private final Calendar mTime = Calendar.getInstance(); + + private final ClockBitmapInfo mInfo; + + private final AdaptiveIconDrawable mFullDrawable; + private final LayerDrawable mForeground; + + ClockIconDrawable(ClockBitmapInfo clockInfo) { + super(clockInfo); + + mInfo = clockInfo; + mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState + .newDrawable().mutate(); + mForeground = (LayerDrawable) mFullDrawable.getForeground(); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mFullDrawable.setBounds(bounds); + } + + @Override + public void drawInternal(Canvas canvas, Rect bounds) { + if (mInfo == null) { + super.drawInternal(canvas, bounds); + return; + } + mInfo.drawBackground(canvas, bounds, mPaint); + + // prepare and draw the foreground + mInfo.animInfo.applyTime(mTime, mForeground); + + canvas.scale(mInfo.scale, mInfo.scale, + bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset); + canvas.clipPath(mFullDrawable.getIconMask()); + mForeground.draw(canvas); + + reschedule(); + } + + @Override + public boolean isThemed() { + return mInfo.bgFilter != null; + } + + @Override + protected void updateFilter() { + super.updateFilter(); + mFullDrawable.setColorFilter(mPaint.getColorFilter()); + } + + @Override + public void run() { + if (mInfo.animInfo.applyTime(mTime, mForeground)) { + invalidateSelf(); + } else { + reschedule(); + } + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean result = super.setVisible(visible, restart); + if (visible) { + reschedule(); + } else { + unscheduleSelf(this); + } + return result; + } + + private void reschedule() { + if (!isVisible()) { + return; + } + + unscheduleSelf(this); + final long upTime = SystemClock.uptimeMillis(); + final long step = TICK_MS; /* tick every 200 ms */ + scheduleSelf(this, upTime - ((upTime % step)) + step); + } + + @Override + public ConstantState getConstantState() { + return new ClockConstantState(mInfo, isDisabled()); + } + + private static class ClockConstantState extends FastBitmapConstantState { + + private final ClockBitmapInfo mInfo; + + ClockConstantState(ClockBitmapInfo info, boolean isDisabled) { + super(info.icon, info.color, isDisabled); + mInfo = info; + } + + @Override + public FastBitmapDrawable newDrawable() { + ClockIconDrawable drawable = new ClockIconDrawable(mInfo); + drawable.setIsDisabled(mIsDisabled); + return drawable; + } + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..87bda825cc6d4f4d669d3356be57e7895ca40a8a --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java @@ -0,0 +1,127 @@ +/* + * 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. + */ +package com.android.launcher3.icons; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.util.SparseArray; +import java.util.Arrays; + +/** + * Utility class for extracting colors from a bitmap. + */ +public class ColorExtractor { + + private final int NUM_SAMPLES = 20; + private final float[] mTmpHsv = new float[3]; + private final float[] mTmpHueScoreHistogram = new float[360]; + private final int[] mTmpPixels = new int[NUM_SAMPLES]; + private final SparseArray mTmpRgbScores = new SparseArray<>(); + + /** + * This picks a dominant color, looking for high-saturation, high-value, repeated hues. + * @param bitmap The bitmap to scan + */ + public int findDominantColorByHue(Bitmap bitmap) { + return findDominantColorByHue(bitmap, NUM_SAMPLES); + } + + /** + * This picks a dominant color, looking for high-saturation, high-value, repeated hues. + * @param bitmap The bitmap to scan + */ + public int findDominantColorByHue(Bitmap bitmap, int samples) { + final int height = bitmap.getHeight(); + final int width = bitmap.getWidth(); + int sampleStride = (int) Math.sqrt((height * width) / samples); + if (sampleStride < 1) { + sampleStride = 1; + } + + // This is an out-param, for getting the hsv values for an rgb + float[] hsv = mTmpHsv; + Arrays.fill(hsv, 0); + + // First get the best hue, by creating a histogram over 360 hue buckets, + // where each pixel contributes a score weighted by saturation, value, and alpha. + float[] hueScoreHistogram = mTmpHueScoreHistogram; + Arrays.fill(hueScoreHistogram, 0); + float highScore = -1; + int bestHue = -1; + + int[] pixels = mTmpPixels; + Arrays.fill(pixels, 0); + int pixelCount = 0; + + for (int y = 0; y < height; y += sampleStride) { + for (int x = 0; x < width; x += sampleStride) { + int argb = bitmap.getPixel(x, y); + int alpha = 0xFF & (argb >> 24); + if (alpha < 0x80) { + // Drop mostly-transparent pixels. + continue; + } + // Remove the alpha channel. + int rgb = argb | 0xFF000000; + Color.colorToHSV(rgb, hsv); + // Bucket colors by the 360 integer hues. + int hue = (int) hsv[0]; + if (hue < 0 || hue >= hueScoreHistogram.length) { + // Defensively avoid array bounds violations. + continue; + } + if (pixelCount < samples) { + pixels[pixelCount++] = rgb; + } + float score = hsv[1] * hsv[2]; + hueScoreHistogram[hue] += score; + if (hueScoreHistogram[hue] > highScore) { + highScore = hueScoreHistogram[hue]; + bestHue = hue; + } + } + } + + SparseArray rgbScores = mTmpRgbScores; + rgbScores.clear(); + int bestColor = 0xff000000; + highScore = -1; + // Go back over the RGB colors that match the winning hue, + // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets. + // The highest-scoring RGB color wins. + for (int i = 0; i < pixelCount; i++) { + int rgb = pixels[i]; + Color.colorToHSV(rgb, hsv); + int hue = (int) hsv[0]; + if (hue == bestHue) { + float s = hsv[1]; + float v = hsv[2]; + int bucket = (int) (s * 100) + (int) (v * 10000); + // Score by cumulative saturation * value. + float score = s * v; + Float oldTotal = rgbScores.get(bucket); + float newTotal = oldTotal == null ? score : oldTotal + score; + rgbScores.put(bucket, newTotal); + if (newTotal > highScore) { + highScore = newTotal; + // All the colors in the winning bucket are very similar. Last in wins. + bestColor = rgb; + } + } + } + return bestColor; + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..97a0fd3ffca69988b42fc25456a39c15b715804f --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -0,0 +1,143 @@ +/* + * 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. + */ + +package com.android.launcher3.icons; + +import static android.graphics.Paint.ANTI_ALIAS_FLAG; +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.Log; +import android.view.ViewDebug; + +/** + * Used to draw a notification dot on top of an icon. + */ +public class DotRenderer { + + private static final String TAG = "DotRenderer"; + + // The dot size is defined as a percentage of the app icon size. + private static final float SIZE_PERCENTAGE = 0.228f; + + private final float mCircleRadius; + private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); + + private final Bitmap mBackgroundWithShadow; + private final float mBitmapOffset; + + // Stores the center x and y position as a percentage (0 to 1) of the icon size + private final float[] mRightDotPosition; + private final float[] mLeftDotPosition; + + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { + int size = Math.round(SIZE_PERCENTAGE * iconSizePx); + ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT); + builder.ambientShadowAlpha = 88; + mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size); + mCircleRadius = builder.radius; + + mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width. + + // Find the points on the path that are closest to the top left and right corners. + mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1); + mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1); + } + + private static float[] getPathPoint(Path path, float size, float direction) { + float halfSize = size / 2; + // Small delta so that we don't get a zero size triangle + float delta = 1; + + float x = halfSize + direction * halfSize; + Path trianglePath = new Path(); + trianglePath.moveTo(halfSize, halfSize); + trianglePath.lineTo(x + delta * direction, 0); + trianglePath.lineTo(x, -delta); + trianglePath.close(); + + trianglePath.op(path, Path.Op.INTERSECT); + float[] pos = new float[2]; + new PathMeasure(trianglePath, false).getPosTan(0, pos, null); + + pos[0] = pos[0] / size; + pos[1] = pos[1] / size; + return pos; + } + + public float[] getLeftDotPosition() { + return mLeftDotPosition; + } + + public float[] getRightDotPosition() { + return mRightDotPosition; + } + + /** + * Draw a circle on top of the canvas according to the given params. + */ + public void draw(Canvas canvas, DrawParams params) { + if (params == null) { + Log.e(TAG, "Invalid null argument(s) passed in call to draw."); + return; + } + canvas.save(); + + Rect iconBounds = params.iconBounds; + float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition; + float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0]; + float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1]; + + // Ensure dot fits entirely in canvas clip bounds. + Rect canvasBounds = canvas.getClipBounds(); + float offsetX = params.leftAlign + ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset)) + : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset)); + float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset)); + + // We draw the dot relative to its center. + canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); + canvas.scale(params.scale, params.scale); + + mCirclePaint.setColor(Color.BLACK); + canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); + mCirclePaint.setColor(params.color); + canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + canvas.restore(); + } + + public static class DrawParams { + /** The color (possibly based on the icon) to use for the dot. */ + @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) + public int color; + /** The bounds of the icon that the dot is drawn on top of. */ + @ViewDebug.ExportedProperty(category = "notification dot") + public Rect iconBounds = new Rect(); + /** The progress of the animation, from 0 to 1. */ + @ViewDebug.ExportedProperty(category = "notification dot") + public float scale; + /** Whether the dot should align to the top left of the icon rather than the top right. */ + @ViewDebug.ExportedProperty(category = "notification dot") + public boolean leftAlign; + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java new file mode 100644 index 0000000000000000000000000000000000000000..4aa284618c81bec25d038315344c1c1143156432 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2008 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.animation.ObjectAnimator; +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.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.Property; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import androidx.annotation.Nullable; + +public class FastBitmapDrawable extends Drawable { + + private static final Interpolator ACCEL = new AccelerateInterpolator(); + private static final Interpolator DEACCEL = new DecelerateInterpolator(); + + private static final float PRESSED_SCALE = 1.1f; + + private static final float DISABLED_DESATURATION = 1f; + private static final float DISABLED_BRIGHTNESS = 0.5f; + + public static final int CLICK_FEEDBACK_DURATION = 200; + + private static ColorFilter sDisabledFColorFilter; + + protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); + protected Bitmap mBitmap; + protected final int mIconColor; + + @Nullable private ColorFilter mColorFilter; + + private boolean mIsPressed; + protected boolean mIsDisabled; + float mDisabledAlpha = 1f; + + // Animator and properties for the fast bitmap drawable's scale + private static final Property SCALE + = new Property(Float.TYPE, "scale") { + @Override + public Float get(FastBitmapDrawable fastBitmapDrawable) { + return fastBitmapDrawable.mScale; + } + + @Override + public void set(FastBitmapDrawable fastBitmapDrawable, Float value) { + fastBitmapDrawable.mScale = value; + fastBitmapDrawable.invalidateSelf(); + } + }; + private ObjectAnimator mScaleAnimation; + private float mScale = 1; + + private int mAlpha = 255; + + public FastBitmapDrawable(Bitmap b) { + this(b, Color.TRANSPARENT); + } + + public FastBitmapDrawable(BitmapInfo info) { + this(info.icon, info.color); + } + + protected FastBitmapDrawable(Bitmap b, int iconColor) { + this(b, iconColor, false); + } + + protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) { + mBitmap = b; + mIconColor = iconColor; + setFilterBitmap(true); + setIsDisabled(isDisabled); + } + + @Override + public final void draw(Canvas canvas) { + if (mScale != 1f) { + int count = canvas.save(); + Rect bounds = getBounds(); + canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY()); + drawInternal(canvas, bounds); + canvas.restoreToCount(count); + } else { + drawInternal(canvas, getBounds()); + } + } + + protected void drawInternal(Canvas canvas, Rect bounds) { + canvas.drawBitmap(mBitmap, null, bounds, mPaint); + } + + /** + * Returns the primary icon color + */ + public int getIconColor() { + return mIconColor; + } + + /** + * Returns if this represents a themed icon + */ + public boolean isThemed() { + return false; + } + + @Override + public void setColorFilter(ColorFilter cf) { + mColorFilter = cf; + updateFilter(); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + mPaint.setAlpha(alpha); + invalidateSelf(); + } + } + + @Override + public void setFilterBitmap(boolean filterBitmap) { + mPaint.setFilterBitmap(filterBitmap); + mPaint.setAntiAlias(filterBitmap); + } + + @Override + public int getAlpha() { + return mAlpha; + } + + public void resetScale() { + if (mScaleAnimation != null) { + mScaleAnimation.cancel(); + mScaleAnimation = null; + } + mScale = 1; + invalidateSelf(); + } + + public float getAnimatedScale() { + return mScaleAnimation == null ? 1 : mScale; + } + + @Override + public int getIntrinsicWidth() { + return mBitmap.getWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mBitmap.getHeight(); + } + + @Override + public int getMinimumWidth() { + return getBounds().width(); + } + + @Override + public int getMinimumHeight() { + return getBounds().height(); + } + + @Override + public boolean isStateful() { + return true; + } + + @Override + public ColorFilter getColorFilter() { + return mPaint.getColorFilter(); + } + + @Override + protected boolean onStateChange(int[] state) { + boolean isPressed = false; + for (int s : state) { + if (s == android.R.attr.state_pressed) { + isPressed = true; + break; + } + } + if (mIsPressed != isPressed) { + mIsPressed = isPressed; + + 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 { + if (isVisible()) { + mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f); + mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION); + mScaleAnimation.setInterpolator(DEACCEL); + mScaleAnimation.start(); + } else { + mScale = 1f; + invalidateSelf(); + } + } + return true; + } + return false; + } + + public void setIsDisabled(boolean isDisabled) { + if (mIsDisabled != isDisabled) { + mIsDisabled = isDisabled; + updateFilter(); + } + } + + protected boolean isDisabled() { + return mIsDisabled; + } + + private ColorFilter getDisabledColorFilter() { + if (sDisabledFColorFilter == null) { + sDisabledFColorFilter = getDisabledFColorFilter(mDisabledAlpha); + } + return sDisabledFColorFilter; + } + + /** + * Updates the paint to reflect the current brightness and saturation. + */ + protected void updateFilter() { + mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter); + invalidateSelf(); + } + + @Override + public ConstantState getConstantState() { + return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled); + } + + public static ColorFilter getDisabledFColorFilter(float disabledAlpha) { + ColorMatrix tempBrightnessMatrix = new ColorMatrix(); + ColorMatrix tempFilterMatrix = new ColorMatrix(); + + tempFilterMatrix.setSaturation(1f - DISABLED_DESATURATION); + float scale = 1 - DISABLED_BRIGHTNESS; + int brightnessI = (int) (255 * DISABLED_BRIGHTNESS); + float[] mat = tempBrightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = brightnessI; + mat[9] = brightnessI; + mat[14] = brightnessI; + mat[18] = disabledAlpha; + tempFilterMatrix.preConcat(tempBrightnessMatrix); + return new ColorMatrixColorFilter(tempBrightnessMatrix); + } + + protected static class FastBitmapConstantState extends ConstantState { + protected final Bitmap mBitmap; + protected final int mIconColor; + protected final boolean mIsDisabled; + + public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) { + mBitmap = bitmap; + mIconColor = color; + mIsDisabled = isDisabled; + } + + @Override + public FastBitmapDrawable newDrawable() { + return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled); + } + + @Override + public int getChangingConfigurations() { + return 0; + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java new file mode 100644 index 0000000000000000000000000000000000000000..516965ec2ba2fb6f30723e030a9b4f58fed33fe4 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java @@ -0,0 +1,53 @@ +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; + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..17b001642698c07583aa49538db0f9e8697f5b47 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java @@ -0,0 +1,127 @@ +/* + * 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; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.RegionIterator; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; +import android.util.Log; + +import androidx.annotation.ColorInt; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class GraphicsUtils { + + private static final String TAG = "GraphicsUtils"; + + public static Runnable sOnNewBitmapRunnable = () -> { }; + + /** + * Set the alpha component of {@code color} to be {@code alpha}. Unlike the support lib version, + * it bounds the alpha in valid range instead of throwing an exception to allow for safer + * interpolation of color animations + */ + @ColorInt + public static int setColorAlphaBound(int color, int alpha) { + if (alpha < 0) { + alpha = 0; + } else if (alpha > 255) { + alpha = 255; + } + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * Compresses the bitmap to a byte array for serialization. + */ + public static byte[] flattenBitmap(Bitmap bitmap) { + ByteArrayOutputStream out = new ByteArrayOutputStream(getExpectedBitmapSize(bitmap)); + try { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + + /** + * Try go guesstimate how much space the icon will take when serialized to avoid unnecessary + * allocations/copies during the write (4 bytes per pixel). + */ + static int getExpectedBitmapSize(Bitmap bitmap) { + return bitmap.getWidth() * bitmap.getHeight() * 4; + } + + public static int getArea(Region r) { + RegionIterator itr = new RegionIterator(r); + int area = 0; + Rect tempRect = new Rect(); + while (itr.next(tempRect)) { + area += tempRect.width() * tempRect.height(); + } + return area; + } + + /** + * Utility method to track new bitmap creation + */ + public static void noteNewBitmapCreated() { + sOnNewBitmapRunnable.run(); + } + + + /** + * Returns the default path to be used by an icon + */ + public static Path getShapePath(int size) { + AdaptiveIconDrawable drawable = new AdaptiveIconDrawable( + new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK)); + drawable.setBounds(0, 0, size, size); + return new Path(drawable.getIconMask()); + } + + /** + * Returns the color associated with the attribute + */ + public static int getAttrColor(Context context, int attr) { + TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + return colorAccent; + } + + /** + * Returns the alpha corresponding to the theme attribute {@param attr} + */ + public static float getFloat(Context context, int attr, float defValue) { + TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); + float value = ta.getFloat(0, defValue); + ta.recycle(); + return value; + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java new file mode 100644 index 0000000000000000000000000000000000000000..de39e79fec02bc13129a437465539d3887d18ea4 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.icons; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.Log; + +import java.nio.ByteBuffer; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class IconNormalizer { + + private static final String TAG = "IconNormalizer"; + private static final boolean DEBUG = false; + // Ratio of icon visible area to full icon size for a square shaped icon + private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; + // Ratio of icon visible area to full icon size for a circular shaped icon + private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576; + + private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4; + + // Slope used to calculate icon visible area to full icon size for any generic shaped icon. + private static final float LINEAR_SCALE_SLOPE = + (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT); + + private static final int MIN_VISIBLE_ALPHA = 40; + + // Shape detection related constants + private static final float BOUND_RATIO_MARGIN = .05f; + private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; + private static final float SCALE_NOT_INITIALIZED = 0; + + // Ratio of the diameter of an normalized circular icon to the actual icon size. + public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f; + + private final int mMaxSize; + private final Bitmap mBitmap; + private final Canvas mCanvas; + private final Paint mPaintMaskShape; + private final Paint mPaintMaskShapeOutline; + private final byte[] mPixels; + + private final RectF mAdaptiveIconBounds; + private float mAdaptiveIconScale; + + private boolean mEnableShapeDetection; + + // for each y, stores the position of the leftmost x and the rightmost x + private final float[] mLeftBorder; + private final float[] mRightBorder; + private final Rect mBounds; + private final Path mShapePath; + private final Matrix mMatrix; + + /** package private **/ + IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) { + // Use twice the icon size as maximum size to avoid scaling down twice. + mMaxSize = iconBitmapSize * 2; + mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); + mCanvas = new Canvas(mBitmap); + mPixels = new byte[mMaxSize * mMaxSize]; + mLeftBorder = new float[mMaxSize]; + mRightBorder = new float[mMaxSize]; + mBounds = new Rect(); + mAdaptiveIconBounds = new RectF(); + + mPaintMaskShape = new Paint(); + mPaintMaskShape.setColor(Color.RED); + mPaintMaskShape.setStyle(Paint.Style.FILL); + mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); + + mPaintMaskShapeOutline = new Paint(); + mPaintMaskShapeOutline.setStrokeWidth( + 2 * context.getResources().getDisplayMetrics().density); + mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); + mPaintMaskShapeOutline.setColor(Color.BLACK); + mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + mShapePath = new Path(); + mMatrix = new Matrix(); + mAdaptiveIconScale = SCALE_NOT_INITIALIZED; + mEnableShapeDetection = shapeDetection; + } + + private static float getScale(float hullArea, float boundingArea, float fullArea) { + float hullByRect = hullArea / boundingArea; + float scaleRequired; + if (hullByRect < CIRCLE_AREA_BY_RECT) { + scaleRequired = MAX_CIRCLE_AREA_FACTOR; + } else { + scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); + } + + float areaScale = hullArea / fullArea; + // Use sqrt of the final ratio as the images is scaled across both width and height. + return areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; + } + + /** + * @param d Should be AdaptiveIconDrawable + * @param size Canvas size to use + */ + @TargetApi(Build.VERSION_CODES.O) + public static float normalizeAdaptiveIcon(Drawable d, int size, @Nullable RectF outBounds) { + Rect tmpBounds = new Rect(d.getBounds()); + d.setBounds(0, 0, size, size); + + Path path = ((AdaptiveIconDrawable) d).getIconMask(); + Region region = new Region(); + region.setPath(path, new Region(0, 0, size, size)); + + Rect hullBounds = region.getBounds(); + int hullArea = GraphicsUtils.getArea(region); + + if (outBounds != null) { + float sizeF = size; + outBounds.set( + hullBounds.left / sizeF, + hullBounds.top / sizeF, + 1 - (hullBounds.right / sizeF), + 1 - (hullBounds.bottom / sizeF)); + } + d.setBounds(tmpBounds); + return getScale(hullArea, hullArea, size * size); + } + + /** + * Returns if the shape of the icon is same as the path. + * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds. + */ + private boolean isShape(Path maskPath) { + // Condition1: + // If width and height of the path not close to a square, then the icon shape is + // not same as the mask shape. + float iconRatio = ((float) mBounds.width()) / mBounds.height(); + if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) { + if (DEBUG) { + Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio); + } + return false; + } + + // Condition 2: + // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation + // should generate transparent image, if the actual icon is equivalent to the shape. + + // Fit the shape within the icon's bounding box + mMatrix.reset(); + mMatrix.setScale(mBounds.width(), mBounds.height()); + mMatrix.postTranslate(mBounds.left, mBounds.top); + maskPath.transform(mMatrix, mShapePath); + + // XOR operation + mCanvas.drawPath(mShapePath, mPaintMaskShape); + + // DST_OUT operation around the mask path outline + mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline); + + // Check if the result is almost transparent + return isTransparentBitmap(); + } + + /** + * Used to determine if certain the bitmap is transparent. + */ + private boolean isTransparentBitmap() { + ByteBuffer buffer = ByteBuffer.wrap(mPixels); + buffer.rewind(); + mBitmap.copyPixelsToBuffer(buffer); + + int y = mBounds.top; + // buffer position + int index = y * mMaxSize; + // buffer shift after every row, width of buffer = mMaxSize + int rowSizeDiff = mMaxSize - mBounds.right; + + int sum = 0; + for (; y < mBounds.bottom; y++) { + index += mBounds.left; + for (int x = mBounds.left; x < mBounds.right; x++) { + if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { + sum++; + } + index++; + } + index += rowSizeDiff; + } + + float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); + return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; + } + + /** + * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it + * matches the design guidelines for a launcher icon. + * + * We first calculate the convex hull of the visible portion of the icon. + * This hull then compared with the bounding rectangle of the hull to find how closely it + * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an + * ideal solution but it gives satisfactory result without affecting the performance. + * + * This closeness is used to determine the ratio of hull area to the full icon size. + * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR} + * + * @param outBounds optional rect to receive the fraction distance from each edge. + */ + public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, + @Nullable Path path, @Nullable boolean[] outMaskShape) { + if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) { + if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { + mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds); + } + if (outBounds != null) { + outBounds.set(mAdaptiveIconBounds); + } + return mAdaptiveIconScale; + } + int width = d.getIntrinsicWidth(); + int height = d.getIntrinsicHeight(); + if (width <= 0 || height <= 0) { + width = width <= 0 || width > mMaxSize ? mMaxSize : width; + height = height <= 0 || height > mMaxSize ? mMaxSize : height; + } else if (width > mMaxSize || height > mMaxSize) { + int max = Math.max(width, height); + width = mMaxSize * width / max; + height = mMaxSize * height / max; + } + + mBitmap.eraseColor(Color.TRANSPARENT); + d.setBounds(0, 0, width, height); + d.draw(mCanvas); + + ByteBuffer buffer = ByteBuffer.wrap(mPixels); + buffer.rewind(); + mBitmap.copyPixelsToBuffer(buffer); + + // Overall bounds of the visible icon. + int topY = -1; + int bottomY = -1; + int leftX = mMaxSize + 1; + int rightX = -1; + + // Create border by going through all pixels one row at a time and for each row find + // the first and the last non-transparent pixel. Set those values to mLeftBorder and + // mRightBorder and use -1 if there are no visible pixel in the row. + + // buffer position + int index = 0; + // buffer shift after every row, width of buffer = mMaxSize + int rowSizeDiff = mMaxSize - width; + // first and last position for any row. + int firstX, lastX; + + for (int y = 0; y < height; y++) { + firstX = lastX = -1; + for (int x = 0; x < width; x++) { + if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { + if (firstX == -1) { + firstX = x; + } + lastX = x; + } + index++; + } + index += rowSizeDiff; + + mLeftBorder[y] = firstX; + mRightBorder[y] = lastX; + + // If there is at least one visible pixel, update the overall bounds. + if (firstX != -1) { + bottomY = y; + if (topY == -1) { + topY = y; + } + + leftX = Math.min(leftX, firstX); + rightX = Math.max(rightX, lastX); + } + } + + if (topY == -1 || rightX == -1) { + // No valid pixels found. Do not scale. + return 1; + } + + convertToConvexArray(mLeftBorder, 1, topY, bottomY); + convertToConvexArray(mRightBorder, -1, topY, bottomY); + + // Area of the convex hull + float area = 0; + for (int y = 0; y < height; y++) { + if (mLeftBorder[y] <= -1) { + continue; + } + area += mRightBorder[y] - mLeftBorder[y] + 1; + } + + mBounds.left = leftX; + mBounds.right = rightX; + + mBounds.top = topY; + mBounds.bottom = bottomY; + + if (outBounds != null) { + outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height, + 1 - ((float) mBounds.right) / width, + 1 - ((float) mBounds.bottom) / height); + } + if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) { + outMaskShape[0] = isShape(path); + } + // Area of the rectangle required to fit the convex hull + float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); + return getScale(area, rectArea, width * height); + } + + /** + * Modifies {@param xCoordinates} to represent a convex border. Fills in all missing values + * (except on either ends) with appropriate values. + * @param xCoordinates map of x coordinate per y. + * @param direction 1 for left border and -1 for right border. + * @param topY the first Y position (inclusive) with a valid value. + * @param bottomY the last Y position (inclusive) with a valid value. + */ + private static void convertToConvexArray( + float[] xCoordinates, int direction, int topY, int bottomY) { + int total = xCoordinates.length; + // The tangent at each pixel. + float[] angles = new float[total - 1]; + + int first = topY; // First valid y coordinate + int last = -1; // Last valid y coordinate which didn't have a missing value + + float lastAngle = Float.MAX_VALUE; + + for (int i = topY + 1; i <= bottomY; i++) { + if (xCoordinates[i] <= -1) { + continue; + } + int start; + + if (lastAngle == Float.MAX_VALUE) { + start = first; + } else { + float currentAngle = (xCoordinates[i] - xCoordinates[last]) / (i - last); + start = last; + // If this position creates a concave angle, keep moving up until we find a + // position which creates a convex angle. + if ((currentAngle - lastAngle) * direction < 0) { + while (start > first) { + start --; + currentAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start); + if ((currentAngle - angles[start]) * direction >= 0) { + break; + } + } + } + } + + // Reset from last check + lastAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start); + // Update all the points from start. + for (int j = start; j < i; j++) { + angles[j] = lastAngle; + xCoordinates[j] = xCoordinates[start] + lastAngle * (j - start); + } + last = i; + } + } + + /** + * @return The diameter of the normalized circle that fits inside of the square (size x size). + */ + public static int getNormalizedCircleSize(int size) { + float area = size * size * MAX_CIRCLE_AREA_FACTOR; + return (int) Math.round(Math.sqrt((4 * area) / Math.PI)); + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..449c0daa54beac952c8c87df680393ff9de505fe --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2019 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 static android.content.Intent.ACTION_DATE_CHANGED; +import static android.content.Intent.ACTION_TIMEZONE_CHANGED; +import static android.content.Intent.ACTION_TIME_CHANGED; +import static android.content.res.Resources.ID_NULL; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.PatternMatcher; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.launcher3.icons.ThemedIconDrawable.ThemeData; +import com.android.launcher3.util.SafeCloseable; + +import org.xmlpull.v1.XmlPullParser; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; + +/** + * Class to handle icon loading from different packages + */ +public class IconProvider { + + private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"; + private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier( + "config_icon_mask", "string", "android"); + + private static final String TAG_ICON = "icon"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DRAWABLE = "drawable"; + + private static final String TAG = "IconProvider"; + private static final boolean DEBUG = false; + + private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons"; + + private static final String SYSTEM_STATE_SEPARATOR = " "; + private static final String THEMED_ICON_MAP_FILE = "grayscale_icon_map"; + + private static final Map DISABLED_MAP = Collections.emptyMap(); + + private Map mThemedIconMap; + + private final Context mContext; + private final ComponentName mCalendar; + private final ComponentName mClock; + + static final int ICON_TYPE_DEFAULT = 0; + static final int ICON_TYPE_CALENDAR = 1; + static final int ICON_TYPE_CLOCK = 2; + + public IconProvider(Context context) { + this(context, false); + } + + public IconProvider(Context context, boolean supportsIconTheme) { + mContext = context; + mCalendar = parseComponentOrNull(context, R.string.calendar_component_name); + mClock = parseComponentOrNull(context, R.string.clock_component_name); + if (!supportsIconTheme) { + // Initialize an empty map if theming is not supported + mThemedIconMap = DISABLED_MAP; + } + } + + /** + * Enables or disables icon theme support + */ + public void setIconThemeSupported(boolean isSupported) { + mThemedIconMap = isSupported ? null : DISABLED_MAP; + } + + /** + * Adds any modification to the provided systemState for dynamic icons. This system state + * is used by caches to check for icon invalidation. + */ + public String getSystemStateForPackage(String systemState, String packageName) { + if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) { + return systemState + SYSTEM_STATE_SEPARATOR + getDay(); + } else { + return systemState; + } + } + + /** + * Loads the icon for the provided LauncherActivityInfo + */ + public Drawable getIcon(LauncherActivityInfo info, int iconDpi) { + return getIconWithOverrides(info.getApplicationInfo().packageName, info.getUser(), iconDpi, + () -> info.getIcon(iconDpi)); + } + + /** + * Loads the icon for the provided activity info + */ + public Drawable getIcon(ActivityInfo info) { + return getIcon(info, mContext.getResources().getConfiguration().densityDpi); + } + + /** + * Loads the icon for the provided activity info + */ + public Drawable getIcon(ActivityInfo info, int iconDpi) { + return getIconWithOverrides(info.applicationInfo.packageName, + UserHandle.getUserHandleForUid(info.applicationInfo.uid), + iconDpi, () -> loadActivityInfoIcon(info, iconDpi)); + } + + private Drawable getIconWithOverrides(String packageName, UserHandle user, int iconDpi, + Supplier fallback) { + Drawable icon = null; + + int iconType = ICON_TYPE_DEFAULT; + if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) { + icon = loadCalendarDrawable(iconDpi); + iconType = ICON_TYPE_CALENDAR; + } else if (mClock != null + && mClock.getPackageName().equals(packageName) + && Process.myUserHandle().equals(user)) { + icon = loadClockDrawable(iconDpi); + iconType = ICON_TYPE_CLOCK; + } + if (icon == null) { + icon = fallback.get(); + iconType = ICON_TYPE_DEFAULT; + } + + ThemeData td = getThemedIconMap().get(packageName); + return td != null ? td.wrapDrawable(icon, iconType) : icon; + } + + private Drawable loadActivityInfoIcon(ActivityInfo ai, int density) { + final int iconRes = ai.getIconResource(); + Drawable icon = null; + // Get the preferred density icon from the app's resources + if (density != 0 && iconRes != 0) { + try { + final Resources resources = mContext.getPackageManager() + .getResourcesForApplication(ai.applicationInfo); + icon = resources.getDrawableForDensity(iconRes, density); + } catch (NameNotFoundException | Resources.NotFoundException exc) { } + } + // Get the default density icon + if (icon == null) { + icon = ai.loadIcon(mContext.getPackageManager()); + } + return icon; + } + + private Map getThemedIconMap() { + if (mThemedIconMap != null) { + return mThemedIconMap; + } + ArrayMap map = new ArrayMap<>(); + try { + Resources res = mContext.getResources(); + int resID = res.getIdentifier(THEMED_ICON_MAP_FILE, "xml", mContext.getPackageName()); + if (resID != 0) { + XmlResourceParser parser = res.getXml(resID); + final int depth = parser.getDepth(); + + int type; + + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT); + + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } + if (TAG_ICON.equals(parser.getName())) { + String pkg = parser.getAttributeValue(null, ATTR_PACKAGE); + int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0); + if (iconId != 0 && !TextUtils.isEmpty(pkg)) { + map.put(pkg, new ThemeData(res, iconId)); + } + } + } + } + } catch (Exception e) { + Log.e(TAG, "Unable to parse icon map", e); + } + mThemedIconMap = map; + return mThemedIconMap; + } + + private Drawable loadCalendarDrawable(int iconDpi) { + PackageManager pm = mContext.getPackageManager(); + try { + final Bundle metadata = pm.getActivityInfo( + mCalendar, + PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA) + .metaData; + final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName()); + final int id = getDynamicIconId(metadata, resources); + if (id != ID_NULL) { + if (DEBUG) Log.d(TAG, "Got icon #" + id); + return resources.getDrawableForDensity(id, iconDpi, null /* theme */); + } + } catch (PackageManager.NameNotFoundException e) { + if (DEBUG) { + Log.d(TAG, "Could not get activityinfo or resources for package: " + + mCalendar.getPackageName()); + } + } + return null; + } + + private Drawable loadClockDrawable(int iconDpi) { + return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi); + } + + /** + * @param metadata metadata of the default activity of Calendar + * @param resources from the Calendar package + * @return the resource id for today's Calendar icon; 0 if resources cannot be found. + */ + private int getDynamicIconId(Bundle metadata, Resources resources) { + if (metadata == null) { + return ID_NULL; + } + String key = mCalendar.getPackageName() + ICON_METADATA_KEY_PREFIX; + final int arrayId = metadata.getInt(key, ID_NULL); + if (arrayId == ID_NULL) { + return ID_NULL; + } + try { + return resources.obtainTypedArray(arrayId).getResourceId(getDay(), ID_NULL); + } catch (Resources.NotFoundException e) { + if (DEBUG) { + Log.d(TAG, "package defines '" + key + "' but corresponding array not found"); + } + return ID_NULL; + } + } + + /** + * @return Today's day of the month, zero-indexed. + */ + static int getDay() { + return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; + } + + private static ComponentName parseComponentOrNull(Context context, int resId) { + String cn = context.getString(resId); + return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn); + } + + /** + * Returns a string representation of the current system icon state + */ + public String getSystemIconState() { + return (CONFIG_ICON_MASK_RES_ID == ID_NULL + ? "" : mContext.getResources().getString(CONFIG_ICON_MASK_RES_ID)) + + (mThemedIconMap == DISABLED_MAP ? ",no-theme" : ",with-theme"); + } + + /** + * Registers a callback to listen for various system dependent icon changes. + */ + public SafeCloseable registerIconChangeListener(IconChangeListener listener, Handler handler) { + return new IconChangeReceiver(listener, handler); + } + + private class IconChangeReceiver extends BroadcastReceiver implements SafeCloseable { + + private final IconChangeListener mCallback; + private String mIconState; + + IconChangeReceiver(IconChangeListener callback, Handler handler) { + mCallback = callback; + mIconState = getSystemIconState(); + + + IntentFilter packageFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); + packageFilter.addDataScheme("package"); + packageFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL); + mContext.registerReceiver(this, packageFilter, null, handler); + + if (mCalendar != null || mClock != null) { + final IntentFilter filter = new IntentFilter(ACTION_TIMEZONE_CHANGED); + if (mCalendar != null) { + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(ACTION_DATE_CHANGED); + } + mContext.registerReceiver(this, filter, null, handler); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_TIMEZONE_CHANGED: + if (mClock != null) { + mCallback.onAppIconChanged(mClock.getPackageName(), Process.myUserHandle()); + } + // follow through + case ACTION_DATE_CHANGED: + case ACTION_TIME_CHANGED: + if (mCalendar != null) { + for (UserHandle user + : context.getSystemService(UserManager.class).getUserProfiles()) { + mCallback.onAppIconChanged(mCalendar.getPackageName(), user); + } + } + break; + case ACTION_OVERLAY_CHANGED: { + String newState = getSystemIconState(); + if (!mIconState.equals(newState)) { + mIconState = newState; + mCallback.onSystemIconStateChanged(mIconState); + } + break; + } + } + } + + @Override + public void close() { + mContext.unregisterReceiver(this); + } + } + + /** + * Listener for receiving icon changes + */ + public interface IconChangeListener { + + /** + * Called when the icon for a particular app changes + */ + void onAppIconChanged(String packageName, UserHandle user); + + /** + * Called when the global icon state changed, which can typically affect all icons + */ + void onSystemIconStateChanged(String iconState); + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/PlaceHolderIconDrawable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/PlaceHolderIconDrawable.java new file mode 100644 index 0000000000000000000000000000000000000000..5f3343e3168bbb12f36830794c808c44770b7ef7 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/PlaceHolderIconDrawable.java @@ -0,0 +1,79 @@ +/* + * 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import androidx.core.graphics.ColorUtils; + +/** + * Subclass which draws a placeholder icon when the actual icon is not yet loaded + */ +public class PlaceHolderIconDrawable extends FastBitmapDrawable { + + // Path in [0, 100] bounds. + private final Path mProgressPath; + + public PlaceHolderIconDrawable(BitmapInfo info, Context context) { + super(info); + + mProgressPath = GraphicsUtils.getShapePath(100); + mPaint.setColor(ColorUtils.compositeColors( + GraphicsUtils.getAttrColor(context, R.attr.loadingIconColor), info.color)); + } + + @Override + protected void drawInternal(Canvas canvas, Rect bounds) { + int saveCount = canvas.save(); + canvas.translate(bounds.left, bounds.top); + canvas.scale(bounds.width() / 100f, bounds.height() / 100f); + canvas.drawPath(mProgressPath, mPaint); + canvas.restoreToCount(saveCount); + } + + /** Updates this placeholder to {@code newIcon} with animation. */ + public void animateIconUpdate(Drawable newIcon) { + int placeholderColor = mPaint.getColor(); + int originalAlpha = Color.alpha(placeholderColor); + + ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0); + iconUpdateAnimation.setDuration(375); + iconUpdateAnimation.addUpdateListener(valueAnimator -> { + int newAlpha = (int) valueAnimator.getAnimatedValue(); + int newColor = ColorUtils.setAlphaComponent(placeholderColor, newAlpha); + + newIcon.setColorFilter(new PorterDuffColorFilter(newColor, PorterDuff.Mode.SRC_ATOP)); + }); + iconUpdateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + newIcon.setColorFilter(null); + } + }); + iconUpdateAnimation.start(); + } + +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/RoundDrawableWrapper.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/RoundDrawableWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..e569c1ea078f99aa0aa73d5765087d8b90f99212 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/RoundDrawableWrapper.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.DrawableWrapper; + +/** + * A drawable which clips rounded corner around a child drawable + */ +public class RoundDrawableWrapper extends DrawableWrapper { + + private final RectF mTempRect = new RectF(); + private final Path mClipPath = new Path(); + private final float mRoundedCornersRadius; + + public RoundDrawableWrapper(Drawable dr, float radius) { + super(dr); + mRoundedCornersRadius = radius; + } + + @Override + protected void onBoundsChange(Rect bounds) { + mTempRect.set(getBounds()); + mClipPath.reset(); + mClipPath.addRoundRect(mTempRect, mRoundedCornersRadius, + mRoundedCornersRadius, Path.Direction.CCW); + super.onBoundsChange(bounds); + } + + @Override + public final void draw(Canvas canvas) { + int saveCount = canvas.save(); + canvas.clipPath(mClipPath); + super.draw(canvas); + canvas.restoreToCount(saveCount); + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..e24f353ad29c2084caac8cb754e20facb29fe10c --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2016 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 static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; + +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.BlurMaskFilter.Blur; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; + +/** + * Utility class to add shadows to bitmaps. + */ +public class ShadowGenerator { + + public static final boolean ENABLE_SHADOWS = true; + + public static final float BLUR_FACTOR = 1.5f/48; + + // Percent of actual icon size + public static final float KEY_SHADOW_DISTANCE = 1f/48; + private static final int KEY_SHADOW_ALPHA = 10; + // Percent of actual icon size + private static final float HALF_DISTANCE = 0.5f; + private static final int AMBIENT_SHADOW_ALPHA = 7; + + private final int mIconSize; + + private final Paint mBlurPaint; + private final Paint mDrawPaint; + private final BlurMaskFilter mDefaultBlurMaskFilter; + + public ShadowGenerator(int iconSize) { + mIconSize = iconSize; + mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL); + } + + public synchronized void recreateIcon(Bitmap icon, Canvas out) { + recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out); + } + + public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter, + int ambientAlpha, int keyAlpha, Canvas out) { + if (ENABLE_SHADOWS) { + int[] offset = new int[2]; + mBlurPaint.setMaskFilter(blurMaskFilter); + Bitmap shadow = icon.extractAlpha(mBlurPaint, offset); + + // Draw ambient shadow + mDrawPaint.setAlpha(ambientAlpha); + out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint); + + // Draw key shadow + mDrawPaint.setAlpha(keyAlpha); + out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, + mDrawPaint); + } + + // Draw the icon + mDrawPaint.setAlpha(255); + out.drawBitmap(icon, 0, 0, mDrawPaint); + } + + /** + * Returns the minimum amount by which an icon with {@param bounds} should be scaled + * so that the shadows do not get clipped. + */ + public static float getScaleForBounds(RectF bounds) { + float scale = 1; + + if (ENABLE_SHADOWS) { + // For top, left & right, we need same space. + float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top); + if (minSide < BLUR_FACTOR) { + scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide); + } + + float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE; + if (bounds.bottom < bottomSpace) { + scale = Math.min(scale, + (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom)); + } + } + return scale; + } + + public static class Builder { + + public final RectF bounds = new RectF(); + public final int color; + + public int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA; + + public float shadowBlur; + + public float keyShadowDistance; + public int keyShadowAlpha = KEY_SHADOW_ALPHA; + public float radius; + + public Builder(int color) { + this.color = color; + } + + public Builder setupBlurForSize(int height) { + if (ENABLE_SHADOWS) { + shadowBlur = height * 1f / 24; + keyShadowDistance = height * 1f / 16; + } else { + shadowBlur = 0; + keyShadowDistance = 0; + } + return this; + } + + public Bitmap createPill(int width, int height) { + return createPill(width, height, height / 2f); + } + + public Bitmap createPill(int width, int height, float r) { + radius = r; + + int centerX = Math.round(width / 2f + shadowBlur); + int centerY = Math.round(radius + shadowBlur + keyShadowDistance); + int center = Math.max(centerX, centerY); + bounds.set(0, 0, width, height); + bounds.offsetTo(center - width / 2f, center - height / 2f); + + int size = center * 2; + return BitmapRenderer.createHardwareBitmap(size, size, this::drawShadow); + } + + public void drawShadow(Canvas c) { + Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + p.setColor(color); + + if (ENABLE_SHADOWS) { + // Key shadow + p.setShadowLayer(shadowBlur, 0, keyShadowDistance, + setColorAlphaBound(Color.BLACK, keyShadowAlpha)); + c.drawRoundRect(bounds, radius, radius, p); + + // Ambient shadow + p.setShadowLayer(shadowBlur, 0, 0, + setColorAlphaBound(Color.BLACK, ambientShadowAlpha)); + c.drawRoundRect(bounds, radius, radius, p); + } + + if (Color.alpha(color) < 255) { + // Clear any content inside the pill-rect for translucent fill. + p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + p.clearShadowLayer(); + p.setColor(Color.BLACK); + c.drawRoundRect(bounds, radius, radius, p); + + p.setXfermode(null); + p.setColor(color); + c.drawRoundRect(bounds, radius, radius, p); + } + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java new file mode 100644 index 0000000000000000000000000000000000000000..b2e554b7b6f6c70d9e27abc5e2006927f70883d5 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2021 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 static android.content.res.Configuration.UI_MODE_NIGHT_MASK; +import static android.content.res.Configuration.UI_MODE_NIGHT_YES; +import static android.content.res.Resources.ID_NULL; + +import static com.android.launcher3.icons.GraphicsUtils.getExpectedBitmapSize; +import static com.android.launcher3.icons.IconProvider.ICON_TYPE_CALENDAR; +import static com.android.launcher3.icons.IconProvider.ICON_TYPE_CLOCK; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.android.launcher3.icons.BitmapInfo.Extender; +import com.android.launcher3.icons.cache.BaseIconCache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Class to handle monochrome themed app icons + */ +@SuppressWarnings("NewApi") +public class ThemedIconDrawable extends FastBitmapDrawable { + + public static final String TAG = "ThemedIconDrawable"; + + final ThemedBitmapInfo bitmapInfo; + final int colorFg, colorBg; + + // The foreground/monochrome icon for the app + private final Drawable mMonochromeIcon; + private final AdaptiveIconDrawable mBgWrapper; + private final Rect mBadgeBounds; + + protected ThemedIconDrawable(ThemedConstantState constantState) { + super(constantState.mBitmap, constantState.colorFg, constantState.mIsDisabled); + bitmapInfo = constantState.bitmapInfo; + colorBg = constantState.colorBg; + colorFg = constantState.colorFg; + + mMonochromeIcon = bitmapInfo.mThemeData.loadMonochromeDrawable(colorFg); + mBgWrapper = new AdaptiveIconDrawable(new ColorDrawable(colorBg), null); + mBadgeBounds = bitmapInfo.mUserBadge == null ? null : + new Rect(0, 0, bitmapInfo.mUserBadge.getWidth(), bitmapInfo.mUserBadge.getHeight()); + + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mBgWrapper.setBounds(bounds); + mMonochromeIcon.setBounds(bounds); + } + + @Override + protected void drawInternal(Canvas canvas, Rect bounds) { + int count = canvas.save(); + canvas.scale(bitmapInfo.mNormalizationScale, bitmapInfo.mNormalizationScale, + bounds.exactCenterX(), bounds.exactCenterY()); + mPaint.setColor(colorBg); + canvas.drawPath(mBgWrapper.getIconMask(), mPaint); + mMonochromeIcon.draw(canvas); + canvas.restoreToCount(count); + if (mBadgeBounds != null) { + canvas.drawBitmap(bitmapInfo.mUserBadge, mBadgeBounds, getBounds(), mPaint); + } + } + + @Override + public boolean isThemed() { + return true; + } + + @Override + public ConstantState getConstantState() { + return new ThemedConstantState(bitmapInfo, colorBg, colorFg, mIsDisabled); + } + + static class ThemedConstantState extends FastBitmapConstantState { + + final ThemedBitmapInfo bitmapInfo; + final int colorFg, colorBg; + + public ThemedConstantState(ThemedBitmapInfo bitmapInfo, + int colorBg, int colorFg, boolean isDisabled) { + super(bitmapInfo.icon, bitmapInfo.color, isDisabled); + this.bitmapInfo = bitmapInfo; + this.colorBg = colorBg; + this.colorFg = colorFg; + } + + @Override + public FastBitmapDrawable newDrawable() { + return new ThemedIconDrawable(this); + } + } + + public static class ThemedBitmapInfo extends BitmapInfo { + + final ThemeData mThemeData; + final float mNormalizationScale; + final Bitmap mUserBadge; + + public ThemedBitmapInfo(Bitmap icon, int color, ThemeData themeData, + float normalizationScale, Bitmap userBadge) { + super(icon, color); + mThemeData = themeData; + mNormalizationScale = normalizationScale; + mUserBadge = userBadge; + } + + @Override + public FastBitmapDrawable newThemedIcon(Context context) { + int[] colors = getColors(context); + FastBitmapDrawable drawable = new ThemedConstantState(this, colors[0], colors[1], false) + .newDrawable(); + drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return drawable; + } + + @Nullable + public byte[] toByteArray() { + if (isNullOrLowRes()) { + return null; + } + String resName = mThemeData.mResources.getResourceName(mThemeData.mResID); + ByteArrayOutputStream out = new ByteArrayOutputStream( + getExpectedBitmapSize(icon) + 3 + resName.length()); + try { + DataOutputStream dos = new DataOutputStream(out); + dos.writeByte(TYPE_THEMED); + dos.writeFloat(mNormalizationScale); + dos.writeUTF(resName); + icon.compress(Bitmap.CompressFormat.PNG, 100, dos); + + dos.flush(); + dos.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + + static ThemedBitmapInfo decode(byte[] data, int color, + BitmapFactory.Options decodeOptions, UserHandle user, BaseIconCache iconCache, + Context context) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data))) { + dis.readByte(); // type + float normalizationScale = dis.readFloat(); + + String resName = dis.readUTF(); + int resId = context.getResources() + .getIdentifier(resName, "drawable", context.getPackageName()); + if (resId == ID_NULL) { + return null; + } + + Bitmap userBadgeBitmap = null; + if (!Process.myUserHandle().equals(user)) { + try (BaseIconFactory iconFactory = iconCache.getIconFactory()) { + userBadgeBitmap = iconFactory.getUserBadgeBitmap(user); + } + } + + ThemeData themeData = new ThemeData(context.getResources(), resId); + Bitmap icon = BitmapFactory.decodeStream(dis, null, decodeOptions); + return new ThemedBitmapInfo(icon, color, themeData, normalizationScale, + userBadgeBitmap); + } catch (IOException e) { + return null; + } + } + } + + public static class ThemeData { + + final Resources mResources; + final int mResID; + + public ThemeData(Resources resources, int resID) { + mResources = resources; + mResID = resID; + } + + Drawable loadMonochromeDrawable(int accentColor) { + Drawable d = mResources.getDrawable(mResID).mutate(); + d.setTint(accentColor); + d = new InsetDrawable(d, .2f); + return d; + } + + public Drawable wrapDrawable(Drawable original, int iconType) { + if (!(original instanceof AdaptiveIconDrawable)) { + return original; + } + AdaptiveIconDrawable aid = (AdaptiveIconDrawable) original; + String resourceType = mResources.getResourceTypeName(mResID); + if (iconType == ICON_TYPE_CALENDAR && "array".equals(resourceType)) { + TypedArray ta = mResources.obtainTypedArray(mResID); + int id = ta.getResourceId(IconProvider.getDay(), ID_NULL); + ta.recycle(); + return id == ID_NULL ? original + : new ThemedAdaptiveIcon(aid, new ThemeData(mResources, id)); + } else if (iconType == ICON_TYPE_CLOCK && "array".equals(resourceType)) { + ((ClockDrawableWrapper) original).mThemeData = this; + return original; + } else if ("drawable".equals(resourceType)) { + return new ThemedAdaptiveIcon(aid, this); + } else { + return original; + } + } + } + + static class ThemedAdaptiveIcon extends AdaptiveIconDrawable implements Extender { + + protected final ThemeData mThemeData; + + public ThemedAdaptiveIcon(AdaptiveIconDrawable parent, ThemeData themeData) { + super(parent.getBackground(), parent.getForeground()); + mThemeData = themeData; + } + + @Override + public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory, + float normalizationScale, UserHandle user) { + Bitmap userBadge = Process.myUserHandle().equals(user) + ? null : iconFactory.getUserBadgeBitmap(user); + return new ThemedBitmapInfo(bitmap, color, mThemeData, normalizationScale, userBadge); + } + + @Override + public void drawForPersistence(Canvas canvas) { + draw(canvas); + } + + @Override + public Drawable getThemedDrawable(Context context) { + int[] colors = getColors(context); + Drawable bg = new ColorDrawable(colors[0]); + float inset = getExtraInsetFraction() / (1 + 2 * getExtraInsetFraction()); + Drawable fg = new InsetDrawable(mThemeData.loadMonochromeDrawable(colors[1]), inset); + return new AdaptiveIconDrawable(bg, fg); + } + } + + /** + * Get an int array representing background and foreground colors for themed icons + */ + public static int[] getColors(Context context) { + Resources res = context.getResources(); + int[] colors = new int[2]; + if ((res.getConfiguration().uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES) { + colors[0] = res.getColor(android.R.color.system_neutral1_800); + colors[1] = res.getColor(android.R.color.system_accent1_100); + } else { + colors[0] = res.getColor(android.R.color.system_accent1_100); + colors[1] = res.getColor(android.R.color.system_neutral2_700); + } + return colors; + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java new file mode 100644 index 0000000000000000000000000000000000000000..d685737c447cc4ce743c5009484d8c611f576def --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java @@ -0,0 +1,570 @@ +/* + * 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 static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActivityIcon; +import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON; +import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; + +import android.content.ComponentName; +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Handler; +import android.os.LocaleList; +import android.os.Looper; +import android.os.Process; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.launcher3.icons.BaseIconFactory; +import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SQLiteCacheHelper; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +public abstract class BaseIconCache { + + private static final String TAG = "BaseIconCache"; + private static final boolean DEBUG = false; + + private static final int INITIAL_ICON_CACHE_CAPACITY = 50; + + // Empty class name is used for storing package default entry. + public static final String EMPTY_CLASS_NAME = "."; + + public static class CacheEntry { + + @NonNull + public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO; + public CharSequence title = ""; + public CharSequence contentDescription = ""; + } + + private final HashMap mDefaultIcons = new HashMap<>(); + + protected final Context mContext; + protected final PackageManager mPackageManager; + + private final Map mCache; + protected final Handler mWorkerHandler; + + protected int mIconDpi; + protected IconDB mIconDb; + protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList(); + protected String mSystemState = ""; + + private final String mDbFileName; + private final Looper mBgLooper; + + public BaseIconCache(Context context, String dbFileName, Looper bgLooper, + int iconDpi, int iconPixelSize, boolean inMemoryCache) { + mContext = context; + mDbFileName = dbFileName; + mPackageManager = context.getPackageManager(); + mBgLooper = bgLooper; + mWorkerHandler = new Handler(mBgLooper); + + if (inMemoryCache) { + mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); + } else { + // Use a dummy cache + mCache = new AbstractMap() { + @Override + public Set> entrySet() { + return Collections.emptySet(); + } + + @Override + public CacheEntry put(ComponentKey key, CacheEntry value) { + return value; + } + }; + } + + updateSystemState(); + mIconDpi = iconDpi; + mIconDb = new IconDB(context, dbFileName, iconPixelSize); + } + + /** + * Returns the persistable serial number for {@param user}. Subclass should implement proper + * caching strategy to avoid making binder call every time. + */ + protected abstract long getSerialNumberForUser(UserHandle user); + + /** + * Return true if the given app is an instant app and should be badged appropriately. + */ + protected abstract boolean isInstantApp(ApplicationInfo info); + + /** + * Opens and returns an icon factory. The factory is recycled by the caller. + */ + public abstract BaseIconFactory getIconFactory(); + + public void updateIconParams(int iconDpi, int iconPixelSize) { + mWorkerHandler.post(() -> updateIconParamsBg(iconDpi, iconPixelSize)); + } + + private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) { + mIconDpi = iconDpi; + mDefaultIcons.clear(); + mIconDb.clear(); + mIconDb.close(); + mIconDb = new IconDB(mContext, mDbFileName, iconPixelSize); + mCache.clear(); + } + + private Drawable getFullResIcon(Resources resources, int iconId) { + if (resources != null && iconId != 0) { + try { + return resources.getDrawableForDensity(iconId, mIconDpi); + } catch (Resources.NotFoundException e) { } + } + return getFullResDefaultActivityIcon(mIconDpi); + } + + public Drawable getFullResIcon(String packageName, int iconId) { + try { + return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId); + } catch (PackageManager.NameNotFoundException e) { } + return getFullResDefaultActivityIcon(mIconDpi); + } + + public Drawable getFullResIcon(ActivityInfo info) { + try { + return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo), + info.getIconResource()); + } catch (PackageManager.NameNotFoundException e) { } + return getFullResDefaultActivityIcon(mIconDpi); + } + + private BitmapInfo makeDefaultIcon(UserHandle user) { + try (BaseIconFactory li = getIconFactory()) { + return li.makeDefaultIcon(user); + } + } + + /** + * Remove any records for the supplied ComponentName. + */ + public synchronized void remove(ComponentName componentName, UserHandle user) { + mCache.remove(new ComponentKey(componentName, user)); + } + + /** + * Remove any records for the supplied package name from memory. + */ + private void removeFromMemCacheLocked(String packageName, UserHandle user) { + HashSet forDeletion = new HashSet<>(); + for (ComponentKey key: mCache.keySet()) { + if (key.componentName.getPackageName().equals(packageName) + && key.user.equals(user)) { + forDeletion.add(key); + } + } + for (ComponentKey condemned: forDeletion) { + mCache.remove(condemned); + } + } + + /** + * Removes the entries related to the given package in memory and persistent DB. + */ + public synchronized void removeIconsForPkg(String packageName, UserHandle user) { + removeFromMemCacheLocked(packageName, user); + long userSerial = getSerialNumberForUser(user); + mIconDb.delete( + IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", + new String[]{packageName + "/%", Long.toString(userSerial)}); + } + + public IconCacheUpdateHandler getUpdateHandler() { + updateSystemState(); + return new IconCacheUpdateHandler(this); + } + + /** + * Refreshes the system state definition used to check the validity of the cache. It + * incorporates all the properties that can affect the cache like the list of enabled locale + * and system-version. + */ + private void updateSystemState() { + mLocaleList = mContext.getResources().getConfiguration().getLocales(); + mSystemState = mLocaleList.toLanguageTags() + "," + Build.VERSION.SDK_INT; + } + + protected String getIconSystemState(String packageName) { + return mSystemState; + } + + /** + * 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. + */ + @VisibleForTesting + public synchronized void addIconToDBAndMemCache(T object, CachingLogic cachingLogic, + PackageInfo info, long userSerial, boolean replaceExisting) { + UserHandle user = cachingLogic.getUser(object); + ComponentName componentName = cachingLogic.getComponent(object); + + final ComponentKey key = new ComponentKey(componentName, user); + CacheEntry entry = null; + if (!replaceExisting) { + entry = mCache.get(key); + // We can't reuse the entry if the high-res icon is not present. + if (entry == null || entry.bitmap.isNullOrLowRes()) { + entry = null; + } + } + if (entry == null) { + entry = new CacheEntry(); + entry.bitmap = cachingLogic.loadIcon(mContext, object); + } + // Icon can't be loaded from cachingLogic, which implies alternative icon was loaded + // (e.g. fallback icon, default icon). So we drop here since there's no point in caching + // an empty entry. + if (entry.bitmap.isNullOrLowRes()) return; + entry.title = cachingLogic.getLabel(object); + entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); + if (cachingLogic.addToMemCache()) mCache.put(key, entry); + + ContentValues values = newContentValues(entry.bitmap, entry.title.toString(), + componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList)); + addIconToDB(values, componentName, info, userSerial, + cachingLogic.getLastUpdatedTime(object, info)); + } + + /** + * Updates {@param values} to contain versioning information and adds it to the DB. + * @param values {@link ContentValues} containing icon & title + */ + private void addIconToDB(ContentValues values, ComponentName key, + PackageInfo info, long userSerial, long lastUpdateTime) { + values.put(IconDB.COLUMN_COMPONENT, key.flattenToString()); + values.put(IconDB.COLUMN_USER, userSerial); + values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime); + values.put(IconDB.COLUMN_VERSION, info.versionCode); + mIconDb.insertOrReplace(values); + } + + public synchronized BitmapInfo getDefaultIcon(UserHandle user) { + if (!mDefaultIcons.containsKey(user)) { + mDefaultIcons.put(user, makeDefaultIcon(user)); + } + return mDefaultIcons.get(user); + } + + public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) { + return getDefaultIcon(user).icon == icon.icon; + } + + /** + * Retrieves the entry from the cache. If the entry is not present, it creates a new entry. + * This method is not thread safe, it must be called from a synchronized method. + */ + protected CacheEntry cacheLocked( + @NonNull ComponentName componentName, @NonNull UserHandle user, + @NonNull Supplier infoProvider, @NonNull CachingLogic cachingLogic, + boolean usePackageIcon, boolean useLowResIcon) { + assertWorkerThread(); + ComponentKey cacheKey = new ComponentKey(componentName, user); + CacheEntry entry = mCache.get(cacheKey); + if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) { + entry = new CacheEntry(); + if (cachingLogic.addToMemCache()) { + mCache.put(cacheKey, entry); + } + + // Check the DB first. + T object = null; + boolean providerFetchedOnce = false; + + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { + object = infoProvider.get(); + providerFetchedOnce = true; + + if (object != null) { + entry.bitmap = cachingLogic.loadIcon(mContext, object); + } else { + if (usePackageIcon) { + CacheEntry packageEntry = getEntryForPackageLocked( + componentName.getPackageName(), user, false); + if (packageEntry != null) { + if (DEBUG) Log.d(TAG, "using package default icon for " + + componentName.toShortString()); + entry.bitmap = packageEntry.bitmap; + entry.title = packageEntry.title; + entry.contentDescription = packageEntry.contentDescription; + } + } + if (entry.bitmap == null) { + if (DEBUG) Log.d(TAG, "using default icon for " + + componentName.toShortString()); + entry.bitmap = getDefaultIcon(user); + } + } + } + + if (TextUtils.isEmpty(entry.title)) { + if (object == null && !providerFetchedOnce) { + object = infoProvider.get(); + providerFetchedOnce = true; + } + if (object != null) { + entry.title = cachingLogic.getLabel(object); + entry.contentDescription = mPackageManager.getUserBadgedLabel( + cachingLogic.getDescription(object, entry.title), user); + } + } + } + return entry; + } + + public synchronized void clear() { + assertWorkerThread(); + mIconDb.clear(); + } + + /** + * Adds a default package entry in the cache. This entry is not persisted and will be removed + * when the cache is flushed. + */ + protected synchronized void cachePackageInstallInfo(String packageName, UserHandle user, + Bitmap icon, CharSequence title) { + removeFromMemCacheLocked(packageName, user); + + ComponentKey cacheKey = getPackageKey(packageName, user); + CacheEntry entry = mCache.get(cacheKey); + + // For icon caching, do not go through DB. Just update the in-memory entry. + if (entry == null) { + entry = new CacheEntry(); + } + if (!TextUtils.isEmpty(title)) { + entry.title = title; + } + if (icon != null) { + BaseIconFactory li = getIconFactory(); + entry.bitmap = li.createShapedIconBitmap(icon, user); + li.close(); + } + if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) { + mCache.put(cacheKey, entry); + } + } + + private static ComponentKey getPackageKey(String packageName, UserHandle user) { + ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); + return new ComponentKey(cn, user); + } + + /** + * Gets an entry for the package, which can be used as a fallback entry for various components. + * This method is not thread safe, it must be called from a synchronized method. + */ + protected CacheEntry getEntryForPackageLocked(String packageName, UserHandle user, + boolean useLowResIcon) { + assertWorkerThread(); + ComponentKey cacheKey = getPackageKey(packageName, user); + CacheEntry entry = mCache.get(cacheKey); + + if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) { + entry = new CacheEntry(); + boolean entryUpdated = true; + + // Check the DB first. + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { + try { + int flags = Process.myUserHandle().equals(user) ? 0 : + PackageManager.GET_UNINSTALLED_PACKAGES; + PackageInfo info = mPackageManager.getPackageInfo(packageName, flags); + ApplicationInfo appInfo = info.applicationInfo; + if (appInfo == null) { + throw new NameNotFoundException("ApplicationInfo is null"); + } + + 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), user, appInfo.targetSdkVersion, + isInstantApp(appInfo)); + li.close(); + + entry.title = appInfo.loadLabel(mPackageManager); + entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); + entry.bitmap = BitmapInfo.of( + useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color); + + // Add the icon in the DB here, since these do not get written during + // package updates. + ContentValues values = newContentValues( + iconInfo, entry.title.toString(), packageName, null); + addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user), + info.lastUpdateTime); + + } catch (NameNotFoundException e) { + if (DEBUG) Log.d(TAG, "Application not installed " + packageName); + entryUpdated = false; + } + } + + // Only add a filled-out entry to the cache + if (entryUpdated) { + mCache.put(cacheKey, entry); + } + } + return entry; + } + + protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { + Cursor c = null; + try { + c = mIconDb.query( + lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES, + IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", + new String[]{ + cacheKey.componentName.flattenToString(), + Long.toString(getSerialNumberForUser(cacheKey.user))}); + if (c.moveToNext()) { + // Set the alpha to be 255, so that we never have a wrong color + entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255)); + entry.title = c.getString(1); + if (entry.title == null) { + entry.title = ""; + entry.contentDescription = ""; + } else { + entry.contentDescription = mPackageManager.getUserBadgedLabel( + entry.title, cacheKey.user); + } + + if (!lowRes) { + try { + entry.bitmap = BitmapInfo.fromByteArray( + c.getBlob(2), entry.bitmap.color, cacheKey.user, this, mContext); + } catch (Exception e) { + return false; + } + } + return entry.bitmap != null; + } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + } finally { + if (c != null) { + c.close(); + } + } + return false; + } + + /** + * Returns a cursor for an arbitrary query to the cache db + */ + public synchronized Cursor queryCacheDb(String[] columns, String selection, + String[] selectionArgs) { + return mIconDb.query(columns, selection, selectionArgs); + } + + /** + * Cache class to store the actual entries on disk + */ + public static final class IconDB extends SQLiteCacheHelper { + private static final int RELEASE_VERSION = 31; + + public static final String TABLE_NAME = "icons"; + public static final String COLUMN_ROWID = "rowid"; + public static final String COLUMN_COMPONENT = "componentName"; + public static final String COLUMN_USER = "profileId"; + public static final String COLUMN_LAST_UPDATED = "lastUpdated"; + public static final String COLUMN_VERSION = "version"; + public static final String COLUMN_ICON = "icon"; + public static final String COLUMN_ICON_COLOR = "icon_color"; + public static final String COLUMN_LABEL = "label"; + public static final String COLUMN_SYSTEM_STATE = "system_state"; + public static final String COLUMN_KEYWORDS = "keywords"; + + public static final String[] COLUMNS_HIGH_RES = new String[] { + IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON }; + public static final String[] COLUMNS_LOW_RES = new String[] { + IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL }; + + public IconDB(Context context, String dbFileName, int iconPixelSize) { + super(context, dbFileName, (RELEASE_VERSION << 16) + iconPixelSize, TABLE_NAME); + } + + @Override + protected void onCreateTable(SQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + + COLUMN_COMPONENT + " TEXT NOT NULL, " + + COLUMN_USER + " INTEGER NOT NULL, " + + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_ICON + " BLOB, " + + COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_LABEL + " TEXT, " + + COLUMN_SYSTEM_STATE + " TEXT, " + + COLUMN_KEYWORDS + " TEXT, " + + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + + ");"); + } + } + + private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, + String packageName, @Nullable String keywords) { + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_ICON, bitmapInfo.toByteArray()); + values.put(IconDB.COLUMN_ICON_COLOR, bitmapInfo.color); + + values.put(IconDB.COLUMN_LABEL, label); + values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName)); + values.put(IconDB.COLUMN_KEYWORDS, keywords); + return values; + } + + private void assertWorkerThread() { + if (Looper.myLooper() != mBgLooper) { + throw new IllegalStateException("Cache accessed on wrong thread " + Looper.myLooper()); + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java new file mode 100644 index 0000000000000000000000000000000000000000..c12e9dcc152c9f486cbd39fe26c3ad28a71a7065 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java @@ -0,0 +1,65 @@ +/* + * 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.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.os.LocaleList; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.icons.BitmapInfo; + +public interface CachingLogic { + + ComponentName getComponent(T object); + + UserHandle getUser(T object); + + CharSequence getLabel(T object); + + default CharSequence getDescription(T object, CharSequence fallback) { + return fallback; + } + + @NonNull + BitmapInfo loadIcon(Context context, T object); + + /** + * Provides a option list of keywords to associate with this object + */ + @Nullable + default String getKeywords(T object, LocaleList localeList) { + return null; + } + + /** + * Returns the timestamp the entry was last updated in cache. + */ + default long getLastUpdatedTime(T object, PackageInfo info) { + return info.lastUpdateTime; + } + + /** + * Returns true the object should be added to mem cache; otherwise returns false. + */ + default boolean addToMemCache() { + return true; + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java new file mode 100644 index 0000000000000000000000000000000000000000..3dfb3840f95146b723e020b0fcfcd67175407843 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java @@ -0,0 +1,79 @@ +/* + * 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 implements Runnable { + + private final Handler mWorkerHandler; + private final Supplier mTask; + + private final Executor mCallbackExecutor; + private final Consumer mCallback; + private final Runnable mEndRunnable; + + private boolean mEnded = false; + private boolean mCanceled = false; + + public HandlerRunnable(Handler workerHandler, Supplier task, Executor callbackExecutor, + Consumer callback) { + this(workerHandler, task, callbackExecutor, callback, () -> { }); + } + + public HandlerRunnable(Handler workerHandler, Supplier task, Executor callbackExecutor, + Consumer 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(); + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..9e1ad7b7b2cd2ddc16e71e478f8d1cb5158c2790 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java @@ -0,0 +1,313 @@ +/* + * 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.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseBooleanArray; + +import com.android.launcher3.icons.cache.BaseIconCache.IconDB; + +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 + */ +public class IconCacheUpdateHandler { + + private static final String TAG = "IconCacheUpdateHandler"; + + /** + * In this mode, all invalid icons are marked as to-be-deleted in {@link #mItemsToDelete}. + * This mode is used for the first run. + */ + private static final boolean MODE_SET_INVALID_ITEMS = true; + + /** + * In this mode, any valid icon is removed from {@link #mItemsToDelete}. This is used for all + * subsequent runs, which essentially acts as set-union of all valid items. + */ + private static final boolean MODE_CLEAR_VALID_ITEMS = false; + + private static final Object ICON_UPDATE_TOKEN = new Object(); + + private final HashMap mPkgInfoMap; + private final BaseIconCache mIconCache; + + private final ArrayMap> mPackagesToIgnore = new ArrayMap<>(); + + private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray(); + private boolean mFilterMode = MODE_SET_INVALID_ITEMS; + + IconCacheUpdateHandler(BaseIconCache cache) { + mIconCache = cache; + + mPkgInfoMap = new HashMap<>(); + + // Remove all active icon update tasks. + mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); + + createPackageInfoMap(); + } + + /** + * Sets a package to ignore for processing + */ + public void addPackagesToIgnore(UserHandle userHandle, String packageName) { + Set packages = mPackagesToIgnore.get(userHandle); + if (packages == null) { + packages = new HashSet<>(); + mPackagesToIgnore.put(userHandle, packages); + } + packages.add(packageName); + } + + private void createPackageInfoMap() { + PackageManager pm = mIconCache.mPackageManager; + for (PackageInfo info : + pm.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES)) { + mPkgInfoMap.put(info.packageName, info); + } + } + + /** + * 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 void updateIcons(List apps, CachingLogic cachingLogic, + OnUpdateCallback onUpdateCallback) { + // Filter the list per user + HashMap> userComponentMap = new HashMap<>(); + int count = apps.size(); + for (int i = 0; i < count; i++) { + T app = apps.get(i); + UserHandle userHandle = cachingLogic.getUser(app); + HashMap componentMap = userComponentMap.get(userHandle); + if (componentMap == null) { + componentMap = new HashMap<>(); + userComponentMap.put(userHandle, componentMap); + } + componentMap.put(cachingLogic.getComponent(app), app); + } + + for (Entry> entry : userComponentMap.entrySet()) { + updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback); + } + + // From now on, clear every valid item from the global valid map. + mFilterMode = MODE_CLEAR_VALID_ITEMS; + } + + /** + * 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") + private void updateIconsPerUser(UserHandle user, HashMap componentMap, + CachingLogic cachingLogic, OnUpdateCallback onUpdateCallback) { + Set ignorePackages = mPackagesToIgnore.get(user); + if (ignorePackages == null) { + ignorePackages = Collections.emptySet(); + } + long userSerial = mIconCache.getSerialNumberForUser(user); + + Stack appsToUpdate = new Stack<>(); + + try (Cursor c = mIconCache.mIconDb.query( + new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, + IconDB.COLUMN_SYSTEM_STATE}, + IconDB.COLUMN_USER + " = ? ", + new String[]{Long.toString(userSerial)})) { + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = mPkgInfoMap.get(component.getPackageName()); + + int rowId = c.getInt(rowIndex); + if (info == null) { + if (!ignorePackages.contains(component.getPackageName())) { + + if (mFilterMode == MODE_SET_INVALID_ITEMS) { + mIconCache.remove(component, user); + mItemsToDelete.put(rowId, true); + } + } + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + T app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime + && TextUtils.equals(c.getString(systemStateIndex), + mIconCache.getIconSystemState(info.packageName))) { + + if (mFilterMode == MODE_CLEAR_VALID_ITEMS) { + mItemsToDelete.put(rowId, false); + } + continue; + } + + if (app == null) { + if (mFilterMode == MODE_SET_INVALID_ITEMS) { + mIconCache.remove(component, user); + mItemsToDelete.put(rowId, true); + } + } else { + appsToUpdate.add(app); + } + } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + // Continue updating whatever we have read so far + } + + // Insert remaining apps. + if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { + Stack appsToAdd = new Stack<>(); + appsToAdd.addAll(componentMap.values()); + new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic, + onUpdateCallback).scheduleNext(); + } + } + + /** + * Commits all updates as part of the update handler to disk. Not more calls should be made + * to this class after this. + */ + public void finish() { + // Commit all deletes + int deleteCount = 0; + StringBuilder queryBuilder = new StringBuilder() + .append(IconDB.COLUMN_ROWID) + .append(" IN ("); + + int count = mItemsToDelete.size(); + for (int i = 0; i < count; i++) { + if (mItemsToDelete.valueAt(i)) { + if (deleteCount > 0) { + queryBuilder.append(", "); + } + queryBuilder.append(mItemsToDelete.keyAt(i)); + deleteCount++; + } + } + queryBuilder.append(')'); + + if (deleteCount > 0) { + mIconCache.mIconDb.delete(queryBuilder.toString(), null); + } + } + + /** + * A runnable that updates invalid icons and adds missing icons in the DB for the provided + * LauncherActivityInfo list. Items are updated/added one at a time, so that the + * worker thread doesn't get blocked. + */ + private class SerializedIconUpdateTask implements Runnable { + private final long mUserSerial; + private final UserHandle mUserHandle; + private final Stack mAppsToAdd; + private final Stack mAppsToUpdate; + private final CachingLogic mCachingLogic; + private final HashSet mUpdatedPackages = new HashSet<>(); + private final OnUpdateCallback mOnUpdateCallback; + + SerializedIconUpdateTask(long userSerial, UserHandle userHandle, + Stack appsToAdd, Stack appsToUpdate, CachingLogic cachingLogic, + OnUpdateCallback onUpdateCallback) { + mUserHandle = userHandle; + mUserSerial = userSerial; + mAppsToAdd = appsToAdd; + mAppsToUpdate = appsToUpdate; + mCachingLogic = cachingLogic; + mOnUpdateCallback = onUpdateCallback; + } + + @Override + public void run() { + if (!mAppsToUpdate.isEmpty()) { + T app = mAppsToUpdate.pop(); + String pkg = mCachingLogic.getComponent(app).getPackageName(); + PackageInfo info = mPkgInfoMap.get(pkg); + + mIconCache.addIconToDBAndMemCache( + app, mCachingLogic, info, mUserSerial, true /*replace existing*/); + mUpdatedPackages.add(pkg); + + if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { + // No more app to update. Notify callback. + mOnUpdateCallback.onPackageIconsUpdated(mUpdatedPackages, mUserHandle); + } + + // Let it run one more time. + scheduleNext(); + } else if (!mAppsToAdd.isEmpty()) { + T app = mAppsToAdd.pop(); + 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) { + mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info, + mUserSerial, false /*replace existing*/); + } + + if (!mAppsToAdd.isEmpty()) { + scheduleNext(); + } + } + } + + public void scheduleNext() { + mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, + SystemClock.uptimeMillis() + 1); + } + } + + public interface OnUpdateCallback { + + void onPackageIconsUpdated(HashSet updatedPackages, UserHandle user); + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java b/PermissionController/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java new file mode 100644 index 0000000000000000000000000000000000000000..71451031dea9fd02b474a40772c1146d04526c7b --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java @@ -0,0 +1,84 @@ +package com.android.launcher3.util; + +/** + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.ComponentName; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Arrays; + +public class ComponentKey { + + public final ComponentName componentName; + public final UserHandle user; + + private final int mHashCode; + + public ComponentKey(ComponentName componentName, UserHandle user) { + if (componentName == null || user == null) { + throw new NullPointerException(); + } + this.componentName = componentName; + this.user = user; + mHashCode = Arrays.hashCode(new Object[] {componentName, user}); + + } + + @Override + public int hashCode() { + return mHashCode; + } + + @Override + public boolean equals(Object o) { + ComponentKey other = (ComponentKey) o; + return other.componentName.equals(componentName) && other.user.equals(user); + } + + /** + * Encodes a component key as a string of the form [flattenedComponentString#userId]. + */ + @Override + public String toString() { + return componentName.flattenToString() + "#" + user.hashCode(); + } + + /** + * Parses and returns ComponentKey objected from string representation + * Returns null if string is not properly formatted + */ + @Nullable + public static ComponentKey fromString(@NonNull String str) { + int sep = str.indexOf('#'); + if (sep < 0 || (sep + 1) >= str.length()) { + return null; + } + ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep)); + if (componentName == null) { + return null; + } + try { + return new ComponentKey(componentName, + UserHandle.getUserHandleForUid(Integer.parseInt(str.substring(sep + 1)))); + } catch (NumberFormatException ex) { + return null; + } + } +} \ No newline at end of file diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/PermissionController/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..fe864a2847641fcc105fb0202b89da347614bafa --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java @@ -0,0 +1,58 @@ +/* + * 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.util; + +import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS; + +import android.content.Context; +import android.content.ContextWrapper; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteDatabase.OpenParams; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.Build; + +/** + * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by + * A context wrapper which creates databases without support for localized collators. + */ +public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper { + + private static final boolean ATLEAST_P = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + + public NoLocaleSQLiteHelper(Context context, String name, int version) { + super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version); + if (ATLEAST_P) { + setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build()); + } + } + + private static class NoLocalContext extends ContextWrapper { + public NoLocalContext(Context base) { + super(base); + } + + @Override + public SQLiteDatabase openOrCreateDatabase( + String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) { + return super.openOrCreateDatabase( + name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler); + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java b/PermissionController/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..49de4bd1bfbf671ca462d818a44553acea2e42b5 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java @@ -0,0 +1,125 @@ +package com.android.launcher3.util; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteFullException; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +/** + * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB. + * Any exception during write operations are ignored, and any version change causes a DB reset. + */ +public abstract class SQLiteCacheHelper { + private static final String TAG = "SQLiteCacheHelper"; + + private static final boolean IN_MEMORY_CACHE = false; + + private final String mTableName; + private final MySQLiteOpenHelper mOpenHelper; + + private boolean mIgnoreWrites; + + public SQLiteCacheHelper(Context context, String name, int version, String tableName) { + if (IN_MEMORY_CACHE) { + name = null; + } + mTableName = tableName; + mOpenHelper = new MySQLiteOpenHelper(context, name, version); + + mIgnoreWrites = false; + } + + /** + * @see SQLiteDatabase#delete(String, String, String[]) + */ + public void delete(String whereClause, String[] whereArgs) { + if (mIgnoreWrites) { + return; + } + try { + mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs); + } catch (SQLiteFullException e) { + onDiskFull(e); + } catch (SQLiteException e) { + Log.d(TAG, "Ignoring sqlite exception", e); + } + } + + /** + * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int) + */ + public void insertOrReplace(ContentValues values) { + if (mIgnoreWrites) { + return; + } + try { + mOpenHelper.getWritableDatabase().insertWithOnConflict( + mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE); + } catch (SQLiteFullException e) { + onDiskFull(e); + } catch (SQLiteException e) { + Log.d(TAG, "Ignoring sqlite exception", e); + } + } + + private void onDiskFull(SQLiteFullException e) { + Log.e(TAG, "Disk full, all write operations will be ignored", e); + mIgnoreWrites = true; + } + + /** + * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String) + */ + public Cursor query(String[] columns, String selection, String[] selectionArgs) { + return mOpenHelper.getReadableDatabase().query( + mTableName, columns, selection, selectionArgs, null, null, null); + } + + public void clear() { + mOpenHelper.clearDB(mOpenHelper.getWritableDatabase()); + } + + public void close() { + mOpenHelper.close(); + } + + protected abstract void onCreateTable(SQLiteDatabase db); + + /** + * A private inner class to prevent direct DB access. + */ + private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper { + + public MySQLiteOpenHelper(Context context, String name, int version) { + super(context, name, version); + } + + @Override + public void onCreate(SQLiteDatabase db) { + onCreateTable(db); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } + + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } + + private void clearDB(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + mTableName); + onCreate(db); + } + } +} diff --git a/PermissionController/iconloaderlib/src/com/android/launcher3/util/SafeCloseable.java b/PermissionController/iconloaderlib/src/com/android/launcher3/util/SafeCloseable.java new file mode 100644 index 0000000000000000000000000000000000000000..ba8ee04d278007a7008c58cf4924474b0a246dc0 --- /dev/null +++ b/PermissionController/iconloaderlib/src/com/android/launcher3/util/SafeCloseable.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 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; + +/** + * Extension of closeable which does not throw an exception + */ +public interface SafeCloseable extends AutoCloseable { + + @Override + void close(); +} diff --git a/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java b/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..48f11fde3bdd3ca25bde9c82903e2b4c44c0d579 --- /dev/null +++ b/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java @@ -0,0 +1,89 @@ +/* + * 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; + +import android.content.Context; + +/** + * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class + * that are threadsafe. + */ +public class IconFactory extends BaseIconFactory { + + private static final Object sPoolSync = new Object(); + private static IconFactory sPool; + private static int sPoolId = 0; + + /** + * Return a new Message instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static IconFactory obtain(Context context) { + int poolId; + synchronized (sPoolSync) { + if (sPool != null) { + IconFactory m = sPool; + sPool = m.next; + m.next = null; + return m; + } + poolId = sPoolId; + } + + return new IconFactory(context, + context.getResources().getConfiguration().densityDpi, + context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), + poolId); + } + + public static void clearPool() { + synchronized (sPoolSync) { + sPool = null; + sPoolId++; + } + } + + private final int mPoolId; + + private IconFactory next; + + private IconFactory(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { + super(context, fillResIconDpi, iconBitmapSize); + mPoolId = poolId; + } + + /** + * Recycles a LauncherIcons that may be in-use. + */ + public void recycle() { + synchronized (sPoolSync) { + if (sPoolId != mPoolId) { + return; + } + // Clear any temporary state variables + clear(); + + next = sPool; + sPool = this; + } + } + + @Override + public void close() { + recycle(); + } +} diff --git a/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java b/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java new file mode 100644 index 0000000000000000000000000000000000000000..cc4ad7b0b217dca8d80efc636e3583af533fe2b7 --- /dev/null +++ b/PermissionController/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java @@ -0,0 +1,114 @@ +/* + * 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; + +import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED; +import static android.content.Intent.ACTION_MANAGED_PROFILE_REMOVED; + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.SparseLongArray; + +import com.android.launcher3.icons.cache.BaseIconCache; + +/** + * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class + * that are threadsafe. + */ +@TargetApi(Build.VERSION_CODES.P) +public class SimpleIconCache extends BaseIconCache { + + private static SimpleIconCache sIconCache = null; + private static final Object CACHE_LOCK = new Object(); + + private final SparseLongArray mUserSerialMap = new SparseLongArray(2); + private final UserManager mUserManager; + + public SimpleIconCache(Context context, String dbFileName, Looper bgLooper, int iconDpi, + int iconPixelSize, boolean inMemoryCache) { + super(context, dbFileName, bgLooper, iconDpi, iconPixelSize, inMemoryCache); + mUserManager = context.getSystemService(UserManager.class); + + // Listen for user cache changes. + IntentFilter filter = new IntentFilter(ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(ACTION_MANAGED_PROFILE_REMOVED); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + resetUserCache(); + } + }, filter, null, new Handler(bgLooper), 0); + } + + @Override + protected long getSerialNumberForUser(UserHandle user) { + synchronized (mUserSerialMap) { + int index = mUserSerialMap.indexOfKey(user.getIdentifier()); + if (index >= 0) { + return mUserSerialMap.valueAt(index); + } + long serial = mUserManager.getSerialNumberForUser(user); + mUserSerialMap.put(user.getIdentifier(), serial); + return serial; + } + } + + private void resetUserCache() { + synchronized (mUserSerialMap) { + mUserSerialMap.clear(); + } + } + + @Override + protected boolean isInstantApp(ApplicationInfo info) { + return info.isInstantApp(); + } + + @Override + public BaseIconFactory getIconFactory() { + return IconFactory.obtain(mContext); + } + + public static SimpleIconCache getIconCache(Context context) { + synchronized (CACHE_LOCK) { + if (sIconCache != null) { + return sIconCache; + } + boolean inMemoryCache = + context.getResources().getBoolean(R.bool.simple_cache_enable_im_memory); + String dbFileName = context.getString(R.string.cache_db_name); + + HandlerThread bgThread = new HandlerThread("simple-icon-cache"); + bgThread.start(); + + sIconCache = new SimpleIconCache(context.getApplicationContext(), dbFileName, + bgThread.getLooper(), context.getResources().getConfiguration().densityDpi, + context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), + inMemoryCache); + return sIconCache; + } + } +} diff --git a/PermissionController/lint-baseline.xml b/PermissionController/lint-baseline.xml new file mode 100644 index 0000000000000000000000000000000000000000..05a3072344d653064f96fab7c6c59cb79d5210b1 --- /dev/null +++ b/PermissionController/lint-baseline.xml @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/proguard.flags b/PermissionController/proguard.flags index 1f0b0326988a86fd09dfc91adc0f4df532f10087..13590aa39656ad2dc38bc60b2e35cab78c79f347 100644 --- a/PermissionController/proguard.flags +++ b/PermissionController/proguard.flags @@ -8,7 +8,12 @@ -dontwarn androidx.core.** # Keep classes that implements RoleBehavior, which are used by reflection. --keep class * implements com.android.permissioncontroller.role.model.RoleBehavior { +-keep class * implements com.android.role.controller.model.RoleBehavior { + *; +} + +# Keep classes that implements RoleUiBehavior, which are used by reflection. +-keep class * implements com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior { *; } @@ -25,4 +30,4 @@ *** get*(); *** set*(***); *** has*(); -} \ No newline at end of file +} diff --git a/PermissionController/res/color-v33/safety_center_button_info.xml b/PermissionController/res/color-v33/safety_center_button_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..ef4b24050b46a778c937a672c912d5fe8b4485ed --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_button_info.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_button_recommend.xml b/PermissionController/res/color-v33/safety_center_button_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..1db5b7cc64092766315643ed3edad520fb941219 --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_button_recommend.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_button_warn.xml b/PermissionController/res/color-v33/safety_center_button_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..8a21c01501ad9ab545241b608c33afab02962b9b --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_button_warn.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_outline_button_info.xml b/PermissionController/res/color-v33/safety_center_outline_button_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..19c5167af21f6389d8b2869d1054953513f41f5f --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_outline_button_info.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_outline_button_recommend.xml b/PermissionController/res/color-v33/safety_center_outline_button_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..daed711e4d6045f7ccf69bb0233a71dc24e9c854 --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_outline_button_recommend.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_outline_button_warn.xml b/PermissionController/res/color-v33/safety_center_outline_button_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..b29b8f7174bdcbe0f5d4f47ebc0eed4954da440f --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_outline_button_warn.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/PermissionController/res/color-v33/safety_center_toggle_disabled.xml b/PermissionController/res/color-v33/safety_center_toggle_disabled.xml new file mode 100644 index 0000000000000000000000000000000000000000..496ede7fe07bd61d17d97ddb337f11e9953e5ea3 --- /dev/null +++ b/PermissionController/res/color-v33/safety_center_toggle_disabled.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/PermissionController/res/color-v33/sc_surface_light.xml b/PermissionController/res/color-v33/sc_surface_light.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac63072a9e5a568e055049156d7b4ea9c832c27f --- /dev/null +++ b/PermissionController/res/color-v33/sc_surface_light.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/PermissionController/res/color-v34/preference_highlight_color.xml b/PermissionController/res/color-v34/preference_highlight_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2889bf482f2b4a3aaa1f1a4afa609ebcb5cc22f --- /dev/null +++ b/PermissionController/res/color-v34/preference_highlight_color.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v31/safety_status_info.xml b/PermissionController/res/drawable-v31/safety_status_info.xml deleted file mode 100644 index d1c1a6e6ac0b8e0143c0114a4229e297dfece2ae..0000000000000000000000000000000000000000 --- a/PermissionController/res/drawable-v31/safety_status_info.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - diff --git a/PermissionController/res/drawable-v31/safety_status_info_review.xml b/PermissionController/res/drawable-v31/safety_status_info_review.xml deleted file mode 100644 index f043cd0e6eceadefce3e50a2d81509fb6db6924b..0000000000000000000000000000000000000000 --- a/PermissionController/res/drawable-v31/safety_status_info_review.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - diff --git a/PermissionController/res/drawable-v31/safety_status_recommendation.xml b/PermissionController/res/drawable-v31/safety_status_recommendation.xml deleted file mode 100644 index c1c341bd6d976717999ce37f2ec8becb914d32a1..0000000000000000000000000000000000000000 --- a/PermissionController/res/drawable-v31/safety_status_recommendation.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - diff --git a/PermissionController/res/drawable-v31/safety_status_warn.xml b/PermissionController/res/drawable-v31/safety_status_warn.xml deleted file mode 100644 index 2c69c0483b08e7046b6a820351bc62f4cb7b0c9f..0000000000000000000000000000000000000000 --- a/PermissionController/res/drawable-v31/safety_status_warn.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - diff --git a/PermissionController/res/drawable-v31/ic_check.xml b/PermissionController/res/drawable-v33/ic_check.xml similarity index 100% rename from PermissionController/res/drawable-v31/ic_check.xml rename to PermissionController/res/drawable-v33/ic_check.xml diff --git a/PermissionController/res/drawable-v31/ic_block.xml b/PermissionController/res/drawable-v33/ic_chevron_right.xml similarity index 77% rename from PermissionController/res/drawable-v31/ic_block.xml rename to PermissionController/res/drawable-v33/ic_chevron_right.xml index b77c260348705e573f9064aa926909a54d8e0b90..be997e8de3a574e05f8109f21edf77e3a93d3a0f 100644 --- a/PermissionController/res/drawable-v31/ic_block.xml +++ b/PermissionController/res/drawable-v33/ic_chevron_right.xml @@ -22,5 +22,5 @@ android:tint="?attr/colorControlNormal"> + android:pathData="M9.4,18 L8,16.6 12.6,12 8,7.4 9.4,6 15.4,12Z"/> \ No newline at end of file diff --git a/PermissionController/res/drawable/safety_shield.xml b/PermissionController/res/drawable-v33/ic_collapse_issues.xml similarity index 69% rename from PermissionController/res/drawable/safety_shield.xml rename to PermissionController/res/drawable-v33/ic_collapse_issues.xml index d41f378532de5a204fe19e0e215b1dd473ec96d3..182371d8619497b730fd64de314e1a60bc21389b 100644 --- a/PermissionController/res/drawable/safety_shield.xml +++ b/PermissionController/res/drawable-v33/ic_collapse_issues.xml @@ -1,5 +1,5 @@ + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v31/ic_expand_less.xml b/PermissionController/res/drawable-v33/ic_expand_less.xml similarity index 100% rename from PermissionController/res/drawable-v31/ic_expand_less.xml rename to PermissionController/res/drawable-v33/ic_expand_less.xml diff --git a/PermissionController/res/drawable-v31/ic_expand_more.xml b/PermissionController/res/drawable-v33/ic_expand_more.xml similarity index 100% rename from PermissionController/res/drawable-v31/ic_expand_more.xml rename to PermissionController/res/drawable-v33/ic_expand_more.xml diff --git a/PermissionController/res/drawable-v31/ic_safety_null_state.xml b/PermissionController/res/drawable-v33/ic_privacy.xml similarity index 56% rename from PermissionController/res/drawable-v31/ic_safety_null_state.xml rename to PermissionController/res/drawable-v33/ic_privacy.xml index d08e6097b0c64c2885f513e554645b0a5773815d..00e9ff099b40fe197152038cafdddb0cc9898805 100644 --- a/PermissionController/res/drawable-v31/ic_safety_null_state.xml +++ b/PermissionController/res/drawable-v33/ic_privacy.xml @@ -1,5 +1,5 @@ - - + - - diff --git a/PermissionController/res/drawable-v31/ic_safety_info_outline.xml b/PermissionController/res/drawable-v33/ic_safety_center_shield.xml similarity index 54% rename from PermissionController/res/drawable-v31/ic_safety_info_outline.xml rename to PermissionController/res/drawable-v33/ic_safety_center_shield.xml index b680ff1aee9b1653b4361c765192aab1d1b28fad..e817b7194bb80ae579c961ce8a02ca4ed7eea5ab 100644 --- a/PermissionController/res/drawable-v31/ic_safety_info_outline.xml +++ b/PermissionController/res/drawable-v33/ic_safety_center_shield.xml @@ -1,4 +1,4 @@ - - + android:viewportHeight="24" + android:tint="?android:attr/textColorPrimary"> + + + android:fillColor="#000000" + android:pathData="M12,4.14l6,2.25v4.71c0,2.12-0.62,4-1.88,5.74c-1.13,1.55-2.48,2.56-4.12,3.09c-1.64-0.52-2.99-1.54-4.12-3.09 C6.62,15.1,6,13.22,6,11.1V6.39L12,4.14 M12,2L4,5v6.1c0,2.53,0.75,4.84,2.26,6.91C7.77,20.09,9.68,21.42,12,22 c2.32-0.58,4.23-1.91,5.74-3.99C19.25,15.94,20,13.63,20,11.1V5L12,2L12,2z" /> - + android:fillColor="#000000" + android:pathData="M 10.95 15.55 L 7.42 12.02 L 8.85 10.6 L 10.95 12.7 L 15.17 8.47 L 16.6 9.9 Z" /> + \ No newline at end of file diff --git a/PermissionController/res/drawable-v31/ic_safety_empty.xml b/PermissionController/res/drawable-v33/ic_safety_empty.xml similarity index 100% rename from PermissionController/res/drawable-v31/ic_safety_empty.xml rename to PermissionController/res/drawable-v33/ic_safety_empty.xml diff --git a/PermissionController/res/drawable-v31/ic_safety_recommendation_outline.xml b/PermissionController/res/drawable-v33/ic_safety_group_collapse.xml similarity index 69% rename from PermissionController/res/drawable-v31/ic_safety_recommendation_outline.xml rename to PermissionController/res/drawable-v33/ic_safety_group_collapse.xml index 2b7f554d92a994aa87894b9b1e882da273eb22f5..a804b6e9ea765144f6312d8093d8280b1fa575e6 100644 --- a/PermissionController/res/drawable-v31/ic_safety_recommendation_outline.xml +++ b/PermissionController/res/drawable-v33/ic_safety_group_collapse.xml @@ -19,8 +19,10 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + + diff --git a/PermissionController/res/drawable-v33/ic_safety_group_expand.xml b/PermissionController/res/drawable-v33/ic_safety_group_expand.xml new file mode 100644 index 0000000000000000000000000000000000000000..bf0606d7a6ed99d0bb079a7135dac27af1a18d09 --- /dev/null +++ b/PermissionController/res/drawable-v33/ic_safety_group_expand.xml @@ -0,0 +1,12 @@ + + + + diff --git a/PermissionController/res/drawable-v33/ic_safety_info.xml b/PermissionController/res/drawable-v33/ic_safety_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..a3032ae727ebda5a1b2ec9aad8aea24d0d654611 --- /dev/null +++ b/PermissionController/res/drawable-v33/ic_safety_info.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/PermissionController/res/drawable-v31/ic_safety_info.xml b/PermissionController/res/drawable-v33/ic_safety_issue_dismiss.xml similarity index 60% rename from PermissionController/res/drawable-v31/ic_safety_info.xml rename to PermissionController/res/drawable-v33/ic_safety_issue_dismiss.xml index e245d92d5e7d36242153339cc1bafe7d12275eb6..c2188c4594620dd7c5cf72a2daa203cfe007e994 100644 --- a/PermissionController/res/drawable-v31/ic_safety_info.xml +++ b/PermissionController/res/drawable-v33/ic_safety_issue_dismiss.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + android:width="20dp" + android:height="20dp" + android:viewportWidth="20" + android:viewportHeight="20"> + + diff --git a/PermissionController/res/drawable-v31/ic_safety_recommendation.xml b/PermissionController/res/drawable-v33/ic_safety_recommendation.xml similarity index 60% rename from PermissionController/res/drawable-v31/ic_safety_recommendation.xml rename to PermissionController/res/drawable-v33/ic_safety_recommendation.xml index a11963d45c0a5369b73dcc07ce96a7dc004c5fad..13366c25c72bc3fb54b9cb0ac159a10c21354b1a 100644 --- a/PermissionController/res/drawable-v31/ic_safety_recommendation.xml +++ b/PermissionController/res/drawable-v33/ic_safety_recommendation.xml @@ -1,5 +1,6 @@ - - + android:width="20dp" + android:height="20dp" + android:viewportWidth="20" + android:viewportHeight="20"> + diff --git a/PermissionController/res/drawable-v31/ic_safety_warn.xml b/PermissionController/res/drawable-v33/ic_safety_warn.xml similarity index 60% rename from PermissionController/res/drawable-v31/ic_safety_warn.xml rename to PermissionController/res/drawable-v33/ic_safety_warn.xml index 94cfa78317b82949b839ab8e868ae901d5bde76b..d4bc306c0bbb6323f83c41b159841050606c0975 100644 --- a/PermissionController/res/drawable-v31/ic_safety_warn.xml +++ b/PermissionController/res/drawable-v33/ic_safety_warn.xml @@ -1,5 +1,6 @@ - - + android:width="20dp" + android:height="20dp" + android:viewportWidth="20" + android:viewportHeight="20"> + diff --git a/PermissionController/res/drawable-v33/ic_settings_gear.xml b/PermissionController/res/drawable-v33/ic_settings_gear.xml new file mode 100644 index 0000000000000000000000000000000000000000..e2284a71b225edacb4d818d4ce72ef4b8cd04821 --- /dev/null +++ b/PermissionController/res/drawable-v33/ic_settings_gear.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v31/ic_history.xml b/PermissionController/res/drawable-v33/ic_settings_info.xml similarity index 54% rename from PermissionController/res/drawable-v31/ic_history.xml rename to PermissionController/res/drawable-v33/ic_settings_info.xml index 24ed38a65d6d1aaa79270325a647f05b08dab498..f5fee3a52f4d1570214e472c908c964a9c813e2d 100644 --- a/PermissionController/res/drawable-v31/ic_history.xml +++ b/PermissionController/res/drawable-v33/ic_settings_info.xml @@ -13,17 +13,18 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:fillColor="?android:attr/textColorSecondary" + android:pathData="M11,7h2v2h-2z"/> - \ No newline at end of file + android:fillColor="?android:attr/textColorSecondary" + android:pathData="M11,11h2v6h-2z"/> + + diff --git a/PermissionController/res/drawable-v33/indicator_background_circle.xml b/PermissionController/res/drawable-v33/indicator_background_circle.xml new file mode 100644 index 0000000000000000000000000000000000000000..2292a9e5c9b2e7121eb00035bde2277f44274fe2 --- /dev/null +++ b/PermissionController/res/drawable-v33/indicator_background_circle.xml @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/more_issues_collapse_anim.xml b/PermissionController/res/drawable-v33/more_issues_collapse_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..8bac610bf0cf63a2a1fe8d6cf998c14039d2fcef --- /dev/null +++ b/PermissionController/res/drawable-v33/more_issues_collapse_anim.xml @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/more_issues_expand_anim.xml b/PermissionController/res/drawable-v33/more_issues_expand_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..048b74a30f8a0a01d10175064fe5d1eaa1f2bca6 --- /dev/null +++ b/PermissionController/res/drawable-v33/more_issues_expand_anim.xml @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v31/safety_center_card_background.xml b/PermissionController/res/drawable-v33/safety_center_card_background.xml similarity index 86% rename from PermissionController/res/drawable-v31/safety_center_card_background.xml rename to PermissionController/res/drawable-v33/safety_center_card_background.xml index 2af4e43c48cb10d8c731bb968ad356edac61c560..e04b32f4f171696eee82285f741ccb20fd5761fa 100644 --- a/PermissionController/res/drawable-v31/safety_center_card_background.xml +++ b/PermissionController/res/drawable-v33/safety_center_card_background.xml @@ -16,6 +16,6 @@ --> - - + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_card_widget_background.xml b/PermissionController/res/drawable-v33/safety_center_card_widget_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..f7ff2f2cb5dd1f93867e5fa3d274e81e2b13307a --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_card_widget_background.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_group_collapse_anim.xml b/PermissionController/res/drawable-v33/safety_center_group_collapse_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c2a33e56590f3bc87434c8ce8a369e4b4cc3625 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_group_collapse_anim.xml @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_group_expand_anim.xml b/PermissionController/res/drawable-v33/safety_center_group_expand_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..46102721bc06022da58cba118af9eabfebcf5415 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_group_expand_anim.xml @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_issue_resolved_avd.xml b/PermissionController/res/drawable-v33/safety_center_issue_resolved_avd.xml new file mode 100644 index 0000000000000000000000000000000000000000..7d27c1ad26fbdc078b2d28cd820b64893ac3cb26 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_issue_resolved_avd.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_more_issues_card_background.xml b/PermissionController/res/drawable-v33/safety_center_more_issues_card_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c423a6d16e43da203f37bca923f2258b57e45b2 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_more_issues_card_background.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v33/safety_center_sensor_toggle_disabled.xml b/PermissionController/res/drawable-v33/safety_center_sensor_toggle_disabled.xml new file mode 100644 index 0000000000000000000000000000000000000000..364fbd9bba895167168b53e0aec4210da2a2502c --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_sensor_toggle_disabled.xml @@ -0,0 +1,25 @@ + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_center_sensor_toggle_enabled.xml b/PermissionController/res/drawable-v33/safety_center_sensor_toggle_enabled.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd6957e821b3046bc6a22bfa966134cb0f22ed64 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_center_sensor_toggle_enabled.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_flat_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_flat_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..18e99bd733dec760c4290ab355cf11ffc165773d --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_flat_background.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_large_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_large_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..ef78867b87f20693c081537ac19ec734729cc56a --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_large_background.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_small_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_small_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd455b0f6d501a835f573acf1365298a21e139a4 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_flat_bottom_small_background.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_flat_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_flat_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..2326ce4bd64ef7cf6a1ff3e8cf2dfd00172779b3 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_flat_background.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_large_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_large_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5a60e85b7a296c48b150a8148ebcde5724b3212 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_large_background.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_small_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_small_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..2cbe94e8f5096fa10c45fcc2341e91efb4064125 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_large_bottom_small_background.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_flat_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_flat_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..eced55ee8ad27fb880a9aa7a0d483fa9a5acf079 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_flat_background.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_large_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_large_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..5e26cd41ad474ff8bed01589db9509e7749274ad --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_large_background.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_small_background.xml b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_small_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c8df5022507d1252a4205a258e22bf57090f9ec --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entity_top_small_bottom_small_background.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_entry_icon_action_background.xml b/PermissionController/res/drawable-v33/safety_entry_icon_action_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..dc6a3fa28633bfc6b89b558753b87f7d9154b25b --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_entry_icon_action_background.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_group_entry_background.xml b/PermissionController/res/drawable-v33/safety_group_entry_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..66d98fe6429acda3dd6ad3bcda6c91f8c809ade4 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_group_entry_background.xml @@ -0,0 +1,24 @@ + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_info.xml b/PermissionController/res/drawable-v33/safety_status_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..e52ed3d60afd19bcc352296fa8088399da5e508d --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_info.xml @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..31b5982ba662bd78f13906842158f021ff3184cf --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_info_to_info_anim.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..6e63c5097f777b8a02489669fbe6953c45ed9f88 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_recommend_to_info_anim.xml @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_recommendation.xml b/PermissionController/res/drawable-v33/safety_status_recommendation.xml new file mode 100644 index 0000000000000000000000000000000000000000..69b3b89027c7bcce553a7213eed0e47d552c1dde --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_recommendation.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/PermissionController/res/drawable-v33/safety_status_small_info_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_info_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..8f8a0c5644cad724835b5d6461ee3c193f0922b8 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_info_to_info_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_info_to_recommendation_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_info_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..37bd68c44662c29b4a2f14d90b98458b6f78ab92 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_info_to_recommendation_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_info_to_warn_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_info_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc374e3707b5e6a928f90b0f7f46bb4c47df99fc --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_info_to_warn_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..881656e2836765945d781ee607a33b5700c4472c --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_info_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_recommendation_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..e7eb778847967c194d4a1c632b95017faded44fe --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_recommendation_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_warn_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..3016c4b823b9d12cbd0f3770348a3818b438d6a4 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_recommendation_to_warn_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_warn_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_warn_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..2921a281d60d45e3ff8b12a38c38eb7bc9b8c770 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_warn_to_info_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_warn_to_recommendation_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_warn_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..7efb6fc46a3f7701724abc723b355080eb2ae1c4 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_warn_to_recommendation_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_small_warn_to_warn_anim.xml b/PermissionController/res/drawable-v33/safety_status_small_warn_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..ce98c4af7ca89f8c324b03fbb6e39b22b4d66545 --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_small_warn_to_warn_anim.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_warn.xml b/PermissionController/res/drawable-v33/safety_status_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e50a7b184e4500e1ae81442bc175a4ae03f4daf --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_warn.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml b/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..36333360b2cac4ec7cafef141e3ad829269fdc3b --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_warn_to_info_anim.xml @@ -0,0 +1,697 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml b/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..f67521552fff3dac11975ab8475c906cc2537d4c --- /dev/null +++ b/PermissionController/res/drawable-v33/safety_status_warn_to_recommend_anim.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_info_to_scanning_anim.xml b/PermissionController/res/drawable-v33/status_info_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c22f6ae969788c3cc85d86a2ba9c8b817c449b4 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_info_to_scanning_anim.xml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_recommend_to_scanning_anim.xml b/PermissionController/res/drawable-v33/status_recommend_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..873fe92c0c3ce51a59bc8fb6b1f1e5bd91cf4fd7 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_recommend_to_scanning_anim.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_info.xml b/PermissionController/res/drawable-v33/status_scanning_anim_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..84564ed77f622a0daacfe01e82a149fb0181ffc9 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_anim_info.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml b/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..89fb3ca81ab161522efc9370becd5819df3df3af --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_anim_recommend.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml b/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..48c8ea7302cf46713410d1131e76141b4688ce48 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_anim_warn.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_info.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..5032e7203d1a15e4c10b3944d3afde6c33c58027 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_info.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_recommend.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d60a75324133f3b4333efdb80baf2705dbab953 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_recommend.xml @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_warn.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..9ce7d18ccad6be9e161217a1fe9bd67b57870f1d --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_info_to_warn.xml @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_info.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..f69fb1680ea17c4ac65317daa40920a782a5229b --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_info.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_recommend.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..8f8c9fb4647c3a85cca040d7a1389c09690cfcc3 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_recommend.xml @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_warn.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..cbe900d102ea084711a1a7eee57153eb4e03bd96 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_recommend_to_warn.xml @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_info.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..723675887e46b3cae3f98572e1833ea5234f1bec --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_info.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_recommend.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..15300ce0248e39972d325dac22f0095845a4a644 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_recommend.xml @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_warn.xml b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..86a05381547dcfeda670977919650f4b62323aee --- /dev/null +++ b/PermissionController/res/drawable-v33/status_scanning_end_anim_warn_to_warn.xml @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v33/status_warn_to_scanning_anim.xml b/PermissionController/res/drawable-v33/status_warn_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..c954c60c0660805c431dcaee75defce7ccfcf091 --- /dev/null +++ b/PermissionController/res/drawable-v33/status_warn_to_scanning_anim.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/ic_business.xml b/PermissionController/res/drawable-v34/ic_business.xml new file mode 100644 index 0000000000000000000000000000000000000000..23ee37d0e0f60da8d097d0888ad40e05ad482e2e --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_business.xml @@ -0,0 +1,10 @@ + + + diff --git a/PermissionController/res/drawable-v34/ic_collections_bookmark.xml b/PermissionController/res/drawable-v34/ic_collections_bookmark.xml new file mode 100644 index 0000000000000000000000000000000000000000..29f9e569c3a40f58e00f73c2fe0bc46bdcab3f99 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_collections_bookmark.xml @@ -0,0 +1,10 @@ + + + diff --git a/PermissionController/res/drawable-v34/ic_gear.xml b/PermissionController/res/drawable-v34/ic_gear.xml new file mode 100644 index 0000000000000000000000000000000000000000..958284d4c4c78634a7af26888e6ef0ed74b6ac18 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_gear.xml @@ -0,0 +1,13 @@ + + + + diff --git a/PermissionController/res/drawable-v34/ic_help.xml b/PermissionController/res/drawable-v34/ic_help.xml new file mode 100644 index 0000000000000000000000000000000000000000..5a8f419f6c5fa6f336f2ad9d4798c7406f83f794 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_help.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/ic_info_24dp.xml b/PermissionController/res/drawable-v34/ic_info_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..35f7f5f611e17384ff26a8f6f6d465a9f6e47181 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_info_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_center_brand_chip.xml b/PermissionController/res/drawable-v34/ic_safety_center_brand_chip.xml new file mode 100644 index 0000000000000000000000000000000000000000..82ee628ffcd53d1f730af2baaac3e8d16f237df3 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_center_brand_chip.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_center_shield.xml b/PermissionController/res/drawable-v34/ic_safety_center_shield.xml new file mode 100644 index 0000000000000000000000000000000000000000..b7e7d7e5f646c3b28fa7cf055a282a0e3784c676 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_center_shield.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_info.xml b/PermissionController/res/drawable-v34/ic_safety_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..59ffbcfe4222010ec3e51b545f3a665b46a59dbc --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_info.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_null_state.xml b/PermissionController/res/drawable-v34/ic_safety_null_state.xml new file mode 100644 index 0000000000000000000000000000000000000000..a9dd828aa30ababd27a88507f8d6f7a30bca0130 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_null_state.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_recommendation.xml b/PermissionController/res/drawable-v34/ic_safety_recommendation.xml new file mode 100644 index 0000000000000000000000000000000000000000..dedec6a87e29dbbd3c792421093e0835817b9d58 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_recommendation.xml @@ -0,0 +1,35 @@ + + + + + + + diff --git a/PermissionController/res/drawable-v34/ic_safety_warn.xml b/PermissionController/res/drawable-v34/ic_safety_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..7acacbc5ae0dd8b027a8dfa1b02ace371f776225 --- /dev/null +++ b/PermissionController/res/drawable-v34/ic_safety_warn.xml @@ -0,0 +1,35 @@ + + + + + + + diff --git a/PermissionController/res/drawable/safety_center_button_background_dark.xml b/PermissionController/res/drawable-v34/safety_center_brand_chip_background.xml similarity index 79% rename from PermissionController/res/drawable/safety_center_button_background_dark.xml rename to PermissionController/res/drawable-v34/safety_center_brand_chip_background.xml index 90884857c6dba9b37319f6f6420dcd40a557d957..2d216edd8fcd9b595055ca2f1a0d389cf02302ce 100644 --- a/PermissionController/res/drawable/safety_center_button_background_dark.xml +++ b/PermissionController/res/drawable-v34/safety_center_brand_chip_background.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/safety_status_info.xml b/PermissionController/res/drawable-v34/safety_status_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..c12c191448bc82314d74c1c54daef7aca7020b94 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_info.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_info_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_info_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..91d07a50da2af47d53c504743db3b8351dae0912 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_info_to_info_anim.xml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/safety_status_recommend_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_recommend_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..c618ff3082121952a06819a55bf9f48b70eb7cef --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_recommend_to_info_anim.xml @@ -0,0 +1,722 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/safety_status_recommendation.xml b/PermissionController/res/drawable-v34/safety_status_recommendation.xml new file mode 100644 index 0000000000000000000000000000000000000000..4dcab9f9d958c8a458ee799ee3fdf45137d5a8bb --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_recommendation.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_info_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_info_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..264a3fc92e69a849c9f32072e08fd6d58f607e94 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_info_to_info_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_info_to_recommendation_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_info_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec9ad1b2c65d4c596c199181588626d3e7c0c0b0 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_info_to_recommendation_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_info_to_warn_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_info_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..21a215b7ba6ee65035df75e3f6690bdb4f10c02e --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_info_to_warn_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..696a6699908f2a546919c85679403d0aa6a360d1 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_info_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_recommendation_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..9e69006fbdc9d7e51b71910ca20285d9d076ea95 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_recommendation_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_warn_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..4264bf81a989b22d32987dbb21be470d40c1e78b --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_recommendation_to_warn_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_warn_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_warn_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..79779a6a20b4b494782ebb058b4046b88aa30112 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_warn_to_info_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_warn_to_recommendation_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_warn_to_recommendation_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..c6ae91634fdc6d00a19dda7d03810136a27bc20a --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_warn_to_recommendation_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_small_warn_to_warn_anim.xml b/PermissionController/res/drawable-v34/safety_status_small_warn_to_warn_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e2a67a4ee27aad487f854ee87444f89954e42ed --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_small_warn_to_warn_anim.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_warn.xml b/PermissionController/res/drawable-v34/safety_status_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..6b3304992a5a8fc7676c888d49d0dc4bb5118a06 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_warn.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/PermissionController/res/drawable-v34/safety_status_warn_to_info_anim.xml b/PermissionController/res/drawable-v34/safety_status_warn_to_info_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..eba9107fe0e7baab08469f939e6d15ca32921633 --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_warn_to_info_anim.xml @@ -0,0 +1,722 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/safety_status_warn_to_recommend_anim.xml b/PermissionController/res/drawable-v34/safety_status_warn_to_recommend_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..8b97e4871d4ee2e975f7222aa22e666bce954d4a --- /dev/null +++ b/PermissionController/res/drawable-v34/safety_status_warn_to_recommend_anim.xml @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_info_to_scanning_anim.xml b/PermissionController/res/drawable-v34/status_info_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..7a1737b62b55630cadd6f673835eb381f9030092 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_info_to_scanning_anim.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_recommend_to_scanning_anim.xml b/PermissionController/res/drawable-v34/status_recommend_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..1b71689a90301077463f1f1358d7dc1b8452912e --- /dev/null +++ b/PermissionController/res/drawable-v34/status_recommend_to_scanning_anim.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_anim_info.xml b/PermissionController/res/drawable-v34/status_scanning_anim_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..e0e9527550fb321dbd57501ac5a656ed59c35e80 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_anim_info.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_anim_recommend.xml b/PermissionController/res/drawable-v34/status_scanning_anim_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..50ec7edf23f49c17dcd00cb9c23b55058d9ea779 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_anim_recommend.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_anim_warn.xml b/PermissionController/res/drawable-v34/status_scanning_anim_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6c319ef054f2c74a0a9ae00f826d7ceab2b4fd1 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_anim_warn.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_info.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..9336ddfa81fa7bc4641252c84260db845fc416a0 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_info.xml @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_recommend.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a55c83eceb73701826c4fb1bd260f49d32bbe9a --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_recommend.xml @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_warn.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c42eed500174993b18aae6d12d41269b718b3c2 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_info_to_warn.xml @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_info.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..859c519958c9a56fa09cf7acf01edc5afec4a48e --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_info.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_recommend.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..f6a8583c144b43682b15578e6f8f44fba3db080b --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_recommend.xml @@ -0,0 +1,458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_warn.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..59ef6b3c958c25a1b73e614f05ed0875a52cf0c0 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_recommend_to_warn.xml @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_info.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..07a38ea51fdac48722d8886c33fbc5220ab0978b --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_info.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_recommend.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_recommend.xml new file mode 100644 index 0000000000000000000000000000000000000000..3f06af41e6cc7a9aac7bb15d05eefc5bb496323b --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_recommend.xml @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_warn.xml b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_warn.xml new file mode 100644 index 0000000000000000000000000000000000000000..452e7f0510e1132c280691fb538364c51536c493 --- /dev/null +++ b/PermissionController/res/drawable-v34/status_scanning_end_anim_warn_to_warn.xml @@ -0,0 +1,441 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable-v34/status_warn_to_scanning_anim.xml b/PermissionController/res/drawable-v34/status_warn_to_scanning_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..e456186324a7ec6eab2a3a23664f141af5a759af --- /dev/null +++ b/PermissionController/res/drawable-v34/status_warn_to_scanning_anim.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable/coarse_off_dark.gif b/PermissionController/res/drawable/coarse_off_dark.gif deleted file mode 100644 index 09a7da18d9f4a49fcbd280e720addcbdb148644b..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/coarse_off_dark.gif and /dev/null differ diff --git a/PermissionController/res/drawable/coarse_off_light.gif b/PermissionController/res/drawable/coarse_off_light.gif deleted file mode 100644 index a5419cd917662d8b2be413a95ad95fd076c4718f..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/coarse_off_light.gif and /dev/null differ diff --git a/PermissionController/res/drawable/coarse_on_dark.gif b/PermissionController/res/drawable/coarse_on_dark.gif deleted file mode 100644 index a2ea07bd0e513218d1ec48f8379d967269e35ebc..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/coarse_on_dark.gif and /dev/null differ diff --git a/PermissionController/res/drawable/coarse_on_light.gif b/PermissionController/res/drawable/coarse_on_light.gif deleted file mode 100644 index 491edb612ce4ded816bc40dd134e5a1751650e8e..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/coarse_on_light.gif and /dev/null differ diff --git a/PermissionController/res/drawable/fine_off_dark.gif b/PermissionController/res/drawable/fine_off_dark.gif deleted file mode 100644 index 560e38a3437031b7721f1c93997ec940bb041419..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/fine_off_dark.gif and /dev/null differ diff --git a/PermissionController/res/drawable/fine_off_light.gif b/PermissionController/res/drawable/fine_off_light.gif deleted file mode 100644 index 5661b9270f519d9de5a02b89a53934cf20dbb0b0..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/fine_off_light.gif and /dev/null differ diff --git a/PermissionController/res/drawable/fine_on_dark.gif b/PermissionController/res/drawable/fine_on_dark.gif deleted file mode 100644 index aadf7821b76f9d776a16fdde0b231812f837a211..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/fine_on_dark.gif and /dev/null differ diff --git a/PermissionController/res/drawable/fine_on_light.gif b/PermissionController/res/drawable/fine_on_light.gif deleted file mode 100644 index a592c63050218e3179c6ae931d3561245e244b6b..0000000000000000000000000000000000000000 Binary files a/PermissionController/res/drawable/fine_on_light.gif and /dev/null differ diff --git a/PermissionController/res/drawable-v31/ic_safety_warn_outline.xml b/PermissionController/res/drawable/forward_arrow.xml similarity index 60% rename from PermissionController/res/drawable-v31/ic_safety_warn_outline.xml rename to PermissionController/res/drawable/forward_arrow.xml index a97eba0733e087a301380d148bcd100f9501f34e..f1564f07508ee0dd080ea15b44c9d930f71229b1 100644 --- a/PermissionController/res/drawable-v31/ic_safety_warn_outline.xml +++ b/PermissionController/res/drawable/forward_arrow.xml @@ -1,5 +1,5 @@ - - + android:width="12dp" + android:height="20dp" + android:viewportWidth="12.0" + android:viewportHeight="20.0" + android:tint="?android:attr/textColorPrimaryInverse"> + diff --git a/PermissionController/res/drawable-v31/indicator_background_circle.xml b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml similarity index 71% rename from PermissionController/res/drawable-v31/indicator_background_circle.xml rename to PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml index 357d11ab1a3d8b9406b51543f43463d64f2b52f4..6393742aa87f7963cc65e3bc6ec98676e6d94362 100644 --- a/PermissionController/res/drawable-v31/indicator_background_circle.xml +++ b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml @@ -1,3 +1,4 @@ + - - + android:shape="rectangle"> + + \ No newline at end of file diff --git a/PermissionController/res/drawable/ic_more_info_arrow.xml b/PermissionController/res/drawable/ic_more_info_arrow.xml new file mode 100644 index 0000000000000000000000000000000000000000..73eb5ccfc04a72b04439518b9dc9b0b694af24b6 --- /dev/null +++ b/PermissionController/res/drawable/ic_more_info_arrow.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/PermissionController/res/drawable/ic_settings_notification.xml b/PermissionController/res/drawable/ic_settings_notification.xml new file mode 100644 index 0000000000000000000000000000000000000000..3256dd2ddc549e30f07396690769ac8111a24718 --- /dev/null +++ b/PermissionController/res/drawable/ic_settings_notification.xml @@ -0,0 +1,92 @@ + + + + + + + + diff --git a/PermissionController/res/drawable/ic_shield_exclamation_outline.xml b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml new file mode 100644 index 0000000000000000000000000000000000000000..5785babf9d09282c61229cd7103ea170e2d5b363 --- /dev/null +++ b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/PermissionController/res/drawable/settings_gear.xml b/PermissionController/res/drawable/settings_gear.xml new file mode 100644 index 0000000000000000000000000000000000000000..86b418debc682ab807f97c89a5b800122e1aad7c --- /dev/null +++ b/PermissionController/res/drawable/settings_gear.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/PermissionController/res/layout-television/grant_permissions.xml b/PermissionController/res/layout-television/grant_permissions.xml index c3ecb7c954ef80854c38087dadcd3cddccaeb629..a7712e00e1b6af98ba6d81e858a9b8938bc630df 100644 --- a/PermissionController/res/layout-television/grant_permissions.xml +++ b/PermissionController/res/layout-television/grant_permissions.xml @@ -37,7 +37,7 @@ + android:layout_marginStart="@dimen/grant_permissions_actions_margin_left" + android:layout_marginEnd="@dimen/grant_permissions_actions_margin_right">