diff --git a/Android.bp b/Android.bp index df6fdaa5fdf6c..691a9cf35ddb0 100644 --- a/Android.bp +++ b/Android.bp @@ -242,6 +242,9 @@ java_library { "com.android.sysprop.init", "com.android.sysprop.localization", "PlatformProperties", + "vendor.lineage.livedisplay-V2.0-java", + "vendor.lineage.livedisplay-V2.1-java", + "vendor.lineage.touch-V1.0-java", ], sdk_version: "core_platform", installable: false, @@ -365,6 +368,15 @@ java_library { name: "framework-minus-apex-intdefs", defaults: ["framework-minus-apex-defaults"], plugins: ["intdef-annotation-processor"], + + // Errorprone and android lint will already run on framework-minus-apex, don't rerun them on + // the intdefs version in order to speed up the build. + errorprone: { + enabled: false, + }, + lint: { + enabled: false, + }, } // This "framework" module is NOT installed to the device. It's diff --git a/CleanSpec.mk b/CleanSpec.mk index 02e8eecbb721b..e8ab07d646037 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -78,6 +78,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/pocket/*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/ITelephonyRegistry.P) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/docs/api-stubs*) diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java index 3781b6dcf1625..03e5468a342a8 100644 --- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java @@ -257,7 +257,7 @@ private void deleteBlob(BlobHandle blobHandle) throws Exception { runShellCommand(String.format( "cmd blob_store delete-blob --algo %s --digest %s --label %s --expiry %d --tag %s", blobHandle.algorithm, - Base64.getEncoder().encode(blobHandle.digest), + Base64.getEncoder().encodeToString(blobHandle.digest), blobHandle.label, blobHandle.expiryTimeMillis, blobHandle.tag)); diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 7393bcde13b62..2619f42805afa 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -936,6 +936,13 @@ private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMilli long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener, String listenerTag, Executor targetExecutor, WorkSource workSource, AlarmClockInfo alarmClock) { + + if (mPackageName.equals("com.google.android.gms")) { + if (windowMillis == WINDOW_EXACT && !canScheduleExactAlarms()) { + windowMillis = WINDOW_HEURISTIC; + } + } + if (triggerAtMillis < 0) { /* NOTYET if (mAlwaysExact) { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d6b246a9e2e39..553670afca90c 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -5727,7 +5727,7 @@ private void decrementAlarmCount(int uid, int decrement) { } } if (oldCount < decrement) { - Slog.wtf(TAG, "Attempt to decrement existing alarm count " + oldCount + " by " + Slog.w(TAG, "Attempt to decrement existing alarm count " + oldCount + " by " + decrement + " for uid " + uid); } } diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 12083b6fe20bb..4ca8b1e184317 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -85,8 +85,10 @@ class AppRuntime : public AndroidRuntime AndroidRuntime* ar = AndroidRuntime::getRuntime(); ar->callMain(mClassName, mClass, mArgs); - IPCThreadState::self()->stopProcess(); - hardware::IPCThreadState::self()->stopProcess(); + if (mClassName != "com.android.internal.os.ExecInit") { + IPCThreadState::self()->stopProcess(); + hardware::IPCThreadState::self()->stopProcess(); + } } virtual void onZygoteInit() diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java index 3b14be7327f75..24727c5f24482 100644 --- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java +++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java @@ -107,7 +107,7 @@ else if (arg.equals("--windows")) { DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java index ab198b319e273..488292d68620e 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java @@ -139,7 +139,7 @@ public static void dumpWindowsToFile(SparseArray> serializer.attribute("", "id", Integer.toString(displayId)); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); for (int i = 0, n = windows.size(); i < n; ++i) { dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java index b1b432bf79abf..a31deb0845751 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java @@ -767,7 +767,7 @@ public void dumpWindowHierarchy(String fileName) { if(root != null) { Display display = getAutomatorBridge().getDefaultDisplay(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(root, new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName), display.getRotation(), size.x, size.y); diff --git a/core/api/current.txt b/core/api/current.txt index c8a43db2f9c20..f1b161afe354f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20118,6 +20118,7 @@ package android.media { method public void setRingerMode(int); method @Deprecated public void setRouting(int, int, int); method public void setSpeakerphoneOn(boolean); + method public void setStreamMaxVolume(int, int); method @Deprecated public void setStreamMute(int, boolean); method @Deprecated public void setStreamSolo(int, boolean); method public void setStreamVolume(int, int, int); @@ -31716,6 +31717,7 @@ package android.os { method public void writeTypedArray(@Nullable T[], int); method public void writeTypedArrayMap(@Nullable android.util.ArrayMap, int); method public void writeTypedList(@Nullable java.util.List); + method public void writeTypedList(@Nullable java.util.List, int); method public void writeTypedObject(@Nullable T, int); method public void writeTypedSparseArray(@Nullable android.util.SparseArray, int); method public void writeValue(@Nullable Object); diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index e6cdcc0ee7420..0d6a07938e959 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -31,7 +31,6 @@ import com.android.internal.annotations.GuardedBy; -import java.util.Objects; import java.util.Set; /** @@ -87,12 +86,6 @@ public Account(String name, String type, String accessId) { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } - if (name.length() > 200) { - throw new IllegalArgumentException("account name is longer than 200 characters"); - } - if (type.length() > 200) { - throw new IllegalArgumentException("account type is longer than 200 characters"); - } this.name = name; this.type = type; this.accessId = accessId; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ae8809d8ac294..04d673813e956 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -98,6 +98,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; +import android.graphics.Rect; import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.media.MediaFrameworkInitializer; @@ -248,6 +249,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import android.content.res.ResourcesImpl; +import android.view.DisplayInfo; /** * This manages the execution of the main thread in an * application process, scheduling and executing activities, @@ -526,6 +529,8 @@ public int hashCode() { /** A client side controller to handle process level configuration changes. */ private ConfigurationController mConfigurationController; + private float mDssScale = 1.0f; + /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */ public static final class ActivityClientRecord { @UnsupportedAppUsage @@ -897,6 +902,8 @@ static final class AppBindData { AutofillOptions autofillOptions; + float dssScale; + /** * Content capture options for the application - when null, it means ContentCapture is not * enabled for the package. @@ -1126,6 +1133,7 @@ public final void bindApplication(String processName, ApplicationInfo appInfo, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, + float dssScale, String buildSerial, AutofillOptions autofillOptions, ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges, SharedMemory serializedSystemFontMap, @@ -1178,6 +1186,7 @@ public final void bindApplication(String processName, ApplicationInfo appInfo, data.initProfilerInfo = profilerInfo; data.buildSerial = buildSerial; data.autofillOptions = autofillOptions; + data.dssScale = dssScale; data.contentCaptureOptions = contentCaptureOptions; data.disabledCompatChanges = disabledCompatChanges; data.mSerializedSystemFontMap = serializedSystemFontMap; @@ -3835,7 +3844,7 @@ private void reportSizeConfigurations(ActivityClientRecord r) { return; } Configuration[] configurations = r.activity.getResources().getSizeConfigurations(); - if (configurations == null) { + if (configurations == null || r.activity.mFinished) { return; } r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); @@ -4535,7 +4544,7 @@ private void handleBindService(BindServiceData data) { Service s = mServices.get(data.token); if (DEBUG_SERVICE) Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); - if (s != null) { + if (s != null && createData != null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), @@ -4566,7 +4575,7 @@ private void handleBindService(BindServiceData data) { private void handleUnbindService(BindServiceData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); - if (s != null) { + if (s != null && createData != null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), @@ -4673,7 +4682,7 @@ private void handleDumpProvider(DumpComponentInfo info) { private void handleServiceArgs(ServiceArgsData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); - if (s != null) { + if (s != null && createData != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); @@ -4920,13 +4929,14 @@ public void handleResumeActivity(ActivityClientRecord r, boolean finalStateReque l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; - if (r.activity.mVisibleFromClient) { - ViewManager wm = a.getWindowManager(); - View decor = r.window.getDecorView(); - wm.updateViewLayout(decor, l); - } } + if (r.activity.mVisibleFromClient) { + ViewManager wm = a.getWindowManager(); + View decor = r.window.getDecorView(); + wm.updateViewLayout(decor, l); + } + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { @@ -6488,6 +6498,8 @@ private void handleBindApplication(AppBindData data) { Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), data.startRequestedElapsedTime, data.startRequestedUptime); + mDssScale = data.dssScale; + AppCompatCallbacks.install(data.disabledCompatChanges); // Let libcore handle any compat changes after installing the list of compat changes. AppSpecializationHooks.handleCompatChangesBeforeBindingApplication(); @@ -7062,11 +7074,6 @@ public final IContentProvider acquireProvider( } } if (holder == null) { - if (UserManager.get(c).isUserUnlocked(userId)) { - Slog.e(TAG, "Failed to find provider info for " + auth); - } else { - Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); - } return null; } @@ -7988,4 +7995,27 @@ public boolean isInDensityCompatMode() { // ------------------ Regular JNI ------------------------ private native void nPurgePendingResources(); private native void nInitZygoteChildHeapProfiling(); + + /** + * Gets scale value on appplication layer + */ + public float getDssScale() { + return mDssScale; + } + + @UnsupportedAppUsage + public static boolean applyDssScaleIfNeeded(DisplayInfo info, Rect bounds) { + final ActivityThread thread = sCurrentActivityThread; + if (thread != null) { + final float dssScale = thread.mDssScale; + if (dssScale != 1.0f) { + final int width = (int) (bounds.width() * dssScale + .5f); + final int height = (int) (bounds.height() * dssScale + .5f); + info.logicalWidth = info.appWidth = width; + info.logicalHeight = info.appHeight = height; + return true; + } + } + return false; + } } diff --git a/core/java/android/app/AppLockData.aidl b/core/java/android/app/AppLockData.aidl new file mode 100644 index 0000000000000..073d1efd25055 --- /dev/null +++ b/core/java/android/app/AppLockData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 FlamingoOS 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 android.app; + +parcelable AppLockData; \ No newline at end of file diff --git a/core/java/android/app/AppLockData.java b/core/java/android/app/AppLockData.java new file mode 100644 index 0000000000000..19a72c4034fd2 --- /dev/null +++ b/core/java/android/app/AppLockData.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 FlamingoOS 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 android.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Class to hold package level information about an + * application protected with app lock. + * + * @hide + */ +public final class AppLockData implements Parcelable { + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public AppLockData createFromParcel(Parcel in) { + return new AppLockData(in); + } + + @Override + public AppLockData[] newArray(int size) { + return new AppLockData[size]; + } + }; + + private final String mPackageName; + private final boolean mShouldRedactNotification; + + /** @hide */ + public AppLockData( + @NonNull final String packageName, + final boolean shouldRedactNotification + ) { + mPackageName = packageName; + mShouldRedactNotification = shouldRedactNotification; + } + + private AppLockData(final Parcel in) { + mPackageName = in.readString(); + mShouldRedactNotification = in.readBoolean(); + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + public boolean getShouldRedactNotification() { + return mShouldRedactNotification; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel parcel, final int flags) { + parcel.writeString(mPackageName); + parcel.writeBoolean(mShouldRedactNotification); + } + + @Override + @NonNull + public String toString() { + return "AppLockData[ packageName = " + mPackageName + + ", shouldRedactNotification = " + mShouldRedactNotification + " ]"; + } +} \ No newline at end of file diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java new file mode 100644 index 0000000000000..cfb22d57f9db1 --- /dev/null +++ b/core/java/android/app/AppLockManager.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2022 FlamingoOS 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 android.app; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.RequiresPermission; +import android.annotation.UserHandleAware; +import android.content.Context; +import android.os.RemoteException; + +import java.util.List; + +/** + * @hide + */ +@SystemApi +@SystemService(Context.APP_LOCK_SERVICE) +public final class AppLockManager { + + /** @hide */ + public static final long DEFAULT_TIMEOUT = 10 * 1000; + + /** @hide */ + public static final boolean DEFAULT_BIOMETRICS_ALLOWED = true; + + /** + * Intent action for starting credential activity in SystemUI. + * @hide + */ + public static final String ACTION_UNLOCK_APP = "android.app.action.UNLOCK_APP"; + + /** + * Intent extra to indicate whether usage of biometrics is allowed. + * @hide + */ + public static final String EXTRA_ALLOW_BIOMETRICS = "android.app.AppLockManager.ALLOW_BIOMETRICS"; + + /** + * Intent extra for the name of the application to unlock. + * @hide + */ + public static final String EXTRA_PACKAGE_LABEL = "android.app.AppLockManager.PACKAGE_LABEL"; + + private final Context mContext; + private final IAppLockManagerService mService; + + /** @hide */ + AppLockManager(Context context, IAppLockManagerService service) { + mContext = context; + mService = service; + } + + /** + * Add an application to be protected. Package should be an user + * installed application or a system app whitelisted in + * {@link config_appLockAllowedSystemApps}. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name of the app to add. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void addPackage(@NonNull String packageName) { + try { + mService.addPackage(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove an application from the protected packages list. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name of the app to remove. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void removePackage(@NonNull String packageName) { + try { + mService.removePackage(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the current auto lock timeout. + * + * @param userId the user id given by the caller. + * @return the timeout in milliseconds if configuration for + * current user exists, -1 otherwise. + */ + @UserHandleAware + public long getTimeout() { + try { + return mService.getTimeout(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set auto lock timeout. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param timeout the timeout in milliseconds. Must be >= 5. + * @param userId the user id given by the caller. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setTimeout(long timeout) { + try { + mService.setTimeout(timeout, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get all the packages protected with app lock. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @return a unique list of {@link AppLockData} of the protected apps. + * @hide + */ + @UserHandleAware + @NonNull + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public List getPackageData() { + try { + return mService.getPackageData(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether notification content should be redacted for a package + * in locked state. Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name. + * @param secure true to hide notification content. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setShouldRedactNotification(@NonNull String packageName, boolean secure) { + try { + mService.setShouldRedactNotification(packageName, secure, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether to allow unlocking with biometrics. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param biometricsAllowed whether to use biometrics. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setBiometricsAllowed(boolean biometricsAllowed) { + try { + mService.setBiometricsAllowed(biometricsAllowed, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check whether biometrics is allowed for unlocking. + * + * @return true if biometrics will be used for unlocking, false otheriwse. + */ + @UserHandleAware + public boolean isBiometricsAllowed() { + try { + return mService.isBiometricsAllowed(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unlock a package following authentication with credentials. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the name of the package to unlock. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void unlockPackage(@NonNull String packageName) { + try { + mService.unlockPackage(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} \ No newline at end of file diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index d641a3b469a68..4b82c671896fc 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -806,8 +806,41 @@ public Boolean recompute(HasSystemFeatureQuery query) { } }; + private static final String[] featuresPixel = { + "com.google.android.apps.photos.PIXEL_2019_PRELOAD", + "com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD", + "com.google.android.apps.photos.PIXEL_2018_PRELOAD", + "com.google.android.apps.photos.PIXEL_2017_PRELOAD", + "com.google.android.feature.PIXEL_2022_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2020_EXPERIENCE", + "com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2019_EXPERIENCE", + "com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2018_EXPERIENCE", + "com.google.android.feature.PIXEL_2017_EXPERIENCE", + "com.google.android.feature.PIXEL_EXPERIENCE", + "com.google.android.feature.GOOGLE_BUILD", + "com.google.android.feature.GOOGLE_EXPERIENCE" + }; + + private static final String[] featuresNexus = { + "com.google.android.apps.photos.NEXUS_PRELOAD", + "com.google.android.apps.photos.nexus_preload" + }; + @Override public boolean hasSystemFeature(String name, int version) { + String packageName = ActivityThread.currentPackageName(); + if (packageName != null && + packageName.equals("com.google.android.apps.photos") && + SystemProperties.getBoolean("persist.sys.pixelprops.gphotos", true)) { + if (Arrays.asList(featuresPixel).contains(name)) return false; + if (Arrays.asList(featuresNexus).contains(name)) return true; + } + if (Arrays.asList(featuresPixel).contains(name)) return true; return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); } diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index c0aebeed596a6..7bfb1b5c1ba69 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -47,6 +47,13 @@ public final class AutomaticZenRule implements Parcelable { private boolean mModified = false; private String mPkg; + /** + * The maximum string length for any string contained in this automatic zen rule. This pertains + * both to fields in the rule itself (such as its name) and items with sub-fields. + * @hide + */ + public static final int MAX_STRING_LENGTH = 1000; + /** * Creates an automatic zen rule. * @@ -93,10 +100,10 @@ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner, @Nullable ComponentName configurationActivity, @NonNull Uri conditionId, @Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) { - this.name = name; - this.owner = owner; - this.configurationActivity = configurationActivity; - this.conditionId = conditionId; + this.name = getTrimmedString(name); + this.owner = getTrimmedComponentName(owner); + this.configurationActivity = getTrimmedComponentName(configurationActivity); + this.conditionId = getTrimmedUri(conditionId); this.interruptionFilter = interruptionFilter; this.enabled = enabled; this.mZenPolicy = policy; @@ -115,12 +122,14 @@ public AutomaticZenRule(String name, ComponentName owner, ComponentName configur public AutomaticZenRule(Parcel source) { enabled = source.readInt() == ENABLED; if (source.readInt() == ENABLED) { - name = source.readString(); + name = getTrimmedString(source.readString()); } interruptionFilter = source.readInt(); - conditionId = source.readParcelable(null, android.net.Uri.class); - owner = source.readParcelable(null, android.content.ComponentName.class); - configurationActivity = source.readParcelable(null, android.content.ComponentName.class); + conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class)); + owner = getTrimmedComponentName( + source.readParcelable(null, android.content.ComponentName.class)); + configurationActivity = getTrimmedComponentName( + source.readParcelable(null, android.content.ComponentName.class)); creationTime = source.readLong(); mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); mModified = source.readInt() == ENABLED; @@ -196,7 +205,7 @@ public long getCreationTime() { * Sets the representation of the state that causes this rule to become active. */ public void setConditionId(Uri conditionId) { - this.conditionId = conditionId; + this.conditionId = getTrimmedUri(conditionId); } /** @@ -211,7 +220,7 @@ public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) { * Sets the name of this rule. */ public void setName(String name) { - this.name = name; + this.name = getTrimmedString(name); } /** @@ -243,7 +252,7 @@ public void setZenPolicy(ZenPolicy zenPolicy) { * that are not backed by {@link android.service.notification.ConditionProviderService}. */ public void setConfigurationActivity(@Nullable ComponentName componentName) { - this.configurationActivity = componentName; + this.configurationActivity = getTrimmedComponentName(componentName); } /** @@ -333,4 +342,35 @@ public AutomaticZenRule[] newArray(int size) { return new AutomaticZenRule[size]; } }; + + /** + * If the package or class name of the provided ComponentName are longer than MAX_STRING_LENGTH, + * return a trimmed version that truncates each of the package and class name at the max length. + */ + private static ComponentName getTrimmedComponentName(ComponentName cn) { + if (cn == null) return null; + return new ComponentName(getTrimmedString(cn.getPackageName()), + getTrimmedString(cn.getClassName())); + } + + /** + * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH. + */ + private static String getTrimmedString(String input) { + if (input != null && input.length() > MAX_STRING_LENGTH) { + return input.substring(0, MAX_STRING_LENGTH); + } + return input; + } + + /** + * Returns a truncated copy of the Uri by trimming the string representation to the maximum + * string length. + */ + private static Uri getTrimmedUri(Uri input) { + if (input != null && input.toString().length() > MAX_STRING_LENGTH) { + return Uri.parse(getTrimmedString(input.toString())); + } + return input; + } } diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 1a77b65c8ef6b..d8067850e940e 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -28,6 +28,9 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.HardwareRenderer; +import android.graphics.Typeface; +import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.LocaleList; import android.os.Trace; import android.util.DisplayMetrics; @@ -40,6 +43,9 @@ import java.util.ArrayList; import java.util.Locale; +import android.app.ActivityThread; +import android.content.res.ResourcesImpl; + /** * A client side controller to handle process level configuration changes. * @hide @@ -185,6 +191,7 @@ void handleConfigurationChanged(@Nullable Configuration config, final Application app = mActivityThread.getApplication(); final Resources appResources = app.getResources(); + Typeface.updateDefaultFont(appResources); mResourcesManager.applyConfigurationToResources(config, compat); updateLocaleListFromAppContext(app.getApplicationContext()); @@ -256,6 +263,18 @@ void updateDefaultDensity(int densityDpi) { DisplayMetrics.DENSITY_DEVICE = densityDpi; Bitmap.setDefaultDensity(densityDpi); } + + // Change DisplayMetrics.DENSITY_DEVICE and default density for bitmaps, but at the same + // time we tell Resources class to load all resources for the original density in order to + // make all loaded resources independent of current dss value. + final float mDssScale = ActivityThread.currentActivityThread().getDssScale(); + if (mDssScale != 1) { + DisplayMetrics.DENSITY_DEVICE = densityDpi; + Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEVICE); + ResourcesImpl.setLoadDensityDpi((int)(densityDpi / mDssScale + .5f)); + } else { + ResourcesImpl.setLoadDensityDpi(Configuration.DENSITY_DPI_UNDEFINED); + } } /** Get current default display dpi. This is only done to maintain @UnsupportedAppUsage. */ diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 3550923782795..c209660f4197c 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -301,6 +301,13 @@ public class DownloadManager { */ public final static int PAUSED_UNKNOWN = 4; + /** + * Value of {@link #COLUMN_REASON} when the download is paused manually. + * + * @hide + */ + public final static int PAUSED_MANUAL = 5; + /** * Broadcast intent action sent by the download manager when a download completes. */ @@ -994,6 +1001,7 @@ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) { parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY)); parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK)); parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI)); + parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_MANUAL)); } if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) { parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS)); @@ -1283,6 +1291,34 @@ public void forceDownload(long... ids) { mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } + /** + * Pause the given running download manually. + * + * @param id the ID of the download to be paused + * @return the number of downloads actually updated + * @hide + */ + public int pauseDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_MANUAL); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** + * Resume the given paused download manually. + * + * @param id the ID of the download to be resumed + * @return the number of downloads actually updated + * @hide + */ + public int resumeDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit @@ -1773,6 +1809,9 @@ private long getPausedReason(int status) { case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: return PAUSED_QUEUED_FOR_WIFI; + case Downloads.Impl.STATUS_PAUSED_MANUAL: + return PAUSED_MANUAL; + default: return PAUSED_UNKNOWN; } @@ -1828,6 +1867,7 @@ private int translateStatus(int status) { case Downloads.Impl.STATUS_WAITING_TO_RETRY: case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: + case Downloads.Impl.STATUS_PAUSED_MANUAL: return STATUS_PAUSED; case Downloads.Impl.STATUS_SUCCESS: diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 8367441b1b95b..05e7b6c47ae0a 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -760,4 +760,55 @@ interface IActivityManager { *

*/ int getBackgroundRestrictionExemptionReason(int uid); + + /** + * Should disable touch if three fingers to screen shot is active? + */ + boolean isSwipeToScreenshotGestureActive(); + + /** + * Force full screen for devices with cutout + */ + boolean shouldForceCutoutFullscreen(in String packageName); + + /** + * Sets scale value for package in DSS list + * + * @throws RemoteException + */ + void setDssForPackage(in String packageName, float scale); + + /** + * Shows all information of package applied DSS + * + * @throws RemoteException + */ + void showAllDSSInfo(); + + /** + * Adds package into Dss list + * + * @throws RemoteException + */ + void addPackageData(in String packageName, float scalingFactor); + + /** + * Checks the scale app + * + * @throws RemoteException + */ + boolean isScaledApp(int pid); + + /** + * Checks the scale app by package + * + * @throws RemoteException + */ + boolean isScaledAppByPackageName(in String packageName); + + /** + * Gets scale value of package + * @throws RemoteException + */ + float getScalingFactor(in String packageName); } diff --git a/core/java/android/app/IAppLockManagerService.aidl b/core/java/android/app/IAppLockManagerService.aidl new file mode 100644 index 0000000000000..f1b25a2066867 --- /dev/null +++ b/core/java/android/app/IAppLockManagerService.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 FlamingoOS 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 android.app; + +import android.app.AppLockData; + +/** + * Interface for managing app lock. + * @hide + */ +interface IAppLockManagerService { + + void addPackage(in String packageName, in int userId); + + void removePackage(in String packageName, in int userId); + + long getTimeout(in int userId); + + void setTimeout(in long timeout, in int userId); + + List getPackageData(in int userId); + + void setShouldRedactNotification(in String packageName, in boolean secure, in int userId); + + void setBiometricsAllowed(in boolean biometricsAllowed, in int userId); + + boolean isBiometricsAllowed(in int userId); + + void unlockPackage(in String packageName, in int userId); +} \ No newline at end of file diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 75ad363a2668d..5e9afae7ac31e 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -79,7 +79,9 @@ oneway interface IApplicationThread { int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, - in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, + in Bundle coreSettings, + float dssScale, + in String buildSerial, in AutofillOptions autofillOptions, in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges, in SharedMemory serializedSystemFontMap, long startRequestedElapsedTime, long startRequestedUptime); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index da6a551175e32..a24d61eb1f4e8 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -236,4 +236,7 @@ interface INotificationManager void migrateNotificationFilter(in INotificationListener token, int defaultTypes, in List disallowedPkgs); void setToastRateLimitingEnabled(boolean enable); + + void forceShowLedLight(int color); + void forcePulseLedLight(int color, int onTime, int offTime); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 8984c4292023b..0dc9870b05a21 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -64,6 +64,8 @@ import java.util.List; import java.util.concurrent.TimeoutException; +import com.android.internal.util.cherish.PixelPropsUtils; + /** * Base class for implementing application instrumentation code. When running * with instrumentation turned on, this class will be instantiated for you @@ -1231,6 +1233,8 @@ public Application newApplication(ClassLoader cl, String className, Context cont Application app = getFactory(context.getPackageName()) .instantiateApplication(cl, className); app.attach(context); + String packageName = context.getPackageName(); + PixelPropsUtils.setProps(packageName); return app; } @@ -1248,6 +1252,8 @@ static public Application newApplication(Class clazz, Context context) ClassNotFoundException { Application app = (Application)clazz.newInstance(); app.attach(context); + String packageName = context.getPackageName(); + PixelPropsUtils.setProps(packageName); return app; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e820733803943..e7c19da8d24a0 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5459,14 +5459,12 @@ private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) int pillColor = Colors.flattenAlpha(getColors(p).getProtectionColor(), bgColor); int textColor = Colors.flattenAlpha(getPrimaryTextColor(p), pillColor); contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor); - contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor); // Use different highlighted colors for conversations' unread count if (p.mHighlightExpander) { pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor); textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor); } contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor); - contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor); } private void bindHeaderChronometerAndTime(RemoteViews contentView, diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index c9cc1a179102a..5572a15522fa5 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -137,6 +137,8 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_IMPORTANCE = "importance"; private static final String ATT_LIGHTS = "lights"; private static final String ATT_LIGHT_COLOR = "light_color"; + private static final String ATT_ON_TIME = "light_on_time"; + private static final String ATT_OFF_TIME = "light_off_time"; private static final String ATT_VIBRATION = "vibration"; private static final String ATT_VIBRATION_ENABLED = "vibration_enabled"; private static final String ATT_SOUND = "sound"; @@ -221,6 +223,8 @@ public final class NotificationChannel implements Parcelable { public static final int ALLOW_BUBBLE_OFF = 0; private static final int DEFAULT_LIGHT_COLOR = 0; + private static final int DEFAULT_ON_TIME = 0; + private static final int DEFAULT_OFF_TIME = 0; private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = @@ -240,6 +244,8 @@ public final class NotificationChannel implements Parcelable { private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; private boolean mLights; private int mLightColor = DEFAULT_LIGHT_COLOR; + private int mLightOnTime = DEFAULT_ON_TIME; + private int mLightOffTime = DEFAULT_OFF_TIME; private long[] mVibration; // Bitwise representation of fields that have been changed by the user, preventing the app from // making changes to these fields. @@ -319,6 +325,8 @@ protected NotificationChannel(Parcel in) { } mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; mLightColor = in.readInt(); + mLightOnTime = in.readInt(); + mLightOffTime = in.readInt(); mBlockableSystem = in.readBoolean(); mAllowBubbles = in.readInt(); mOriginalImportance = in.readInt(); @@ -379,6 +387,8 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(0); } dest.writeInt(mLightColor); + dest.writeInt(mLightOnTime); + dest.writeInt(mLightOffTime); dest.writeBoolean(mBlockableSystem); dest.writeInt(mAllowBubbles); dest.writeInt(mOriginalImportance); @@ -551,6 +561,30 @@ public void setLightColor(int argb) { this.mLightColor = argb; } + /** + * Sets the notification light ON time for notifications posted to this channel, if lights are + * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature. + * + * Only modifiable before the channel is submitted to + * {@link NotificationManager#notify(String, int, Notification)}. + * @hide + */ + public void setLightOnTime(int time) { + this.mLightOnTime = time; + } + + /** + * Sets the notification light OFF time for notifications posted to this channel, if lights are + * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature. + * + * Only modifiable before the channel is submitted to + * {@link NotificationManager#notify(String, int, Notification)}. + * @hide + */ + public void setLightOffTime(int time) { + this.mLightOffTime = time; + } + /** * Sets whether notification posted to this channel should vibrate. The vibration pattern can * be set with {@link #setVibrationPattern(long[])}. @@ -733,6 +767,24 @@ public int getLightColor() { return mLightColor; } + /** + * Returns the notification light ON time for notifications posted to this channel. Irrelevant + * unless {@link #shouldShowLights()}. + * @hide + */ + public int getLightOnTime() { + return mLightOnTime; + } + + /** + * Returns the notification light OFF time for notifications posted to this channel. Irrelevant + * unless {@link #shouldShowLights()}. + * @hide + */ + public int getLightOffTime() { + return mLightOffTime; + } + /** * Returns whether notifications posted to this channel always vibrate. */ @@ -946,6 +998,8 @@ private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); + setLightOnTime(safeInt(parser, ATT_ON_TIME, DEFAULT_ON_TIME)); + setLightOffTime(safeInt(parser, ATT_OFF_TIME, DEFAULT_OFF_TIME)); setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); @@ -1052,6 +1106,12 @@ private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Conte if (getLightColor() != DEFAULT_LIGHT_COLOR) { out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor()); } + if (getLightOnTime() != DEFAULT_ON_TIME) { + out.attribute(null, ATT_ON_TIME, Integer.toString(getLightOnTime())); + } + if (getLightOffTime() != DEFAULT_OFF_TIME) { + out.attribute(null, ATT_OFF_TIME, Integer.toString(getLightOffTime())); + } if (shouldVibrate()) { out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate()); } @@ -1134,6 +1194,8 @@ public JSONObject toJson() throws JSONException { } record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); + record.put(ATT_ON_TIME, Integer.toString(getLightOnTime())); + record.put(ATT_OFF_TIME, Integer.toString(getLightOffTime())); record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); @@ -1227,6 +1289,8 @@ public boolean equals(@Nullable Object o) { && getLockscreenVisibility() == that.getLockscreenVisibility() && mLights == that.mLights && getLightColor() == that.getLightColor() + && getLightOnTime() == that.getLightOnTime() + && getLightOffTime()== that.getLightOffTime() && getUserLockedFields() == that.getUserLockedFields() && isFgServiceShown() == that.isFgServiceShown() && mVibrationEnabled == that.mVibrationEnabled @@ -1253,7 +1317,7 @@ && isDemoted() == that.isDemoted() @Override public int hashCode() { int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd, - getLockscreenVisibility(), getSound(), mLights, getLightColor(), + getLockscreenVisibility(), getSound(), mLights, getLightColor(),getLightOnTime(),getLightOffTime(), getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, @@ -1291,6 +1355,8 @@ private String getFieldsString() { + ", mSound=" + mSound + ", mLights=" + mLights + ", mLightColor=" + mLightColor + + ", mLightOnTime=" + mLightOnTime + + ", mLightOffTime=" + mLightOffTime + ", mVibration=" + Arrays.toString(mVibration) + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) + ", mFgServiceShown=" + mFgServiceShown diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 392f52a08fb5c..df7c1a1efe4f8 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -2632,4 +2632,24 @@ public static int zenModeFromInterruptionFilter(int interruptionFilter, int defV default: return defValue; } } + + /** @hide */ + public void forceShowLedLight(int color) { + final INotificationManager service = getService(); + try { + service.forceShowLedLight(color); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void forcePulseLedLight(int color, int onTime, int offTime) { + final INotificationManager service = getService(); + try { + service.forcePulseLedLight(color, onTime, offTime); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index ae0fc09e35a64..623cdc5ebe0b0 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -229,6 +229,8 @@ public class StatusBarManager { public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1; /** @hide */ public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2; + /** @hide */ + public static final int CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE = 3; /** * Session flag for {@link #registerSessionListener} indicating the listener diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 40192836e0a79..3579b6f3b0015 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -196,6 +196,8 @@ import android.permission.PermissionCheckerManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; +import android.pocket.IPocketService; +import android.pocket.PocketManager; import android.print.IPrintManager; import android.print.PrintManager; import android.safetycenter.SafetyCenterFrameworkInitializer; @@ -955,6 +957,15 @@ public TvInteractiveAppManager createService(ContextImpl ctx) return new TvInteractiveAppManager(service, ctx.getUserId()); }}); + registerService(Context.POCKET_SERVICE, PocketManager.class, + new CachedServiceFetcher() { + @Override + public PocketManager createService(ContextImpl ctx) { + IBinder binder = ServiceManager.getService(Context.POCKET_SERVICE); + IPocketService service = IPocketService.Stub.asInterface(binder); + return new PocketManager(ctx.getOuterContext(), service); + }}); + registerService(Context.TV_INPUT_SERVICE, TvInputManager.class, new CachedServiceFetcher() { @Override @@ -1011,7 +1022,7 @@ public PersistentDataBlockManager createService() throws ServiceNotFoundExceptio new StaticServiceFetcher() { @Override public OemLockManager createService() throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.OEM_LOCK_SERVICE); + IBinder b = ServiceManager.getService(Context.OEM_LOCK_SERVICE); IOemLockService oemLockService = IOemLockService.Stub.asInterface(b); if (oemLockService != null) { return new OemLockManager(oemLockService); @@ -1527,6 +1538,17 @@ public AmbientContextManager createService(ContextImpl ctx) return new AmbientContextManager(ctx.getOuterContext(), manager); }}); + registerService(Context.APP_LOCK_SERVICE, AppLockManager.class, + new CachedServiceFetcher() { + @Override + public AppLockManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder binder = ServiceManager.getServiceOrThrow( + Context.APP_LOCK_SERVICE); + return new AppLockManager(ctx, + IAppLockManagerService.Stub.asInterface(binder)); + }}); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 7014d69635381..a5a50d6957933 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -43,6 +43,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; @@ -1103,7 +1104,10 @@ public boolean[] supportsCommands(String[] commands) { } try { boolean[] res = mInteractor.supportsCommands(mContext.getOpPackageName(), commands); - if (DEBUG) Log.d(TAG, "supportsCommands: cmds=" + commands + " res=" + res); + if (DEBUG) { + Log.d(TAG, "supportsCommands: cmds=" + Arrays.toString(commands) + " res=" + + Arrays.toString(res)); + } return res; } catch (RemoteException e) { throw new RuntimeException("Voice interactor has died", e); diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 067a4c3c047ea..7504a91c94ba3 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -99,9 +99,9 @@ public final class WallpaperColors implements Parcelable { private static final float MIN_COLOR_OCCURRENCE = 0.05f; // Decides when dark theme is optimal for this wallpaper - private static final float DARK_THEME_MEAN_LUMINANCE = 0.3f; + private static final float DARK_THEME_MEAN_LUMINANCE = 30.0f; // Minimum mean luminosity that an image needs to have to support dark text - private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.7f; + private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 70.0f; // We also check if the image has dark pixels in it, // to avoid bright images with some dark spots. private static final float DARK_PIXEL_CONTRAST = 5.5f; @@ -253,9 +253,9 @@ public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryCol this(primaryColor, secondaryColor, tertiaryColor, 0); // Calculate dark theme support based on primary color. - final float[] tmpHsl = new float[3]; - ColorUtils.colorToHSL(primaryColor.toArgb(), tmpHsl); - final float luminance = tmpHsl[2]; + final double[] tmpLab = new double[3]; + ColorUtils.colorToLAB(primaryColor.toArgb(), tmpLab); + final double luminance = tmpLab[0]; if (luminance < DARK_THEME_MEAN_LUMINANCE) { mColorHints |= HINT_SUPPORTS_DARK_THEME; } @@ -549,10 +549,10 @@ private static int calculateDarkHints(Bitmap source, float dimAmount) { // This bitmap was already resized to fit the maximum allowed area. // Let's just loop through the pixels, no sweat! - float[] tmpHsl = new float[3]; + double[] tmpLab = new double[3]; for (int i = 0; i < pixels.length; i++) { int pixelColor = pixels[i]; - ColorUtils.colorToHSL(pixelColor, tmpHsl); + ColorUtils.colorToLAB(pixelColor, tmpLab); final int alpha = Color.alpha(pixelColor); // Apply composite colors where the foreground is a black layer with an alpha value of diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 34c91c360dbeb..64f3c3928cc38 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3553,7 +3553,7 @@ public void onInstallUpdateError( * Maximum supported password length. Kind-of arbitrary. * @hide */ - public static final int MAX_PASSWORD_LENGTH = 16; + public static final int MAX_PASSWORD_LENGTH = 64; /** * Service Action: Service implemented by a device owner or profile owner supervision app to diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java index 24b4f4b99ad0e..63c9839a87e3c 100644 --- a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java +++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java @@ -203,17 +203,14 @@ public boolean equals(Object o) { return mIsEnabled == that.mIsEnabled && mAllowFallbackToDefaultConnection == that.mAllowFallbackToDefaultConnection && mNetworkId == that.mNetworkId - && Objects.equals(mIncludedUids, that.mIncludedUids) - && Objects.equals(mExcludedUids, that.mExcludedUids); + && Arrays.equals(mIncludedUids, that.mIncludedUids) + && Arrays.equals(mExcludedUids, that.mExcludedUids); } @Override public int hashCode() { - return ((Objects.hashCode(mIsEnabled) * 17) - + (Objects.hashCode(mAllowFallbackToDefaultConnection) * 19) - + (Objects.hashCode(mIncludedUids) * 23) - + (Objects.hashCode(mExcludedUids) * 29) - + mNetworkId * 31); + return Objects.hash(mIsEnabled, mAllowFallbackToDefaultConnection, + Arrays.hashCode(mIncludedUids), Arrays.hashCode(mExcludedUids), mNetworkId); } /** diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index b88bf76c96ca7..fdf2b3f543116 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -132,7 +132,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime); out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState); - out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); + out.attribute(null, ATTR_ORIGINAL_BUILD , Build.VERSION.INCREMENTAL); out.endTag(null, tag); } @@ -141,7 +141,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) { // If an OTA has been applied (build fingerprint has changed), discard stale info. final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); - if (!Build.FINGERPRINT.equals(buildFingerprint)) { + if (!Build.VERSION.INCREMENTAL.equals(buildFingerprint)) { return null; } try { diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index b1b59b0e39b1d..f9db498015a0d 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -799,8 +799,11 @@ protected final void fullBackupFileTree(String packageName, String domain, Strin String domainPath = FullBackup.getBackupScheme(this, mOperationType) .tokenToDirectoryPath(domain); if (domainPath == null) { - // Should never happen. - return; + if (startingPath == null) { + return; + } else { + domainPath = startingPath; + } } File rootFile = new File(startingPath); diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java index e6091f04a72a6..2934cd27da198 100644 --- a/core/java/android/companion/BluetoothLeDeviceFilter.java +++ b/core/java/android/companion/BluetoothLeDeviceFilter.java @@ -204,9 +204,10 @@ public boolean equals(@Nullable Object o) { @Override public int hashCode() { - return Objects.hash(mNamePattern, mScanFilter, mRawDataFilter, mRawDataFilterMask, - mRenamePrefix, mRenameSuffix, mRenameBytesFrom, mRenameBytesLength, - mRenameNameFrom, mRenameNameLength, mRenameBytesReverseOrder); + return Objects.hash(mNamePattern, mScanFilter, Arrays.hashCode(mRawDataFilter), + Arrays.hashCode(mRawDataFilterMask), mRenamePrefix, mRenameSuffix, + mRenameBytesFrom, mRenameBytesLength, mRenameNameFrom, mRenameNameLength, + mRenameBytesReverseOrder); } @Override diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 3f2fa2188d247..b0c6cbced4f9f 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -495,14 +495,9 @@ public boolean equals(@Nullable Object o) { @Override public int hashCode() { - int _hash = 1; - _hash = 31 * _hash + mAttributionSourceState.uid; - _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.packageName); - _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.attributionTag); - _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.token); - _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.renouncedPermissions); - _hash = 31 * _hash + Objects.hashCode(getNext()); - return _hash; + return Objects.hash(mAttributionSourceState.uid, mAttributionSourceState.packageName, + mAttributionSourceState.attributionTag, mAttributionSourceState.token, + Arrays.hashCode(mAttributionSourceState.renouncedPermissions), getNext()); } @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fce23cf6819a1..d2418775fc473 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -38,6 +38,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; +import android.app.AppLockManager; import android.app.BroadcastOptions; import android.app.GameManager; import android.app.IApplicationThread; @@ -6048,6 +6049,23 @@ public abstract boolean startInstrumentation(@NonNull ComponentName className, @SystemApi public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context"; + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.os.PocketManager} for accessing and listening to device pocket state. + * + * @hide + * @see #getSystemService + * @see android.os.PocketManager + */ + public static final String POCKET_SERVICE = "pocket"; + + /** + * {@link AppLockManager}. + * + * @see #getSystemService(String) + */ + public static final String APP_LOCK_SERVICE = "app_lock"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7a88a057fb778..c632ab0ee54c0 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5043,6 +5043,15 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String ACTION_SHOW_FOREGROUND_SERVICE_MANAGER = "android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER"; + /** + * Broadcast action: notify the system that the user has performed a gesture on the screen + * to launch the camera. Broadcast should be protected to receivers holding the + * {@link Manifest.permission#STATUS_BAR_SERVICE} permission. + * @hide + */ + public static final String ACTION_SCREEN_CAMERA_GESTURE = + "android.intent.action.SCREEN_CAMERA_GESTURE"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 301d1bbc8e9d2..364aa90ef5867 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1723,7 +1723,8 @@ public void onPackageAdded(UserHandle user, String packageName) throws RemoteExc public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) throws RemoteException { if (DEBUG) { - Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames); + Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + + Arrays.toString(packageNames)); } synchronized (LauncherApps.this) { for (CallbackMessageHandler callback : mCallbacks) { @@ -1736,7 +1737,8 @@ public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing) throws RemoteException { if (DEBUG) { - Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames); + Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + + Arrays.toString(packageNames)); } synchronized (LauncherApps.this) { for (CallbackMessageHandler callback : mCallbacks) { @@ -1750,7 +1752,8 @@ public void onPackagesSuspended(UserHandle user, String[] packageNames, Bundle launcherExtras) throws RemoteException { if (DEBUG) { - Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames); + Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + + Arrays.toString(packageNames)); } synchronized (LauncherApps.this) { for (CallbackMessageHandler callback : mCallbacks) { @@ -1763,7 +1766,8 @@ public void onPackagesSuspended(UserHandle user, String[] packageNames, public void onPackagesUnsuspended(UserHandle user, String[] packageNames) throws RemoteException { if (DEBUG) { - Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames); + Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + + Arrays.toString(packageNames)); } synchronized (LauncherApps.this) { for (CallbackMessageHandler callback : mCallbacks) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 44dc28d2b0faf..620bfe0778e56 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3627,11 +3627,7 @@ private boolean parseBaseApplication(Package owner, Resources res, ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE; } - if (sa.getBoolean( - R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { - ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; - } + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; if (sa.getBoolean( R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, @@ -4709,10 +4705,8 @@ private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package ow * ratio set. */ private void setMaxAspectRatio(Package owner) { - // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. - // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. - float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O - ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; + // Start at an unlimited aspect ratio unless we get a more restrictive one + float maxAspectRatio = 0; if (owner.applicationInfo.maxAspectRatio != 0) { // Use the application max aspect ration as default if set. diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 52774e354c901..1f83d7532f049 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -53,6 +53,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -2617,7 +2618,7 @@ private String toStringInner(boolean secure, boolean includeInternalData, String addIndentOrComma(sb, indent); sb.append("persons="); - sb.append(mPersons); + sb.append(Arrays.toString(mPersons)); addIndentOrComma(sb, indent); diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 5031faa81afa4..d46c5c4bac424 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -34,8 +34,10 @@ import android.util.Xml; import com.android.internal.R; -import com.android.internal.graphics.ColorUtils; -import com.android.internal.graphics.cam.Cam; +import com.android.internal.graphics.color.CieLab; +import com.android.internal.graphics.color.CieXyzAbs; +import com.android.internal.graphics.color.Zcam; +import com.android.internal.graphics.color.ZcamGamut; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -556,8 +558,9 @@ private int modulateColor(int baseColor, float alphaMod, float lStar) { final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255); if (validLStar) { - final Cam baseCam = ColorUtils.colorToCAM(baseColor); - baseColor = ColorUtils.CAMToColor(baseCam.getHue(), baseCam.getChroma(), lStar); + final Zcam baseZcam = new Zcam(new CieXyzAbs(baseColor)); + baseZcam.lightness = CieLab.lToZcamJz(lStar); + baseColor = ZcamGamut.clipToRgb8(baseZcam); } return (baseColor & 0xFFFFFF) | (alpha << 24); diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java index 35ad5033e3950..7bc551d7f49e3 100644 --- a/core/java/android/content/res/GradientColor.java +++ b/core/java/android/content/res/GradientColor.java @@ -22,13 +22,6 @@ import android.annotation.Nullable; import android.content.pm.ActivityInfo.Config; import android.content.res.Resources.Theme; - -import com.android.internal.R; -import com.android.internal.util.GrowingArrayUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.graphics.LinearGradient; import android.graphics.RadialGradient; import android.graphics.Shader; @@ -38,9 +31,16 @@ import android.util.Log; import android.util.Xml; +import com.android.internal.R; +import com.android.internal.util.GrowingArrayUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Lets you define a gradient color, which is used inside @@ -466,7 +466,7 @@ private void onColorsChange() { } if (tempColors.length < 2) { Log.w(TAG, " tag requires 2 color values specified!" + tempColors.length - + " " + tempColors); + + " " + Arrays.toString(tempColors)); } if (mGradientType == GradientDrawable.LINEAR_GRADIENT) { diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index ff072916292be..b44a3e51bf2fb 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -451,11 +451,21 @@ public void updateConfiguration(Configuration config, DisplayMetrics metrics, keyboardHidden = mConfiguration.keyboardHidden; } + // Make possible to force a particular density value for all loading + // resources. When dss is applied, we want to load all resources + // for the original density in order to make them independent of + // current dss value. + int loadDensityDpi = mConfiguration.densityDpi; + if (sLoadDensityDpi != + Configuration.DENSITY_DPI_UNDEFINED) { + loadDensityDpi = sLoadDensityDpi; + } + mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()), mConfiguration.orientation, mConfiguration.touchscreen, - mConfiguration.densityDpi, mConfiguration.keyboard, + loadDensityDpi, mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, mConfiguration.smallestScreenWidthDp, mConfiguration.screenWidthDp, mConfiguration.screenHeightDp, @@ -1480,4 +1490,11 @@ public void pop() { mSize--; } } + + private static int sLoadDensityDpi = Configuration.DENSITY_DPI_UNDEFINED; + + /** Loads a different set of resources */ + public static void setLoadDensityDpi(int loadDensityDpi) { + sLoadDensityDpi = loadDensityDpi; + } } diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java index 3270944ce7e37..e0701b90d6fab 100644 --- a/core/java/android/content/res/ThemedResourceCache.java +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -22,10 +22,10 @@ import android.content.pm.ActivityInfo.Config; import android.content.res.Resources.Theme; import android.content.res.Resources.ThemeKey; -import android.util.ArrayMap; import android.util.LongSparseArray; import java.lang.ref.WeakReference; +import java.util.HashMap; /** * Data structure used for caching data against themes. @@ -34,7 +34,7 @@ */ abstract class ThemedResourceCache { @UnsupportedAppUsage - private ArrayMap>> mThemedEntries; + private HashMap>> mThemedEntries; private LongSparseArray> mUnthemedEntries; private LongSparseArray> mNullThemedEntries; @@ -154,7 +154,7 @@ private LongSparseArray> getThemedLocked(@Nullable Theme t, boo if (mThemedEntries == null) { if (create) { - mThemedEntries = new ArrayMap<>(1); + mThemedEntries = new HashMap<>(1); } else { return null; } @@ -199,11 +199,8 @@ private LongSparseArray> getUnthemedLocked(boolean create) { private boolean prune(@Config int configChanges) { synchronized (this) { if (mThemedEntries != null) { - for (int i = mThemedEntries.size() - 1; i >= 0; i--) { - if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) { - mThemedEntries.removeAt(i); - } - } + mThemedEntries.entrySet() + .removeIf(entry -> pruneEntriesLocked(entry.getValue(), configChanges)); } pruneEntriesLocked(mNullThemedEntries, configChanges); diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 0d0615a28af3d..f872aca54681b 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -740,7 +740,10 @@ private void beginTransaction(SQLiteTransactionListener transactionListener, SQLiteSession.TRANSACTION_MODE_IMMEDIATE, transactionListener, getThreadDefaultConnectionFlags(false /*readOnly*/), null); - } finally { + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; + }finally { releaseReference(); } } @@ -753,6 +756,9 @@ public void endTransaction() { acquireReference(); try { getThreadSession().endTransaction(null); + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; } finally { releaseReference(); } @@ -870,6 +876,9 @@ private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYie acquireReference(); try { return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null); + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; } finally { releaseReference(); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 3bdd39f5d7d76..fed5b1bcfa3cc 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -45,6 +45,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSIllegalArgumentException; @@ -62,6 +63,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -171,6 +173,10 @@ public class Camera { private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200; private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400; private static final int CAMERA_MSG_FOCUS_MOVE = 0x800; + /* ### QC ADD-ONS: START */ + private static final int CAMERA_MSG_STATS_DATA = 0x1000; + private static final int CAMERA_MSG_META_DATA = 0x2000; + /* ### QC ADD-ONS: END */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private long mNativeContext; // accessed by native methods @@ -202,6 +208,17 @@ public class Camera { private boolean mShutterSoundEnabledFromApp = true; private static final int NO_ERROR = 0; + private static final int EACCESS = -13; + private static final int ENODEV = -19; + private static final int EBUSY = -16; + private static final int EINVAL = -22; + private static final int ENOSYS = -38; + private static final int EUSERS = -87; + private static final int EOPNOTSUPP = -95; + /* ### QC ADD-ONS: START */ + private CameraDataCallback mCameraDataCallback; + private CameraMetaDataCallback mCameraMetaDataCallback; + /* ### QC ADD-ONS: END */ /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -264,6 +281,25 @@ public class Camera { */ private static final int CAMERA_FACE_DETECTION_SW = 1; + /** + * @hide + */ + public static boolean shouldExposeAuxCamera() { + /** + * Force to expose only two cameras + * if the package name does not falls in this bucket + */ + String packageName = ActivityThread.currentOpPackageName(); + if (packageName == null) + return true; + List packageList = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packagelist", packageName).split(",")); + List packageExcludelist = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packageexcludelist", "").split(",")); + + return packageList.contains(packageName) && !packageExcludelist.contains(packageName); + } + /** * Returns the number of physical cameras available on this device. * The return value of this method might change dynamically if the device @@ -279,7 +315,20 @@ public class Camera { * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. */ - public native static int getNumberOfCameras(); + public static int getNumberOfCameras() { + int numberOfCameras = _getNumberOfCameras(); + if (!shouldExposeAuxCamera() && numberOfCameras > 2) { + numberOfCameras = 2; + } + return numberOfCameras; + } + + /** + * Returns the number of physical cameras available on this device. + * + * @hide + */ + public native static int _getNumberOfCameras(); /** * Returns the information about a particular camera. @@ -290,6 +339,9 @@ public class Camera { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } _getCameraInfo(cameraId, cameraInfo); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); IAudioService audioService = IAudioService.Stub.asInterface(b); @@ -323,6 +375,17 @@ public static class CameraInfo { */ public static final int CAMERA_FACING_FRONT = 1; + /* ### QC ADD-ONS: START TBD*/ + /** @hide + * camera is in ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_ZSL = 2; + + /** @hide + * camera is in non-ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_NONZSL = 3; + /* ### QC ADD-ONS: END */ /** * The direction that the camera faces. It should be * CAMERA_FACING_BACK or CAMERA_FACING_FRONT. @@ -474,6 +537,10 @@ private int cameraInit(int cameraId) { mPostviewCallback = null; mUsingPreviewAllocation = false; mZoomListener = null; + /* ### QC ADD-ONS: START */ + mCameraDataCallback = null; + mCameraMetaDataCallback = null; + /* ### QC ADD-ONS: END */ Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -490,6 +557,9 @@ private int cameraInit(int cameraId) { /** used by Camera#open, Camera#open(int) */ Camera(int cameraId) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } int err = cameraInit(cameraId); if (checkInitErrors(err)) { if (err == -EACCES) { @@ -1213,7 +1283,23 @@ public void handleMessage(Message msg) { mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera); } return; + /* ### QC ADD-ONS: START */ + case CAMERA_MSG_STATS_DATA: + int statsdata[] = new int[257]; + for(int i =0; i<257; i++ ) { + statsdata[i] = byteToInt( (byte[])msg.obj, i*4); + } + if (mCameraDataCallback != null) { + mCameraDataCallback.onCameraData(statsdata, mCamera); + } + return; + case CAMERA_MSG_META_DATA: + if (mCameraMetaDataCallback != null) { + mCameraMetaDataCallback.onCameraMetaData((byte[])msg.obj, mCamera); + } + return; + /* ### QC ADD-ONS: END */ default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -1724,7 +1810,11 @@ private void updateAppOpsPlayAudio() { } catch (RemoteException e) { Log.e(TAG, "Audio service is unavailable for queries"); } - _enableShutterSound(false); + try { + _enableShutterSound(false); + } catch (Exception e) { + Log.e(TAG, "Couldn't disable shutter sound"); + } } else { enableShutterSound(mShutterSoundEnabledFromApp); } @@ -1732,6 +1822,20 @@ private void updateAppOpsPlayAudio() { } } + /** + * Send a vendor-specific camera command + * + * @hide + */ + public final void sendVendorCommand(int cmd, int arg1, int arg2) { + if (cmd < 1000) { + throw new IllegalArgumentException("Command numbers must be at least 1000"); + } + _sendVendorCommand(cmd, arg1, arg2); + } + + private native final void _sendVendorCommand(int cmd, int arg1, int arg2); + /** * Callback interface for zoom changes during a smooth zoom operation. * @@ -1954,6 +2058,23 @@ public Face() { * as a set. Either they are all valid, or none of them are. */ public Point mouth = null; + + /** + * {@hide} + */ + public int smileDegree = 0; + /** + * {@hide} + */ + public int smileScore = 0; + /** + * {@hide} + */ + public int blinkDetected = 0; + /** + * {@hide} + */ + public int faceRecognised = 0; } /** @@ -2078,6 +2199,27 @@ public Parameters getParameters() { return p; } + /** @hide + * Returns the current cct value of white balance. + * + * If it's in AWB mode, cct is determined by stats/awb module. + * + * If it's in Manual WB mode, it actually returns cct value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getWBCurrentCCT() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int cct = 0; + if (p.getWBCurrentCCT() != null) { + cct = Integer.parseInt(p.getWBCurrentCCT()); + } + + return cct; + } + /** * Returns an empty {@link Parameters} for testing purpose. * @@ -2091,6 +2233,157 @@ public static Parameters getEmptyParameters() { return camera.new Parameters(); } + /* ### QC ADD-ONS: START */ + private static int byteToInt(byte[] b, int offset) { + int value = 0; + for (int i = 0; i < 4; i++) { + int shift = (4 - 1 - i) * 8; + value += (b[(3-i) + offset] & 0x000000FF) << shift; + } + return value; + } + /** @hide + * Handles the callback for when Camera Data is available. + * data is read from the camera. + */ + public interface CameraDataCallback { + /** + * Callback for when camera data is available. + * + * @param data a int array of the camera data + * @param camera the Camera service object + */ + void onCameraData(int[] data, Camera camera); + }; + + /** @hide + * Set camera histogram mode and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setHistogramMode(CameraDataCallback cb) + { + mCameraDataCallback = cb; + native_setHistogramMode(cb!=null); + } + private native final void native_setHistogramMode(boolean mode); + + /** @hide + * Set camera histogram command to send data. + * + */ + public final void sendHistogramData() + { + native_sendHistogramData(); + } + private native final void native_sendHistogramData(); + + /** @hide + * Handles the callback for when Camera Meta Data is available. + * Meta data is read from the camera. + */ + public interface CameraMetaDataCallback { + /** + * Callback for when camera meta data is available. + * + * @param data a byte array of the camera meta data + * @param camera the Camera service object + */ + void onCameraMetaData(byte[] data, Camera camera); + }; + + /** @hide + * Set camera meta data and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setMetadataCb(CameraMetaDataCallback cb) + { + mCameraMetaDataCallback = cb; + native_setMetadataCb(cb!=null); + } + private native final void native_setMetadataCb(boolean mode); + + /** @hide + * Set camera face detection command to send meta data. + */ + public final void sendMetaData() + { + native_sendMetaData(); + } + private native final void native_sendMetaData(); + + /** @hide + * Configure longshot mode. Available only in ZSL. + * + * @param enable enable/disable this mode + */ + public final void setLongshot(boolean enable) + { + native_setLongshot(enable); + } + private native final void native_setLongshot(boolean enable); + + /** @hide + * Handles the Touch Co-ordinate. + */ + public class Coordinate { + /** + * Sets the x,y co-ordinates for a touch event + * + * @param x the x co-ordinate (pixels) + * @param y the y co-ordinate (pixels) + */ + public Coordinate(int x, int y) { + xCoordinate = x; + yCoordinate = y; + } + /** + * Compares {@code obj} to this co-ordinate. + * + * @param obj the object to compare this co-ordinate with. + * @return {@code true} if the xCoordinate and yCoordinate of {@code obj} is the + * same as those of this coordinate. {@code false} otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Coordinate)) { + return false; + } + Coordinate c = (Coordinate) obj; + return xCoordinate == c.xCoordinate && yCoordinate == c.yCoordinate; + } + + /** x co-ordinate for the touch event*/ + public int xCoordinate; + + /** y co-ordinate for the touch event */ + public int yCoordinate; + }; + + /** @hide + * Returns the current focus position. + * + * If it's in AF mode, it's the lens position after af is done. + * + * If it's in Manual Focus mode, it actually returns the value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getCurrentFocusPosition() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int focus_pos = -1; + if (p.getCurrentFocusPosition() != null) { + focus_pos = Integer.parseInt(p.getCurrentFocusPosition()); + } + return focus_pos; + } + + /* ### QC ADD-ONS: END */ /** * Returns a copied {@link Parameters}; for shim use only. * @@ -2352,6 +2645,10 @@ public class Parameters { public static final String WHITE_BALANCE_CLOUDY_DAYLIGHT = "cloudy-daylight"; public static final String WHITE_BALANCE_TWILIGHT = "twilight"; public static final String WHITE_BALANCE_SHADE = "shade"; + /** @hide + * wb manual cct mode. + */ + public static final String WHITE_BALANCE_MANUAL_CCT = "manual-cct"; // Values for color effect settings. public static final String EFFECT_NONE = "none"; @@ -2399,6 +2696,11 @@ public class Parameters { */ public static final String FLASH_MODE_TORCH = "torch"; + /** @hide + * Scene mode is off. + */ + public static final String SCENE_MODE_ASD = "asd"; + /** * Scene mode is off. */ @@ -2475,6 +2777,14 @@ public class Parameters { * Capture the naturally warm color of scenes lit by candles. */ public static final String SCENE_MODE_CANDLELIGHT = "candlelight"; + /** @hide + * SCENE_MODE_BACKLIGHT + **/ + public static final String SCENE_MODE_BACKLIGHT = "backlight"; + /** @hide + * SCENE_MODE_FLOWERS + **/ + public static final String SCENE_MODE_FLOWERS = "flowers"; /** * Applications are looking for a barcode. Camera driver will be @@ -2517,6 +2827,13 @@ public class Parameters { */ public static final String FOCUS_MODE_FIXED = "fixed"; + /** @hide + * Normal focus mode. Applications should call + * {@link #autoFocus(AutoFocusCallback)} to start the focus in this + * mode. + */ + public static final String FOCUS_MODE_NORMAL = "normal"; + /** * Extended depth of field (EDOF). Focusing is done digitally and * continuously. Applications should not call {@link @@ -2569,6 +2886,11 @@ public class Parameters { */ public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + /** @hide + * manual focus mode + */ + public static final String FOCUS_MODE_MANUAL_POSITION = "manual"; + // Indices for focus distance array. /** * The array index of near focus distance for use with @@ -2605,11 +2927,15 @@ public class Parameters { // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; + private static final String PIXEL_FORMAT_YUV420SP_ADRENO = "yuv420sp-adreno"; private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv"; private static final String PIXEL_FORMAT_YUV420P = "yuv420p"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; + private static final String PIXEL_FORMAT_RAW = "raw"; + private static final String PIXEL_FORMAT_YV12 = "yv12"; + private static final String PIXEL_FORMAT_NV12 = "nv12"; /** * Order matters: Keys that are {@link #set(String, String) set} later @@ -3429,8 +3755,11 @@ public void setGpsProcessingMethod(String processing_method) { * parameters. */ public void removeGpsData() { + remove(KEY_QC_GPS_LATITUDE_REF); remove(KEY_GPS_LATITUDE); + remove(KEY_QC_GPS_LONGITUDE_REF); remove(KEY_GPS_LONGITUDE); + remove(KEY_QC_GPS_ALTITUDE_REF); remove(KEY_GPS_ALTITUDE); remove(KEY_GPS_TIMESTAMP); remove(KEY_GPS_PROCESSING_METHOD); @@ -3600,6 +3929,7 @@ public String getSceneMode() { * @see #getSceneMode() */ public void setSceneMode(String value) { + if(getSupportedSceneModes() == null) return; set(KEY_SCENE_MODE, value); } @@ -3637,6 +3967,7 @@ public String getFlashMode() { * @see #getFlashMode() */ public void setFlashMode(String value) { + if(getSupportedFlashModes() == null) return; set(KEY_FLASH_MODE, value); } @@ -4321,6 +4652,7 @@ private void splitInt(String str, int[] output) { splitter.setString(str); int index = 0; for (String s : splitter) { + s = s.replaceAll("\\s",""); output[index++] = Integer.parseInt(s); } } @@ -4454,5 +4786,1231 @@ private boolean same(String s1, String s2) { if (s1 != null && s1.equals(s2)) return true; return false; } + /* ### QC ADD-ONS: START */ + + /* ### QC ADDED PARAMETER KEYS*/ + private static final String KEY_QC_HFR_SIZE = "hfr-size"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_MODE = "preview-frame-rate-mode"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_AUTO_MODE = "frame-rate-auto"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_FIXED_MODE = "frame-rate-fixed"; + private static final String KEY_QC_GPS_LATITUDE_REF = "gps-latitude-ref"; + private static final String KEY_QC_GPS_LONGITUDE_REF = "gps-longitude-ref"; + private static final String KEY_QC_GPS_ALTITUDE_REF = "gps-altitude-ref"; + private static final String KEY_QC_GPS_STATUS = "gps-status"; + private static final String KEY_QC_EXIF_DATETIME = "exif-datetime"; + private static final String KEY_QC_TOUCH_AF_AEC = "touch-af-aec"; + private static final String KEY_QC_TOUCH_INDEX_AEC = "touch-index-aec"; + private static final String KEY_QC_TOUCH_INDEX_AF = "touch-index-af"; + private static final String KEY_QC_MANUAL_FOCUS_POSITION = "manual-focus-position"; + private static final String KEY_QC_MANUAL_FOCUS_POS_TYPE = "manual-focus-pos-type"; + private static final String KEY_QC_SCENE_DETECT = "scene-detect"; + private static final String KEY_QC_ISO_MODE = "iso"; + private static final String KEY_QC_EXPOSURE_TIME = "exposure-time"; + private static final String KEY_QC_MIN_EXPOSURE_TIME = "min-exposure-time"; + private static final String KEY_QC_MAX_EXPOSURE_TIME = "max-exposure-time"; + private static final String KEY_QC_LENSSHADE = "lensshade"; + private static final String KEY_QC_HISTOGRAM = "histogram"; + private static final String KEY_QC_SKIN_TONE_ENHANCEMENT = "skinToneEnhancement"; + private static final String KEY_QC_AUTO_EXPOSURE = "auto-exposure"; + private static final String KEY_QC_SHARPNESS = "sharpness"; + private static final String KEY_QC_MAX_SHARPNESS = "max-sharpness"; + private static final String KEY_QC_CONTRAST = "contrast"; + private static final String KEY_QC_MAX_CONTRAST = "max-contrast"; + private static final String KEY_QC_SATURATION = "saturation"; + private static final String KEY_QC_MAX_SATURATION = "max-saturation"; + private static final String KEY_QC_DENOISE = "denoise"; + private static final String KEY_QC_CONTINUOUS_AF = "continuous-af"; + private static final String KEY_QC_SELECTABLE_ZONE_AF = "selectable-zone-af"; + private static final String KEY_QC_FACE_DETECTION = "face-detection"; + private static final String KEY_QC_MEMORY_COLOR_ENHANCEMENT = "mce"; + private static final String KEY_QC_REDEYE_REDUCTION = "redeye-reduction"; + private static final String KEY_QC_ZSL = "zsl"; + private static final String KEY_QC_CAMERA_MODE = "camera-mode"; + private static final String KEY_QC_VIDEO_HIGH_FRAME_RATE = "video-hfr"; + private static final String KEY_QC_VIDEO_HDR = "video-hdr"; + private static final String KEY_QC_POWER_MODE = "power-mode"; + private static final String KEY_QC_POWER_MODE_SUPPORTED = "power-mode-supported"; + private static final String KEY_QC_WB_MANUAL_CCT = "wb-manual-cct"; + private static final String KEY_QC_MIN_WB_CCT = "min-wb-cct"; + private static final String KEY_QC_MAX_WB_CCT = "max-wb-cct"; + private static final String KEY_QC_AUTO_HDR_ENABLE = "auto-hdr-enable"; + private static final String KEY_QC_VIDEO_ROTATION = "video-rotation"; + + /** @hide + * KEY_QC_AE_BRACKET_HDR + **/ + public static final String KEY_QC_AE_BRACKET_HDR = "ae-bracket-hdr"; + + /* ### QC ADDED PARAMETER VALUES*/ + + // Values for touch af/aec settings. + /** @hide + * TOUCH_AF_AEC_OFF + **/ + public static final String TOUCH_AF_AEC_OFF = "touch-off"; + /** @hide + * TOUCH_AF_AEC_ON + **/ + public static final String TOUCH_AF_AEC_ON = "touch-on"; + + // Values for auto exposure settings. + /** @hide + * Auto exposure frame-avg + **/ + public static final String AUTO_EXPOSURE_FRAME_AVG = "frame-average"; + /** @hide + * Auto exposure center weighted + **/ + public static final String AUTO_EXPOSURE_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * Auto exposure spot metering + **/ + public static final String AUTO_EXPOSURE_SPOT_METERING = "spot-metering"; + + //Values for ISO settings + /** @hide + * ISO_AUTO + **/ + public static final String ISO_AUTO = "auto"; + /** @hide + * ISO_HJR + **/ + public static final String ISO_HJR = "ISO_HJR"; + /** @hide + * ISO_100 + **/ + public static final String ISO_100 = "ISO100"; + /** @hide + * ISO_200 + **/ + public static final String ISO_200 = "ISO200"; + /** @hide + * ISO_400 + **/ + public static final String ISO_400 = "ISO400"; + /** @hide + * ISO_800 + **/ + public static final String ISO_800 = "ISO800"; + /** @hide + * ISO_1600 + **/ + public static final String ISO_1600 = "ISO1600"; + + /** @hide + * ISO_3200 + **/ + public static final String ISO_3200 = "ISO3200"; + + //Values for Lens Shading + /** @hide + * LENSSHADE_ENABLE + **/ + public static final String LENSSHADE_ENABLE = "enable"; + /** @hide + * LENSSHADE_DISABLE + **/ + public static final String LENSSHADE_DISABLE= "disable"; + + //Values for Histogram + /** @hide + * Histogram enable + **/ + public static final String HISTOGRAM_ENABLE = "enable"; + /** @hide + * Histogram disable + **/ + public static final String HISTOGRAM_DISABLE= "disable"; + + //Values for Skin Tone Enhancement + /** @hide + * SKIN_TONE_ENHANCEMENT_ENABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_ENABLE = "enable"; + /** @hide + * SKIN_TONE_ENHANCEMENT_DISABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_DISABLE= "disable"; + + // Values for MCE settings. + /** @hide + * MCE_ENaBLE + **/ + public static final String MCE_ENABLE = "enable"; + /** @hide + * MCE_DISABLE + **/ + public static final String MCE_DISABLE = "disable"; + + // Values for ZSL settings. + /** @hide + * ZSL_ON + **/ + public static final String ZSL_ON = "on"; + /** @hide + * ZSL_OFF + **/ + public static final String ZSL_OFF = "off"; + + // Values for HDR Bracketing settings. + + /** @hide + * AEC bracketing off + **/ + public static final String AE_BRACKET_HDR_OFF = "Off"; + /** @hide + * AEC bracketing hdr + **/ + public static final String AE_BRACKET_HDR = "HDR"; + /** @hide + * AEC bracketing aec-bracket + **/ + public static final String AE_BRACKET = "AE-Bracket"; + + // Values for Power mode. + /** @hide + * LOW_POWER + **/ + public static final String LOW_POWER = "Low_Power"; + /** @hide + * NORMAL_POWER + **/ + public static final String NORMAL_POWER = "Normal_Power"; + + // Values for HFR settings. + /** @hide + * VIDEO_HFR_OFF + **/ + public static final String VIDEO_HFR_OFF = "off"; + /** @hide + * VIDEO_HFR_2X + **/ + public static final String VIDEO_HFR_2X = "60"; + /** @hide + * VIDEO_HFR_3X + **/ + public static final String VIDEO_HFR_3X = "90"; + /** @hide + * VIDEO_HFR_4X + **/ + public static final String VIDEO_HFR_4X = "120"; + + // Values for auto scene detection settings. + /** @hide + * SCENE_DETECT_OFF + **/ + public static final String SCENE_DETECT_OFF = "off"; + /** @hide + * SCENE_DETECT_ON + **/ + public static final String SCENE_DETECT_ON = "on"; + + //Values for Continuous AF + + /** @hide + * CAF off + **/ + public static final String CONTINUOUS_AF_OFF = "caf-off"; + /** @hide + * CAF on + **/ + public static final String CONTINUOUS_AF_ON = "caf-on"; + /** @hide + * Denoise off + **/ + public static final String DENOISE_OFF = "denoise-off"; + /** @hide + * Denoise on + **/ + public static final String DENOISE_ON = "denoise-on"; + + // Values for Redeye Reduction settings. + /** @hide + * REDEYE_REDUCTION_ENABLE + **/ + public static final String REDEYE_REDUCTION_ENABLE = "enable"; + /** @hide + * REDEYE_REDUCTION_DISABLE + **/ + public static final String REDEYE_REDUCTION_DISABLE = "disable"; + + // Values for selectable zone af settings. + /** @hide + * SELECTABLE_ZONE_AF_AUTO + **/ + public static final String SELECTABLE_ZONE_AF_AUTO = "auto"; + /** @hide + * SELECTABLE_ZONE_AF_SPOTMETERING + **/ + public static final String SELECTABLE_ZONE_AF_SPOTMETERING = "spot-metering"; + /** @hide + * SELECTABLE_ZONE_AF_CENTER_WEIGHTED + **/ + public static final String SELECTABLE_ZONE_AF_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * SELECTABLE_ZONE_AF_FRAME_AVERAGE + **/ + public static final String SELECTABLE_ZONE_AF_FRAME_AVERAGE = "frame-average"; + + // Values for Face Detection settings. + /** @hide + * Face Detection off + **/ + public static final String FACE_DETECTION_OFF = "off"; + /** @hide + * Face Detction on + **/ + public static final String FACE_DETECTION_ON = "on"; + + // Values for video rotation settings. + + /** @hide + * VIDEO_ROTATION_0 + **/ + public static final String VIDEO_ROTATION_0 = "0"; + /** @hide + * VIDEO_ROTATION_90 + **/ + public static final String VIDEO_ROTATION_90 = "90"; + /** @hide + * VIDEO_ROTATION_180 + **/ + public static final String VIDEO_ROTATION_180 = "180"; + /** @hide + * VIDEO_ROTATION_270 + **/ + public static final String VIDEO_ROTATION_270 = "270"; + + /* ### QC ADDED PARAMETER APIS*/ + /** @hide + * Gets the supported preview sizes in high frame rate recording mode. + * + * @return a list of Size object. This method will always return a list + * with at least one element. + */ + public List getSupportedHfrSizes() { + String str = get(KEY_QC_HFR_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** @hide + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + public List getSupportedTouchAfAec() { + String str = get(KEY_QC_TOUCH_AF_AEC + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + + /** @hide + * Gets the supported frame rate modes. + * + * @return a List of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public List getSupportedPreviewFrameRateModes() { + String str = get(KEY_QC_PREVIEW_FRAME_RATE_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto scene detection modes. + * + * @return a List of SCENE_DETECT_XXX string constant. null if scene detection + * setting is not supported. + * + */ + public List getSupportedSceneDetectModes() { + String str = get(KEY_QC_SCENE_DETECT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ISO values. + * + * @return a List of FLASH_MODE_XXX string constants. null if flash mode + * setting is not supported. + */ + public List getSupportedIsoValues() { + String str = get(KEY_QC_ISO_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Lensshade modes. + * + * @return a List of LENS_MODE_XXX string constants. null if lens mode + * setting is not supported. + */ + public List getSupportedLensShadeModes() { + String str = get(KEY_QC_LENSSHADE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Histogram modes. + * + * @return a List of HISTOGRAM_XXX string constants. null if histogram mode + * setting is not supported. + */ + public List getSupportedHistogramModes() { + String str = get(KEY_QC_HISTOGRAM + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Skin Tone Enhancement modes. + * + * @return a List of SKIN_TONE_ENHANCEMENT_XXX string constants. null if skin tone enhancement + * setting is not supported. + */ + public List getSupportedSkinToneEnhancementModes() { + String str = get(KEY_QC_SKIN_TONE_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto exposure setting. + * + * @return a List of AUTO_EXPOSURE_XXX string constants. null if auto exposure + * setting is not supported. + */ + public List getSupportedAutoexposure() { + String str = get(KEY_QC_AUTO_EXPOSURE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported MCE modes. + * + * @return a List of MCE_ENABLE/DISABLE string constants. null if MCE mode + * setting is not supported. + */ + public List getSupportedMemColorEnhanceModes() { + String str = get(KEY_QC_MEMORY_COLOR_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ZSL modes. + * + * @return a List of ZSL_OFF/OFF string constants. null if ZSL mode + * setting is not supported. + */ + public List getSupportedZSLModes() { + String str = get(KEY_QC_ZSL + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Video HDR modes. + * + * @return a List of Video HDR_OFF/OFF string constants. null if + * Video HDR mode setting is not supported. + */ + public List getSupportedVideoHDRModes() { + String str = get(KEY_QC_VIDEO_HDR + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported HFR modes. + * + * @return a List of VIDEO_HFR_XXX string constants. null if hfr mode + * setting is not supported. + */ + public List getSupportedVideoHighFrameRateModes() { + String str = get(KEY_QC_VIDEO_HIGH_FRAME_RATE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Continuous AF modes. + * + * @return a List of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public List getSupportedContinuousAfModes() { + String str = get(KEY_QC_CONTINUOUS_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported DENOISE modes. + * + * @return a List of DENOISE_XXX string constant. null if DENOISE + * setting is not supported. + * + */ + public List getSupportedDenoiseModes() { + String str = get(KEY_QC_DENOISE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported selectable zone af setting. + * + * @return a List of SELECTABLE_ZONE_AF_XXX string constants. null if selectable zone af + * setting is not supported. + */ + public List getSupportedSelectableZoneAf() { + String str = get(KEY_QC_SELECTABLE_ZONE_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported face detection modes. + * + * @return a List of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public List getSupportedFaceDetectionModes() { + String str = get(KEY_QC_FACE_DETECTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported redeye reduction modes. + * + * @return a List of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public List getSupportedRedeyeReductionModes() { + String str = get(KEY_QC_REDEYE_REDUCTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Sets GPS altitude reference. This will be stored in JPEG EXIF header. + * @param altRef reference GPS altitude in meters. + */ + public void setGpsAltitudeRef(double altRef) { + set(KEY_QC_GPS_ALTITUDE_REF, Double.toString(altRef)); + } + + /** @hide + * Sets GPS Status. This will be stored in JPEG EXIF header. + * + * @param status GPS status (UTC in seconds since January 1, + * 1970). + */ + public void setGpsStatus(double status) { + set(KEY_QC_GPS_STATUS, Double.toString(status)); + } + + /** @hide + * Sets the touch co-ordinate for Touch AEC. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAec(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AEC, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAec() { + String pair = get(KEY_QC_TOUCH_INDEX_AEC); + return strToCoordinate(pair); + } + + /** @hide + * Sets the touch co-ordinate for Touch AF. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAf(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AF, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAf() { + String pair = get(KEY_QC_TOUCH_INDEX_AF); + return strToCoordinate(pair); + } + /** @hide + * Set Sharpness Level + * + * @param sharpness level + */ + public void setSharpness(int sharpness){ + if((sharpness < 0) || (sharpness > getMaxSharpness()) ) + throw new IllegalArgumentException( + "Invalid Sharpness " + sharpness); + + set(KEY_QC_SHARPNESS, String.valueOf(sharpness)); + } + + /** @hide + * Set Contrast Level + * + * @param contrast level + */ + public void setContrast(int contrast){ + if((contrast < 0 ) || (contrast > getMaxContrast())) + throw new IllegalArgumentException( + "Invalid Contrast " + contrast); + + set(KEY_QC_CONTRAST, String.valueOf(contrast)); + } + + /** @hide + * Set Saturation Level + * + * @param saturation level + */ + public void setSaturation(int saturation){ + if((saturation < 0 ) || (saturation > getMaxSaturation())) + throw new IllegalArgumentException( + "Invalid Saturation " + saturation); + + set(KEY_QC_SATURATION, String.valueOf(saturation)); + } + + /** @hide + * @return true if full size video snapshot is supported. + */ + public boolean isPowerModeSupported() { + String str = get(KEY_QC_POWER_MODE_SUPPORTED); + return TRUE.equals(str); + } + + /** @hide + * Get Sharpness level + * + * @return sharpness level + */ + public int getSharpness(){ + return getInt(KEY_QC_SHARPNESS); + } + + /** @hide + * Get Max Sharpness Level + * + * @return max sharpness level + */ + public int getMaxSharpness(){ + return getInt(KEY_QC_MAX_SHARPNESS); + } + + /** @hide + * Get Contrast level + * + * @return contrast level + */ + public int getContrast(){ + return getInt(KEY_QC_CONTRAST); + } + + /** @hide + * Get Max Contrast Level + * + * @return max contrast level + */ + public int getMaxContrast(){ + return getInt(KEY_QC_MAX_CONTRAST); + } + + /** @hide + * Get Saturation level + * + * @return saturation level + */ + public int getSaturation(){ + return getInt(KEY_QC_SATURATION); + } + + /** @hide + * Get Max Saturation Level + * + * @return max contrast level + */ + public int getMaxSaturation(){ + return getInt(KEY_QC_MAX_SATURATION); + } + + /** @hide + * Sets GPS latitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param latRef GPS latitude reference coordinate. + */ + public void setGpsLatitudeRef(String latRef) { + set(KEY_QC_GPS_LATITUDE_REF, latRef); + } + + /** @hide + * Sets GPS longitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param lonRef GPS longitude reference coordinate. + */ + public void setGpsLongitudeRef(String lonRef) { + set(KEY_QC_GPS_LONGITUDE_REF, lonRef); + } + + /** @hide + * Sets system timestamp. This will be stored in JPEG EXIF header. + * + * @param dateTime current timestamp (UTC in seconds since January 1, + * 1970). + */ + public void setExifDateTime(String dateTime) { + set(KEY_QC_EXIF_DATETIME, dateTime); + } + + /** @hide + * Gets the current Touch AF/AEC setting. + * + * @return one of TOUCH_AF_AEC_XXX string constant. null if Touch AF/AEC + * setting is not supported. + * + */ + public String getTouchAfAec() { + return get(KEY_QC_TOUCH_AF_AEC); + } + + /** @hide + * Sets the current TOUCH AF/AEC setting. + * + * @param value TOUCH_AF_AEC_XXX string constants. + * + */ + public void setTouchAfAec(String value) { + set(KEY_QC_TOUCH_AF_AEC, value); + } + + /** @hide + * Gets the current redeye reduction setting. + * + * @return one of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public String getRedeyeReductionMode() { + return get(KEY_QC_REDEYE_REDUCTION); + } + + /** @hide + * Sets the redeye reduction. Other parameters may be changed after changing + * redeye reduction. After setting redeye reduction, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value REDEYE_REDUCTION_XXX string constants. + * + */ + public void setRedeyeReductionMode(String value) { + set(KEY_QC_REDEYE_REDUCTION, value); + } + + /** @hide + * Gets the frame rate mode setting. + * + * @return one of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public String getPreviewFrameRateMode() { + return get(KEY_QC_PREVIEW_FRAME_RATE_MODE); + } + + /** @hide + * Sets the frame rate mode. + * + * @param value FRAME_RATE_XXX_MODE string constants. + */ + public void setPreviewFrameRateMode(String value) { + set(KEY_QC_PREVIEW_FRAME_RATE_MODE, value); + } + + /** @hide + * Gets the current auto scene detection setting. + * + * @return one of SCENE_DETECT_XXX string constant. null if auto scene detection + * setting is not supported. + * + */ + public String getSceneDetectMode() { + return get(KEY_QC_SCENE_DETECT); + } + + /** @hide + * Sets the auto scene detect. Other parameters may be changed after changing + * scene detect. After setting auto scene detection, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value SCENE_DETECT_XXX string constants. + * + */ + public void setSceneDetectMode(String value) { + set(KEY_QC_SCENE_DETECT, value); + } + + /** @hide + * Gets the current hdr bracketing mode setting. + * + * @return current hdr bracketing mode. + * @see #KEY_AE_BRACKET_OFF + * @see #KEY_AE_BRACKET_HDR + * @see #KEY_AE_BRACKET_BRACKATING + */ + public String getAEBracket() { + return get(KEY_QC_AE_BRACKET_HDR); + } + + /** @hide + * Sets the Power mode. + * + * @param value Power mode. + * @see #getPowerMode() + */ + public void setPowerMode(String value) { + set(KEY_QC_POWER_MODE, value); + } + + /** @hide + * Gets the current power mode setting. + * + * @return current power mode. null if power mode setting is not + * supported. + * @see #POWER_MODE_LOW + * @see #POWER_MODE_NORMAL + */ + public String getPowerMode() { + return get(KEY_QC_POWER_MODE); + } + + /** @hide + * Set HDR-Bracketing Level + * + * @param value HDR-Bracketing + */ + public void setAEBracket(String value){ + set(KEY_QC_AE_BRACKET_HDR, value); + } + + /** @hide + * Gets the current ISO setting. + * + * @return one of ISO_XXX string constant. null if ISO + * setting is not supported. + */ + public String getISOValue() { + return get(KEY_QC_ISO_MODE); + } + + /** @hide + * Sets the ISO. + * + * @param iso ISO_XXX string constant. + */ + public void setISOValue(String iso) { + set(KEY_QC_ISO_MODE, iso); + } + + /** @hide + * Sets the exposure time. + * + * @param value exposure time. + */ + public void setExposureTime(int value) { + set(KEY_QC_EXPOSURE_TIME, Integer.toString(value)); + } + + /** @hide + * Gets the current exposure time. + * + * @return exposure time. + */ + public String getExposureTime() { + return get(KEY_QC_EXPOSURE_TIME); + } + + /** @hide + * Gets the min supported exposure time. + * + * @return min supported exposure time. + */ + public String getMinExposureTime() { + return get(KEY_QC_MIN_EXPOSURE_TIME); + } + + /** @hide + * Gets the max supported exposure time. + * + * @return max supported exposure time. + */ + public String getMaxExposureTime() { + return get(KEY_QC_MAX_EXPOSURE_TIME); + } + + /** @hide + * Gets the current LensShade Mode. + * + * @return LensShade Mode + */ + public String getLensShade() { + return get(KEY_QC_LENSSHADE); + } + + /** @hide + * Sets the current LensShade Mode. + * + * @return LensShade Mode + */ + public void setLensShade(String lensshade) { + set(KEY_QC_LENSSHADE, lensshade); + } + + /** @hide + * Gets the current auto exposure setting. + * + * @return one of AUTO_EXPOSURE_XXX string constant. null if auto exposure + * setting is not supported. + */ + public String getAutoExposure() { + return get(KEY_QC_AUTO_EXPOSURE); + } + + /** @hide + * Sets the current auto exposure setting. + * + * @param value AUTO_EXPOSURE_XXX string constants. + */ + public void setAutoExposure(String value) { + set(KEY_QC_AUTO_EXPOSURE, value); + } + + /** @hide + * Gets the current MCE Mode. + * + * @return MCE value + */ + public String getMemColorEnhance() { + return get(KEY_QC_MEMORY_COLOR_ENHANCEMENT); + } + + /** @hide + * Sets the current MCE Mode. + * + * @return MCE Mode + */ + public void setMemColorEnhance(String mce) { + set(KEY_QC_MEMORY_COLOR_ENHANCEMENT, mce); + } + + /** @hide + * Set white balance manual cct value. + * + * @param cct user CCT setting. + */ + public void setWBManualCCT(int cct) { + set(KEY_QC_WB_MANUAL_CCT, Integer.toString(cct)); + } + + /** @hide + * Gets the WB min supported CCT. + * + * @return min cct value. + */ + public String getWBMinCCT() { + return get(KEY_QC_MIN_WB_CCT); + } + + /** @hide + * Gets the WB max supported CCT. + * + * @return max cct value. + */ + public String getMaxWBCCT() { + return get(KEY_QC_MAX_WB_CCT); + } + + /** @hide + * Gets the current WB CCT. + * + * @return CCT value + */ + public String getWBCurrentCCT() { + return get(KEY_QC_WB_MANUAL_CCT); + } + + /** @hide + * Gets the current ZSL Mode. + * + * @return ZSL mode value + */ + public String getZSLMode() { + return get(KEY_QC_ZSL); + } + + /** @hide + * Sets the current ZSL Mode. ZSL mode is set as a 0th bit in KEY_CAMERA_MODE. + * + * @return null + */ + public void setZSLMode(String zsl) { + set(KEY_QC_ZSL, zsl); + } + + /** @hide + * Sets the current Auto HDR Mode. + * @ auto_hdr auto hdr string for enable/disable + * @return null + */ + public void setAutoHDRMode(String auto_hdr){ + set(KEY_QC_AUTO_HDR_ENABLE,auto_hdr); + } + + /** @hide + * Gets the current Camera Mode Flag. Camera mode includes a + * flag(byte) which indicates different camera modes. + * For now support for ZSL added at bit0 + * + * @return Camera Mode. + */ + public String getCameraMode() { + return get(KEY_QC_CAMERA_MODE); + } + + /** @hide + * Sets the current Camera Mode. + * + * @return null + */ + public void setCameraMode(int cameraMode) { + set(KEY_QC_CAMERA_MODE, cameraMode); + } + + private static final int MANUAL_FOCUS_POS_TYPE_INDEX = 0; + private static final int MANUAL_FOCUS_POS_TYPE_DAC = 1; + /** @hide + * Set focus position. + * + * @param pos user setting of focus position. + */ + public void setFocusPosition(int type, int pos) { + set(KEY_QC_MANUAL_FOCUS_POS_TYPE, Integer.toString(type)); + set(KEY_QC_MANUAL_FOCUS_POSITION, Integer.toString(pos)); + } + + /** @hide + * Gets the current focus position. + * + * @return current focus position + */ + public String getCurrentFocusPosition() { + return get(KEY_QC_MANUAL_FOCUS_POSITION); + } + + + /** @hide + * Gets the current HFR Mode. + * + * @return VIDEO_HFR_XXX string constants + */ + public String getVideoHighFrameRate() { + return get(KEY_QC_VIDEO_HIGH_FRAME_RATE); + } + + /** @hide + * Sets the current HFR Mode. + * + * @param hfr VIDEO_HFR_XXX string constants + */ + public void setVideoHighFrameRate(String hfr) { + set(KEY_QC_VIDEO_HIGH_FRAME_RATE, hfr); + } + + /** @hide + * Gets the current Video HDR Mode. + * + * @return Video HDR mode value + */ + public String getVideoHDRMode() { + return get(KEY_QC_VIDEO_HDR); + } + + /** @hide + * Sets the current Video HDR Mode. + * + * @return null + */ + public void setVideoHDRMode(String videohdr) { + set(KEY_QC_VIDEO_HDR, videohdr); + } + + /** @hide + * Gets the current DENOISE setting. + * + * @return one of DENOISE_XXX string constant. null if Denoise + * setting is not supported. + * + */ + public String getDenoise() { + return get(KEY_QC_DENOISE); + } + + /** @hide + * Gets the current Continuous AF setting. + * + * @return one of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public String getContinuousAf() { + return get(KEY_QC_CONTINUOUS_AF); + } + + /** @hide + * Sets the current Denoise mode. + * @param value DENOISE_XXX string constants. + * + */ + + public void setDenoise(String value) { + set(KEY_QC_DENOISE, value); + } + + /** @hide + * Sets the current Continuous AF mode. + * @param value CONTINUOUS_AF_XXX string constants. + * + */ + public void setContinuousAf(String value) { + set(KEY_QC_CONTINUOUS_AF, value); + } + + /** @hide + * Gets the current selectable zone af setting. + * + * @return one of SELECTABLE_ZONE_AF_XXX string constant. null if selectable zone af + * setting is not supported. + */ + public String getSelectableZoneAf() { + return get(KEY_QC_SELECTABLE_ZONE_AF); + } + + /** @hide + * Sets the current selectable zone af setting. + * + * @param value SELECTABLE_ZONE_AF_XXX string constants. + */ + public void setSelectableZoneAf(String value) { + set(KEY_QC_SELECTABLE_ZONE_AF, value); + } + + /** @hide + * Gets the current face detection setting. + * + * @return one of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public String getFaceDetectionMode() { + return get(KEY_QC_FACE_DETECTION); + } + + /** @hide + * Sets the auto scene detect. Other settings like Touch AF/AEC might be + * changed after setting face detection. + * + * @param value FACE_DETECTION_XXX string constants. + * + */ + public void setFaceDetectionMode(String value) { + set(KEY_QC_FACE_DETECTION, value); + } + + /** @hide + * Gets the current video rotation setting. + * + * @return one of VIDEO_QC_ROTATION_XXX string constant. null if video rotation + * setting is not supported. + */ + public String getVideoRotation() { + return get(KEY_QC_VIDEO_ROTATION); + } + + /** @hide + * Sets the current video rotation setting. + * + * @param value VIDEO_QC_ROTATION_XXX string constants. + */ + public void setVideoRotation(String value) { + set(KEY_QC_VIDEO_ROTATION, value); + } + /** @hide + * Gets the supported video rotation modes. + * + * @return a List of VIDEO_QC_ROTATION_XXX string constant. null if this + * setting is not supported. + */ + public List getSupportedVideoRotationValues() { + String str = get(KEY_QC_VIDEO_ROTATION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + // Splits a comma delimited string to an ArrayList of Coordinate. + // Return null if the passing string is null or the Coordinate is 0. + private ArrayList splitCoordinate(String str) { + if (str == null) return null; + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(str); + ArrayList coordinateList = new ArrayList(); + for (String s : splitter) { + Coordinate coordinate = strToCoordinate(s); + if (coordinate != null) coordinateList.add(coordinate); + } + if (coordinateList.size() == 0) return null; + return coordinateList; + } + + // Parses a string (ex: "500x500") to Coordinate object. + // Return null if the passing string is null. + private Coordinate strToCoordinate(String str) { + if (str == null) return null; + + int pos = str.indexOf('x'); + if (pos != -1) { + String x = str.substring(0, pos); + String y = str.substring(pos + 1); + return new Coordinate(Integer.parseInt(x), + Integer.parseInt(y)); + } + Log.e(TAG, "Invalid Coordinate parameter string=" + str); + return null; + } + /* ### QC ADD-ONS: END */ }; } diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index d8ebb628452a9..eaf8ab25198cd 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -319,9 +319,6 @@ static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) { case FINGERPRINT_ACQUIRED_IMMOBILE: case FINGERPRINT_ACQUIRED_TOO_BRIGHT: case FINGERPRINT_ACQUIRED_VENDOR: - // Bad image captured. Turn off HBM. Matcher will not run, so there's no need to - // keep HBM on. - return true; case FINGERPRINT_ACQUIRED_UNKNOWN: default: return false; diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 5b1973ad2dd47..691690c09e0e0 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -1233,42 +1233,6 @@ public void onCaptureStarted(@NonNull CameraCaptureSession session, // default empty implementation } - /** - * This method is called when the camera device has started reading out the output - * image for the request, at the beginning of the sensor image readout. - * - *

For a capture request, this callback is invoked right after - * {@link #onCaptureStarted}. Unlike {@link #onCaptureStarted}, instead of passing - * a timestamp of start of exposure, this callback passes a timestamp of start of - * camera data readout. This is useful because for a camera running at fixed frame - * rate, the start of readout is at fixed interval, which is not necessarily true for - * the start of exposure, particularly when autoexposure is changing exposure duration - * between frames.

- * - *

This timestamp may not match {@link CaptureResult#SENSOR_TIMESTAMP the result - * timestamp field}. It will, however, match the timestamp of buffers sent to the - * output surfaces with {@link OutputConfiguration#TIMESTAMP_BASE_READOUT_SENSOR} - * timestamp base.

- * - *

This callback will be called only if {@link - * CameraCharacteristics#SENSOR_READOUT_TIMESTAMP} is - * {@link CameraMetadata#SENSOR_READOUT_TIMESTAMP_HARDWARE}, and it's called - * right after {@link #onCaptureStarted}.

- * - * @param session the session returned by {@link CameraDevice#createCaptureSession} - * @param request the request for the readout that just began - * @param timestamp the timestamp at start of readout for a regular request, or - * the timestamp at the input image's start of readout for a - * reprocess request, in nanoseconds. - * @param frameNumber the frame number for this capture - * - * @hide - */ - public void onReadoutStarted(@NonNull CameraCaptureSession session, - @NonNull CaptureRequest request, long timestamp, long frameNumber) { - // default empty implementation - } - /** * This method is called when some results from an image capture are * available. diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 861a8502c44d1..c32ad724bb370 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -4418,40 +4418,6 @@ public Set getPhysicalCameraIds() { public static final Key SENSOR_OPTICAL_BLACK_REGIONS = new Key("android.sensor.opticalBlackRegions", android.graphics.Rect[].class); - /** - *

Whether or not the camera device supports readout timestamp and - * onReadoutStarted callback.

- *

If this tag is HARDWARE, the camera device calls onReadoutStarted in addition to the - * onCaptureStarted callback for each capture. The timestamp passed into the callback - * is the start of camera image readout rather than the start of the exposure. In - * addition, the application can configure an - * {@link android.hardware.camera2.params.OutputConfiguration } with - * TIMESTAMP_BASE_READOUT_SENSOR timestamp base, in which case, the timestamp of the - * output surface matches the timestamp from the corresponding onReadoutStarted callback.

- *

The readout timestamp is beneficial for video recording, because the encoder favors - * uniform timestamps, and the readout timestamps better reflect the cadence camera sensors - * output data.

- *

If this tag is HARDWARE, the camera device produces the start-of-exposure and - * start-of-readout together. As a result, the onReadoutStarted is called right after - * onCaptureStarted. The difference in start-of-readout and start-of-exposure is the sensor - * exposure time, plus certain constant offset. The offset is usually due to camera sensor - * level crop, and it remains constant for a given camera sensor mode.

- *

Possible values:

- *
    - *
  • {@link #SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED NOT_SUPPORTED}
  • - *
  • {@link #SENSOR_READOUT_TIMESTAMP_HARDWARE HARDWARE}
  • - *
- * - *

This key is available on all devices.

- * @see #SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED - * @see #SENSOR_READOUT_TIMESTAMP_HARDWARE - * @hide - */ - @PublicKey - @NonNull - public static final Key SENSOR_READOUT_TIMESTAMP = - new Key("android.sensor.readoutTimestamp", int.class); - /** *

List of lens shading modes for {@link CaptureRequest#SHADING_MODE android.shading.mode} that are supported by this camera device.

*

This list contains lens shading modes that can be set for the camera device. diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 0f1b39c01289d..c26b9eae801f2 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -1307,6 +1307,10 @@ public abstract CaptureRequest.Builder createReprocessCaptureRequest( @Override public abstract void close(); + /** @hide */ + public abstract void setVendorStreamConfigMode(int index) + throws CameraAccessException; + /** * Checks whether a particular {@link SessionConfiguration} is supported by the camera device. * diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index d6d3a97687b5c..ac140fad17937 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -23,9 +23,11 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.ActivityThread; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; +import android.hardware.Camera; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; @@ -49,6 +51,7 @@ import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -638,8 +641,16 @@ private Map getPhysicalIdToCharsMap( HashMap physicalIdsToChars = new HashMap(); Set physicalCameraIds = chars.getPhysicalCameraIds(); + boolean sWorkAroundDepthSensor = + SystemProperties.getBoolean("persist.enable_depthsensor_workaround", false); + CameraCharacteristics physicalChars; for (String physicalCameraId : physicalCameraIds) { - CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId); + if (sWorkAroundDepthSensor) { + physicalCameraId = "20"; + physicalChars = getCameraCharacteristics(physicalCameraId); + } else { + physicalChars = getCameraCharacteristics(physicalCameraId); + } physicalIdsToChars.put(physicalCameraId, physicalChars); } return physicalIdsToChars; @@ -1648,8 +1659,10 @@ private void connectCameraServiceLocked() { private String[] extractCameraIdListLocked() { String[] cameraIds = null; + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + int size = exposeAuxCamera ? mDeviceStatus.size() : 2; int idCount = 0; - for (int i = 0; i < mDeviceStatus.size(); i++) { + for (int i = 0; i < size; i++) { int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -1657,7 +1670,7 @@ private String[] extractCameraIdListLocked() { } cameraIds = new String[idCount]; idCount = 0; - for (int i = 0; i < mDeviceStatus.size(); i++) { + for (int i = 0; i < size; i++) { int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -1922,6 +1935,14 @@ public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessEx throw new IllegalArgumentException("cameraId was null"); } + /* Force to expose only two cameras + * if the package name does not falls in this bucket + */ + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + if (exposeAuxCamera == false && (Integer.parseInt(cameraId) >= 2)) { + throw new IllegalArgumentException("invalid cameraId"); + } + ICameraService cameraService = getCameraService(); if (cameraService == null) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, @@ -2189,6 +2210,11 @@ private void updateCallbackLocked(AvailabilityCallback callback, Executor execut } private void onStatusChangedLocked(int status, String id) { + if (!Camera.shouldExposeAuxCamera() && Integer.parseInt(id) >= 2) { + Log.w(TAG, "[soar.cts] ignore the status update of camera: " + id); + return; + } + if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x", id, status)); @@ -2320,6 +2346,16 @@ private void onTorchStatusChangedLocked(int status, String id) { String.format("Camera id %s has torch status changed to 0x%x", id, status)); } + /* Force to ignore the aux or composite camera torch status update + * if the package name does not falls in this bucket + */ + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + if (exposeAuxCamera == false && Integer.parseInt(id) >= 2) { + Log.w(TAG, "ignore the torch status update of camera: " + id); + return; + } + + if (!validTorchStatus(status)) { Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, status)); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index c67a560b58857..eb8c73aced393 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1657,28 +1657,6 @@ private static boolean shouldKeyBeAdded(TKey key, Field field, int[] filt */ public static final int SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN = 24; - // - // Enumeration values for CameraCharacteristics#SENSOR_READOUT_TIMESTAMP - // - - /** - *

This camera device doesn't support readout timestamp and onReadoutStarted - * callback.

- * @see CameraCharacteristics#SENSOR_READOUT_TIMESTAMP - * @hide - */ - public static final int SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED = 0; - - /** - *

This camera device supports the onReadoutStarted callback as well as outputting - * readout timestamp for streams with TIMESTAMP_BASE_READOUT_SENSOR timestamp base. The - * readout timestamp is generated by the camera hardware and it has the same accuracy - * and timing characteristics of the start-of-exposure time.

- * @see CameraCharacteristics#SENSOR_READOUT_TIMESTAMP - * @hide - */ - public static final int SENSOR_READOUT_TIMESTAMP_HARDWARE = 1; - // // Enumeration values for CameraCharacteristics#LED_AVAILABLE_LEDS // diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index b9eba9c1d5415..c5bb3e2ec80c9 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -15,6 +15,7 @@ */ package android.hardware.camera2.impl; +import android.app.ActivityThread; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; @@ -28,6 +29,7 @@ import android.os.Binder; import android.os.Handler; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; @@ -131,6 +133,18 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession Log.e(TAG, mIdString + "Failed to create capture session; configuration failed"); mConfigureSuccess = false; } + + setSkipUnconfigure(); + } + + private void setSkipUnconfigure() { + String packageName = ActivityThread.currentOpPackageName(); + List packageList = Arrays.asList(SystemProperties.get( + "vendor.camera.skip_unconfigure.packagelist", packageName).split(",")); + + if (packageList.contains(packageName)) { + mSkipUnconfigure = true; + } } @Override @@ -222,7 +236,8 @@ private void checkCaptureRequest(CaptureRequest request) { } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { + } else if (!mDeviceImpl.isPrivilegedApp() && + request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } } @@ -656,21 +671,6 @@ public void onCaptureStarted(CameraDevice camera, } } - @Override - public void onReadoutStarted(CameraDevice camera, - CaptureRequest request, long timestamp, long frameNumber) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onReadoutStarted( - CameraCaptureSessionImpl.this, request, timestamp, - frameNumber)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - @Override public void onCapturePartial(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureResult result) { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index a6c79b3a289f8..38dad790affb9 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -16,10 +16,13 @@ package android.hardware.camera2.impl; +import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; + import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; import android.annotation.NonNull; import android.content.Context; +import android.app.ActivityThread; import android.graphics.ImageFormat; import android.hardware.ICameraService; import android.hardware.camera2.CameraAccessException; @@ -53,6 +56,8 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.util.Range; import android.util.Size; @@ -84,7 +89,7 @@ public class CameraDeviceImpl extends CameraDevice private final boolean DEBUG = false; private static final int REQUEST_ID_NONE = -1; - + private int customOpMode = 0; // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) private ICameraDeviceUserWrapper mRemoteDevice; @@ -130,6 +135,8 @@ public class CameraDeviceImpl extends CameraDevice private final int mTotalPartialCount; private final Context mContext; + private final boolean mForceMultiResolution; + private static final long NANO_PER_SECOND = 1000000000; //ns /** @@ -151,6 +158,7 @@ public class CameraDeviceImpl extends CameraDevice private int mNextSessionId = 0; private final int mAppTargetSdkVersion; + private boolean mIsPrivilegedApp = false; private ExecutorService mOfflineSwitchService; private CameraOfflineSessionImpl mOfflineSessionImpl; @@ -301,6 +309,10 @@ public CameraDeviceImpl(String cameraId, StateCallback callback, Executor execut } else { mTotalPartialCount = partialCount; } + mIsPrivilegedApp = checkPrivilegedAppList(); + + mForceMultiResolution = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_forceMultiResolution); } public CameraDeviceCallbacks getCallbacks() { @@ -389,6 +401,10 @@ public void run() { } } + public void setVendorStreamConfigMode(int fpsrange) { + customOpMode = fpsrange; + } + @Override public String getId() { return mCameraId; @@ -507,6 +523,7 @@ public boolean configureStreamsChecked(InputConfiguration inputConfig, mConfiguredOutputs.put(streamId, outConfig); } } + operatingMode = (operatingMode | (customOpMode << 16)); int offlineStreamIds[]; if (sessionParams != null) { @@ -1503,12 +1520,33 @@ private boolean checkInputConfigurationWithStreamConfigurations( return false; } + private boolean checkPrivilegedAppList() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } + + public boolean isPrivilegedApp() { + return mIsPrivilegedApp; + } + private void checkInputConfiguration(InputConfiguration inputConfig) { if (inputConfig == null) { return; } int inputFormat = inputConfig.getFormat(); - if (inputConfig.isMultiResolution()) { + if (inputConfig.isMultiResolution() || mForceMultiResolution) { MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get( CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP); @@ -1540,6 +1578,14 @@ private void checkInputConfiguration(InputConfiguration inputConfig) { inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid"); } } else { + /* + * don't check input format and size, + * if the package name is in the white list + */ + if (isPrivilegedApp()) { + Log.w(TAG, "ignore input format/size check for white listed app"); + return; + } if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) && !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) { throw new IllegalArgumentException("Input config with format " + @@ -2025,16 +2071,12 @@ public void onCaptureStarted(final CaptureResultExtras resultExtras, final long resultExtras.getLastCompletedReprocessFrameNumber(); final long lastCompletedZslFrameNumber = resultExtras.getLastCompletedZslFrameNumber(); - final boolean hasReadoutTimestamp = resultExtras.hasReadoutTimestamp(); - final long readoutTimestamp = resultExtras.getReadoutTimestamp(); if (DEBUG) { Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber + ": completedRegularFrameNumber " + lastCompletedRegularFrameNumber + ", completedReprocessFrameNUmber " + lastCompletedReprocessFrameNumber - + ", completedZslFrameNumber " + lastCompletedZslFrameNumber - + ", hasReadoutTimestamp " + hasReadoutTimestamp - + (hasReadoutTimestamp ? ", readoutTimestamp " + readoutTimestamp : "")) ; + + ", completedZslFrameNumber " + lastCompletedZslFrameNumber); } final CaptureCallbackHolder holder; @@ -2086,26 +2128,14 @@ public void run() { CameraDeviceImpl.this, holder.getRequest(i), timestamp - (subsequenceId - i) * - NANO_PER_SECOND / fpsRange.getUpper(), + NANO_PER_SECOND/fpsRange.getUpper(), frameNumber - (subsequenceId - i)); - if (hasReadoutTimestamp) { - holder.getCallback().onReadoutStarted( - CameraDeviceImpl.this, - holder.getRequest(i), - readoutTimestamp - (subsequenceId - i) * - NANO_PER_SECOND / fpsRange.getUpper(), - frameNumber - (subsequenceId - i)); - } } } else { holder.getCallback().onCaptureStarted( - CameraDeviceImpl.this, request, + CameraDeviceImpl.this, + holder.getRequest(resultExtras.getSubsequenceId()), timestamp, frameNumber); - if (hasReadoutTimestamp) { - holder.getCallback().onReadoutStarted( - CameraDeviceImpl.this, request, - readoutTimestamp, frameNumber); - } } } } diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 468e6041eb73c..c4387a325b9c7 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -588,31 +588,35 @@ private T getBase(CaptureRequest.Key key) { } private T getBase(Key key) { - int tag; - if (key.hasTag()) { - tag = key.getTag(); - } else { - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); - key.cacheTag(tag); - } - byte[] values = readValues(tag); - if (values == null) { - // If the key returns null, use the fallback key if exists. - // This is to support old key names for the newly published keys. - if (key.mFallbackName == null) { - return null; + try { + int tag; + if (key.hasTag()) { + tag = key.getTag(); + } else { + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); + key.cacheTag(tag); } - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); - values = readValues(tag); + byte[] values = readValues(tag); if (values == null) { - return null; + // If the key returns null, use the fallback key if exists. + // This is to support old key names for the newly published keys. + if (key.mFallbackName == null) { + return null; + } + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); + values = readValues(tag); + if (values == null) { + return null; + } } - } - int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); - Marshaler marshaler = getMarshalerForKey(key, nativeType); - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return marshaler.unmarshal(buffer); + int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + Marshaler marshaler = getMarshalerForKey(key, nativeType); + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + return marshaler.unmarshal(buffer); + } catch (Exception e) { + return null; + } } // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden @@ -2081,6 +2085,18 @@ private static native int nativeGetTagFromKey(String keyName, long vendorId) private static native int nativeGetTypeFromTag(int tag, long vendorId) throws IllegalArgumentException; + private synchronized byte[] nativeReadValues(int tag) { + return nativeReadValues(tag, mMetadataPtr); + } + + private synchronized int nativeGetTypeFromTagLocal(int tag) { + return nativeGetTypeFromTagLocal(mMetadataPtr, tag); + } + + private synchronized int nativeGetTagFromKeyLocal(String keyname) { + return nativeGetTagFromKeyLocal(mMetadataPtr, keyname); + } + /** *

Perform a 0-copy swap of the internal metadata with another object.

* diff --git a/core/java/android/hardware/camera2/impl/CaptureCallback.java b/core/java/android/hardware/camera2/impl/CaptureCallback.java index b064e6a1f9752..6defe63b1766a 100644 --- a/core/java/android/hardware/camera2/impl/CaptureCallback.java +++ b/core/java/android/hardware/camera2/impl/CaptureCallback.java @@ -65,13 +65,6 @@ public CameraCaptureSession.CaptureCallback getSessionCallback() { public abstract void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber); - /** - * This method is called when the camera device has started reading out the output - * image for the request, at the beginning of the sensor image readout. - */ - public abstract void onReadoutStarted(CameraDevice camera, - CaptureRequest request, long timestamp, long frameNumber); - /** * This method is called when some results from an image capture are * available. diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java index 8bf94986a4905..5d9da73fd5c06 100644 --- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java +++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java @@ -33,8 +33,6 @@ public class CaptureResultExtras implements Parcelable { private long lastCompletedRegularFrameNumber; private long lastCompletedReprocessFrameNumber; private long lastCompletedZslFrameNumber; - private boolean hasReadoutTimestamp; - private long readoutTimestamp; public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -58,8 +56,7 @@ public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId, int partialResultCount, int errorStreamId, String errorPhysicalCameraId, long lastCompletedRegularFrameNumber, long lastCompletedReprocessFrameNumber, - long lastCompletedZslFrameNumber, boolean hasReadoutTimestamp, - long readoutTimestamp) { + long lastCompletedZslFrameNumber) { this.requestId = requestId; this.subsequenceId = subsequenceId; this.afTriggerId = afTriggerId; @@ -71,8 +68,6 @@ public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId, this.lastCompletedRegularFrameNumber = lastCompletedRegularFrameNumber; this.lastCompletedReprocessFrameNumber = lastCompletedReprocessFrameNumber; this.lastCompletedZslFrameNumber = lastCompletedZslFrameNumber; - this.hasReadoutTimestamp = hasReadoutTimestamp; - this.readoutTimestamp = readoutTimestamp; } @Override @@ -98,10 +93,6 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(lastCompletedRegularFrameNumber); dest.writeLong(lastCompletedReprocessFrameNumber); dest.writeLong(lastCompletedZslFrameNumber); - dest.writeBoolean(hasReadoutTimestamp); - if (hasReadoutTimestamp) { - dest.writeLong(readoutTimestamp); - } } public void readFromParcel(Parcel in) { @@ -119,10 +110,6 @@ public void readFromParcel(Parcel in) { lastCompletedRegularFrameNumber = in.readLong(); lastCompletedReprocessFrameNumber = in.readLong(); lastCompletedZslFrameNumber = in.readLong(); - hasReadoutTimestamp = in.readBoolean(); - if (hasReadoutTimestamp) { - readoutTimestamp = in.readLong(); - } } public String getErrorPhysicalCameraId() { @@ -168,12 +155,4 @@ public long getLastCompletedReprocessFrameNumber() { public long getLastCompletedZslFrameNumber() { return lastCompletedZslFrameNumber; } - - public boolean hasReadoutTimestamp() { - return hasReadoutTimestamp; - } - - public long getReadoutTimestamp() { - return readoutTimestamp; - } } diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 9e87037798635..4a2517763aaec 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -245,26 +245,6 @@ public final class OutputConfiguration implements Parcelable { */ public static final int TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4; - /** - * Timestamp is the start of readout in the same time domain as TIMESTAMP_BASE_SENSOR. - * - *

The start of the camera sensor readout after exposure. For a rolling shutter camera - * sensor, the timestamp is typically equal to the start of exposure time + - * exposure time + certain fixed offset. The fixed offset could be due to camera sensor - * level crop. The benefit of using readout time is that when camera runs in a fixed - * frame rate, the timestamp intervals between frames are constant.

- * - *

This timestamp is in the same time domain as in TIMESTAMP_BASE_SENSOR, with the exception - * that one is start of exposure, and the other is start of readout.

- * - *

This timestamp base is supported only if {@link - * CameraCharacteristics#SENSOR_READOUT_TIMESTAMP} is - * {@link CameraMetadata#SENSOR_READOUT_TIMESTAMP_HARDWARE}.

- * - * @hide - */ - public static final int TIMESTAMP_BASE_READOUT_SENSOR = 5; - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"TIMESTAMP_BASE_"}, value = @@ -272,8 +252,7 @@ public final class OutputConfiguration implements Parcelable { TIMESTAMP_BASE_SENSOR, TIMESTAMP_BASE_MONOTONIC, TIMESTAMP_BASE_REALTIME, - TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED, - TIMESTAMP_BASE_READOUT_SENSOR}) + TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}) public @interface TimestampBase {}; /** @hide */ @@ -995,7 +974,7 @@ public long getStreamUseCase() { public void setTimestampBase(@TimestampBase int timestampBase) { // Verify that the value is in range if (timestampBase < TIMESTAMP_BASE_DEFAULT || - timestampBase > TIMESTAMP_BASE_READOUT_SENSOR) { + timestampBase > TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED) { throw new IllegalArgumentException("Not a valid timestamp base value " + timestampBase); } diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index 6c83057fdf297..10fc4e641bfd7 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -34,6 +34,11 @@ import java.util.Iterator; import java.util.List; +import android.app.ActivityThread; +import android.os.SystemProperties; +import android.text.TextUtils; + + /** * Various Surface utilities. */ @@ -241,7 +246,13 @@ public static void checkConstrainedHighSpeedSurfaces(Collection surface + " the size must be 1 or 2"); } + if (isPrivilegedApp()) { + //skip checks for privileged apps + return; + } + List highSpeedSizes = null; + if (fpsRange == null) { highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes()); } else { @@ -303,4 +314,21 @@ private static native int nativeDetectSurfaceDimens(Surface surface, /*out*/int[/*2*/] dimens); private static native long nativeGetSurfaceId(Surface surface); + + private static boolean isPrivilegedApp() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } } diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 7d8f2ff92200a..e17fd6e9e42f8 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -18,6 +18,9 @@ import android.annotation.TestApi; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; import android.os.Build; import android.os.SystemProperties; import android.provider.Settings; @@ -37,7 +40,8 @@ */ @TestApi public class AmbientDisplayConfiguration { - private static final String TAG = "AmbientDisplayConfig"; + private static final IntentFilter sIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + private final Context mContext; private final boolean mAlwaysOnByDefault; @@ -50,7 +54,8 @@ public class AmbientDisplayConfiguration { Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, - Settings.Secure.DOZE_TAP_SCREEN_GESTURE + Settings.Secure.DOZE_TAP_SCREEN_GESTURE, + Settings.Secure.DOZE_ON_CHARGE }; /** Non-user configurable doze settings */ @@ -71,6 +76,7 @@ public AmbientDisplayConfiguration(Context context) { public boolean enabled(int user) { return pulseOnNotificationEnabled(user) || pulseOnLongPressEnabled(user) + || pulseOnCustomDozeEventEnabled(user) || alwaysOnEnabled(user) || wakeLockScreenGestureEnabled(user) || wakeDisplayGestureEnabled(user) @@ -92,6 +98,12 @@ public boolean pulseOnNotificationAvailable() { return ambientDisplayAvailable(); } + /** @hide */ + public boolean pulseOnCustomDozeEventEnabled(int user) { + return (Settings.System.getInt(mContext.getContentResolver(), Settings.System.DOZE_TRIGGER_DOUBLETAP, 0) != 0) + && pulseOnNotificationAvailable(); + } + /** @hide */ public boolean pickupGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_PICK_UP_GESTURE, user) @@ -141,7 +153,8 @@ && pickupGestureEnabled(user) /** @hide */ public boolean screenOffUdfpsEnabled(int user) { return !TextUtils.isEmpty(udfpsLongPressSensorType()) - && boolSettingDefaultOff("screen_off_udfps_enabled", user); + && boolSettingDefaultOff(Settings.Secure.SCREEN_OFF_UDFPS_ENABLED, user) + && mContext.getResources().getBoolean(R.bool.config_supportsScreenOffUdfps); } /** @hide */ @@ -218,8 +231,29 @@ private boolean pulseOnLongPressAvailable() { */ @TestApi public boolean alwaysOnEnabled(int user) { - return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0) - && alwaysOnAvailable() && !accessibilityInversionEnabled(user); + return alwaysOnEnabledSetting(user) || alwaysOnChargingEnabled(user); + } + + public boolean alwaysOnEnabledSetting(int user) { + final boolean alwaysOnEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON, + mAlwaysOnByDefault ? 1 : 0, user) == 1; + return alwaysOnEnabled && alwaysOnAvailable() && !accessibilityInversionEnabled(user); + } + + public boolean alwaysOnChargingEnabledSetting(int user) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.DOZE_ON_CHARGE, 0, user) == 1; + } + + private boolean alwaysOnChargingEnabled(int user) { + if (alwaysOnChargingEnabledSetting(user)) { + final Intent intent = mContext.registerReceiver(null, sIntentFilter); + if (intent != null) { + return intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; + } + } + return false; } /** diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 28f1f02b7a93e..171c824f15e5c 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1040,7 +1040,7 @@ public boolean isHardwareDetected() { throw e.rethrowFromSystemServer(); } } else { - Slog.w(TAG, "isFingerprintHardwareDetected(): Service not connected!"); + if (DEBUG) Slog.w(TAG, "isFingerprintHardwareDetected(): Service not connected!"); } return false; } @@ -1069,7 +1069,10 @@ public List getSensorPropertiesInternal() { */ public boolean isPowerbuttonFps() { final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); - return sensorProps.sensorType == TYPE_POWER_BUTTON; + if (sensorProps != null) { + return sensorProps.sensorType == TYPE_POWER_BUTTON; + } + return false; } /** diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index dbb8e40f3a717..8adda2f88c8a0 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -31,7 +31,7 @@ oneway interface IUdfpsOverlayController { // Check acquiredInfo for the acquired type (BiometricFingerprintConstants#FingerprintAcquired). // Check BiometricFingerprintConstants#shouldTurnOffHbm for whether the acquiredInfo // should turn off HBM. - void onAcquired(int sensorId, int acquiredInfo); + void onAcquired(int sensorId, int acquiredInfo, int vendorCode); // Notifies of enrollment progress changes. void onEnrollmentProgress(int sensorId, int remaining); diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java index d06bc1d2053da..3e41d63b0365d 100644 --- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java +++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java @@ -122,6 +122,9 @@ public void sendStandby() { } private IHdmiControlCallback getCallbackWrapper(final OneTouchPlayCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("OneTouchPlayCallback cannot be null."); + } return new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -131,6 +134,9 @@ public void onComplete(int result) { } private IHdmiControlCallback getCallbackWrapper(final DisplayStatusCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("DisplayStatusCallback cannot be null."); + } return new IHdmiControlCallback.Stub() { @Override public void onComplete(int status) { diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index d525753a1874f..2d2882dc56e79 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -490,8 +490,12 @@ private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) { public String toString() { StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType) .append(", primary=").append(mPrimaryId); - if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds); - if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds); + if (mSecondaryIds.length > 0) { + sb.append(", secondary=").append(Arrays.toString(mSecondaryIds)); + } + if (mVendorIds.length > 0) { + sb.append(", vendor=").append(Arrays.toString(mVendorIds)); + } sb.append(")"); return sb.toString(); } diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 4cc001a40146f..83589ea92f426 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -505,7 +505,8 @@ public String toString() { public int hashCode() { return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion, mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired, - mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo); + mIsCaptureSupported, Arrays.hashCode(mBands), mIsBgScanSupported, + mDabFrequencyTable, mVendorInfo); } @Override @@ -525,7 +526,7 @@ public boolean equals(@Nullable Object obj) { if (mNumAudioSources != other.mNumAudioSources) return false; if (mIsInitializationRequired != other.mIsInitializationRequired) return false; if (mIsCaptureSupported != other.mIsCaptureSupported) return false; - if (!Objects.equals(mBands, other.mBands)) return false; + if (!Arrays.equals(mBands, other.mBands)) return false; if (mIsBgScanSupported != other.mIsBgScanSupported) return false; if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false; if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index a9d665c8b8a5a..621eab558337f 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -1963,7 +1963,6 @@ public String toString() { } private static Object mServiceLock = new Object(); - private static ISoundTriggerMiddlewareService mService; /** * Translate an exception thrown from interaction with the underlying service to an error code. @@ -2217,20 +2216,12 @@ private static ISoundTriggerMiddlewareService getService() { binder = ServiceManager.getServiceOrThrow( Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE); - binder.linkToDeath(() -> { - synchronized (mServiceLock) { - mService = null; - } - }, 0); - mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder); - break; + return ISoundTriggerMiddlewareService.Stub.asInterface(binder); } catch (Exception e) { Log.e(TAG, "Failed to bind to soundtrigger service", e); } } - return mService; } - } /** diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 3f49b73a78fcb..8a82cff02548f 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -381,6 +381,10 @@ public void createSession(InputChannel channel, IInputSessionCallback callback) @BinderThread @Override public void setSessionEnabled(IInputMethodSession session, boolean enabled) { + if (session == null) { + Log.w(TAG, "Incoming session is null"); + return; + } try { InputMethodSession ls = ((IInputMethodSessionWrapper) session).getInternalInputMethodSession(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 8e67705c5cf07..e4f4179a9419d 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -88,6 +88,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.os.UserHandle; import android.provider.Settings; import android.text.InputType; import android.text.Layout; @@ -488,6 +489,17 @@ public static boolean canImeRenderGesturalNavButtons() { private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING; + /** + * Volume key cursor control + */ + int mVolumeKeyCursorControl = 0; + /** @hide */ + public static final int VOLUME_CURSOR_OFF = 0; + /** @hide */ + public static final int VOLUME_CURSOR_ON = 1; + /** @hide */ + public static final int VOLUME_CURSOR_ON_REVERSE = 2; + /** * Timeout after which hidden IME surface will be removed from memory * TODO(b/230762351): reset timeout to 5000ms and invalidate cache when IME insets change. @@ -3048,6 +3060,28 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { } return false; } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { + mVolumeKeyCursorControl = Settings.System.getIntForUser(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0, + UserHandle.USER_CURRENT_OR_SELF); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE) + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT); + return true; + } + return false; + } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + mVolumeKeyCursorControl = Settings.System.getIntForUser(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0, + UserHandle.USER_CURRENT_OR_SELF); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE) + ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT); + return true; + } + return false; + } return doMovementKey(keyCode, event, MOVEMENT_DOWN); } @@ -3098,6 +3132,16 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { return handleBack(true); } } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + mVolumeKeyCursorControl = Settings.System.getIntForUser(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0, + UserHandle.USER_CURRENT_OR_SELF); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + return true; + } + return false; + } return doMovementKey(keyCode, event, MOVEMENT_UP); } diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java index a19eb5668d551..8644d9103dcb9 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -23,6 +23,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import java.util.Arrays; /** @@ -395,7 +396,7 @@ public Object[] serialize() { out[i * 2] = entries.keyAt(i); out[i * 2 + 1] = entries.valueAt(i); } - int size = out.toString().getBytes().length; + int size = Arrays.toString(out).getBytes().length; if (size > MAX_SERIALIZED_SIZE) { Log.i(TAG, "Log line too long, did not emit: " + size + " bytes."); throw new RuntimeException(); diff --git a/core/java/android/net/IVpnManager.aidl b/core/java/android/net/IVpnManager.aidl index f30237853a3ea..e3289133b8aa6 100644 --- a/core/java/android/net/IVpnManager.aidl +++ b/core/java/android/net/IVpnManager.aidl @@ -63,4 +63,6 @@ interface IVpnManager { /** General system APIs */ VpnConfig getVpnConfig(int userId); void factoryReset(); + + VpnProfile[] getAllLegacyVpns(); } diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 2e64a74a5d672..2c7c8e09bdf1a 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -717,4 +717,17 @@ public boolean updateLockdownVpn() { throw e.rethrowFromSystemServer(); } } + + + /** + * @hide + */ + public VpnProfile[] getAllLegacyVpns() { + try { + return mService.getAllLegacyVpns(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index cb9a3e43db817..37ba09b381eaf 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +35,7 @@ import android.nfc.INfcUnlockHandler; import android.nfc.ITagRemovedCallback; import android.nfc.INfcDta; import android.os.Bundle; +import android.os.IBinder; /** * @hide @@ -43,6 +47,8 @@ interface INfcAdapter INfcFCardEmulation getNfcFCardEmulationInterface(); INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg); INfcDta getNfcDtaInterface(in String pkg); + IBinder getNfcAdapterVendorInterface(in String vendor); + int getState(); boolean disable(boolean saveState); boolean enable(); diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2436e57b74bc1..d4c966be04b85 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,7 +42,7 @@ * * @hide */ -public final class AidGroup implements Parcelable { +public class AidGroup implements Parcelable { /** * The maximum number of AIDs that can be present in any one group. */ @@ -48,11 +51,11 @@ public final class AidGroup implements Parcelable { static final String TAG = "AidGroup"; @UnsupportedAppUsage - final List aids; + protected List aids; @UnsupportedAppUsage - final String category; + protected String category; @UnsupportedAppUsage - final String description; + protected String description; /** * Creates a new AidGroup object. diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 09540132fe0d4..b85fea437b246 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,24 +52,24 @@ /** * @hide */ -public final class ApduServiceInfo implements Parcelable { +public class ApduServiceInfo implements Parcelable { static final String TAG = "ApduServiceInfo"; /** * The service that implements this */ @UnsupportedAppUsage - final ResolveInfo mService; + protected ResolveInfo mService; /** * Description of the service */ - final String mDescription; + protected String mDescription; /** * Whether this service represents AIDs running on the host CPU */ - final boolean mOnHost; + protected boolean mOnHost; /** * Offhost reader name. @@ -84,18 +87,18 @@ public final class ApduServiceInfo implements Parcelable { * Mapping from category to static AID group */ @UnsupportedAppUsage - final HashMap mStaticAidGroups; + protected HashMap mStaticAidGroups; /** * Mapping from category to dynamic AID group */ @UnsupportedAppUsage - final HashMap mDynamicAidGroups; + protected HashMap mDynamicAidGroups; /** * Whether this service should only be started when the device is unlocked. */ - final boolean mRequiresDeviceUnlock; + protected boolean mRequiresDeviceUnlock; /** * Whether this service should only be started when the device is screen on. @@ -105,17 +108,17 @@ public final class ApduServiceInfo implements Parcelable { /** * The id of the service banner specified in XML. */ - final int mBannerResourceId; + protected int mBannerResourceId; /** * The uid of the package the service belongs to */ - final int mUid; + protected int mUid; /** * Settings Activity for this service */ - final String mSettingsActivityName; + protected String mSettingsActivityName; /** * @hide diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java index 080e058737b6c..9cae043c4bddf 100644 --- a/core/java/android/nfc/tech/MifareClassic.java +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2018 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -173,6 +175,10 @@ public MifareClassic(Tag tag) throws RemoteException { mType = TYPE_CLASSIC; mSize = SIZE_4K; break; + case 0x19: + mType = TYPE_CLASSIC; + mSize = SIZE_2K; + break; case 0x28: mType = TYPE_CLASSIC; mSize = SIZE_1K; diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java index 88730f9af3dff..1d0a5ed183b46 100644 --- a/core/java/android/nfc/tech/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2018 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,8 +68,15 @@ public static NfcA get(Tag tag) { /** @hide */ public NfcA(Tag tag) throws RemoteException { super(tag, TagTechnology.NFC_A); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); - mSak = extras.getShort(EXTRA_SAK); + Bundle extras; + mSak = 0; + if (tag.hasTech(TagTechnology.MIFARE_CLASSIC)) { + extras = tag.getTechExtras(TagTechnology.MIFARE_CLASSIC); + if (extras != null) + mSak = extras.getShort(EXTRA_SAK); + } + extras = tag.getTechExtras(TagTechnology.NFC_A); + mSak |= extras.getShort(EXTRA_SAK); mAtqa = extras.getByteArray(EXTRA_ATQA); } diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 0418a4bb9f806..67065ae22d941 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -438,8 +438,11 @@ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean r map.ensureCapacity(count); } try { + // recycleParcel being false implies that we do not own the parcel. In this case, do + // not use lazy values to be safe, as the parcel could be recycled outside of our + // control. recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative, - /* lazy */ true, mClassLoader); + /* lazy */ recycleParcel, mClassLoader); } catch (BadParcelableException e) { if (sShouldDefuse) { Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e); @@ -447,7 +450,14 @@ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean r } else { throw e; } - } finally { + } catch (RuntimeException e) { + if (sShouldDefuse && (e.getCause() instanceof ClassNotFoundException)) { + Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e); + map.erase(); + } else { + throw e; + } + }finally { mMap = map; if (recycleParcel) { recycleParcel(parcelledData); @@ -1845,7 +1855,6 @@ private void readFromParcelInner(Parcel parcel, int length) { // bundle immediately; neither of which is obvious. synchronized (this) { initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle); - unparcel(/* itemwise */ true); } return; } diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 76f857bd91b76..36f9aa510a74c 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -163,6 +163,40 @@ public class BatteryManager { @SystemApi public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP"; + /** + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_DASH_CHARGER = "dash_charger"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_WARP_CHARGER = "warp_charger"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_VOOC_CHARGER = "vooc_charger"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_TURBO_POWER = "turbo_power"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to indicate OEM fast charging + * {@hide} + */ + public static final String EXTRA_OEM_FAST_CHARGER = "oem_fast_charger"; + // values for "status" field in the ACTION_BATTERY_CHANGED Intent public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN; public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index edfcb3d6f12a7..0621ebc9d9426 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1299,11 +1299,13 @@ public static boolean createDir(File dir) { public static long roundStorageSize(long size) { long val = 1; long pow = 1; - while ((val * pow) < size) { + long pow1024 = 1; + while ((val * pow1024) < size) { val <<= 1; if (val > 512) { val = 1; pow *= 1000; + pow1024 *= 1024; } } return val * pow; diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 4fe6524dee271..73d025b6bf56d 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -76,6 +76,7 @@ interface IPowerManager @UnsupportedAppUsage void reboot(boolean confirm, String reason, boolean wait); void rebootSafeMode(boolean confirm, boolean wait); + void advancedReboot(boolean confirm, String reason, boolean wait); void shutdown(boolean confirm, String reason, boolean wait); void crash(String message); int getLastShutdownReason(); @@ -134,4 +135,8 @@ interface IPowerManager const int GO_TO_SLEEP_REASON_MAX = 10; const int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; + // Lineage custom API + void wakeUpWithProximityCheck(long time, int reason, String details, String opPackageName); + + void setKeyboardVisibility(boolean visible); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 2664f05a9664a..6c6bc682626dd 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2000,7 +2000,20 @@ public final void writeTypedSparseArray(@Nullable SparseA } /** - * @hide + * Flatten a List containing a particular object type into the parcel, at + * the current dataPosition() and growing dataCapacity() if needed. The + * type of the objects in the list must be one that implements Parcelable. + * Unlike the generic writeList() method, however, only the raw data of the + * objects is written and not their type, so you must use the corresponding + * readTypedList() to unmarshall them. + * + * @param val The list of objects to be written. + * @param parcelableFlags Contextual flags as per + * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. + * + * @see #createTypedArrayList + * @see #readTypedList + * @see Parcelable */ public void writeTypedList(@Nullable List val, int parcelableFlags) { if (val == null) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 13ca2c34b27ec..31c7590a4bd57 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -364,6 +364,15 @@ public final class PowerManager { @SystemApi public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1; + /** + * @hide + * User activity flag: Certain hardware buttons are not supposed to + * activate hardware button illumination. This flag indicates a + * button event from one of those buttons. + * @hide + */ + public static final int USER_ACTIVITY_FLAG_NO_BUTTON_LIGHTS = 1 << 2; + /** * @hide */ @@ -500,7 +509,9 @@ public static String sleepReasonToString(@GoToSleepReason int sleepReason) { BRIGHTNESS_CONSTRAINT_TYPE_DOZE, BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR, BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR, - BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_BUTTON, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_KEYBOARD }) @Retention(RetentionPolicy.SOURCE) public @interface BrightnessConstraint{} @@ -552,6 +563,18 @@ public static String sleepReasonToString(@GoToSleepReason int sleepReason) { */ public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR = 7; + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_BUTTON = 8; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_KEYBOARD = 9; + /** * @hide */ @@ -779,6 +802,16 @@ public int hashCode() { */ public static final String REBOOT_RECOVERY = "recovery"; + /** + * The value to pass as the 'reason' argument to reboot() to reboot into + * bootloader mode if you need to get to the choppa (cit) + *

+ * Requires {@link android.Manifest.permission#REBOOT}). + *

+ * @hide + */ + public static final String REBOOT_BOOTLOADER = "bootloader"; + /** * The value to pass as the 'reason' argument to reboot() to reboot into * recovery mode for applying system updates. @@ -1579,6 +1612,22 @@ public void wakeUp(long time, @WakeReason int reason, String details) { } } + /** + * Forces the device to wake up from sleep only if + * nothing is blocking the proximity sensor + * + * @see #wakeUp + * + * @hide + */ + public void wakeUpWithProximityCheck(long time, @WakeReason int reason, String details) { + try { + mService.wakeUpWithProximityCheck(time, reason, details, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Forces the device to start napping. *

@@ -1798,6 +1847,24 @@ public void rebootSafeMode() { } } + /** + * Reboot the device with custom progress meassges. + * Will not return if the reboot is successful. + *

+ * Requires the {@link android.Manifest.permission#REBOOT} permission. + *

+ * + * @param reason code to pass to the kernel (e.g., "recovery") to + * request special boot modes, or null. + * @hide + */ + public void advancedReboot(String reason) { + try { + mService.advancedReboot(false, reason, true); + } catch (RemoteException e) { + } + } + /** * Returns true if the device is currently in power save mode. When in this mode, * applications should reduce their functionality in order to conserve battery as @@ -2549,6 +2616,18 @@ public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds } } + /** + * @hide + */ + public void setKeyboardVisibility(boolean visible) { + try { + if (mService != null) { + mService.setKeyboardVisibility(visible); + } + } catch (RemoteException e) { + } + } + /** * If true, the doze component is not started until after the screen has been * turned off and the screen off animation has been performed. diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 5ca0da2d3f975..a63b0db5c85dc 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -97,6 +97,16 @@ public static boolean isInteractive(int wakefulness) { return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING; } + /** + * Used by the window manager to override the button brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or Float.NaN to disable the override. + */ + public abstract void setButtonBrightnessOverrideFromWindowManager(float brightness); + /** * Used by the window manager to override the screen brightness based on the * current foreground activity. diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e06e7326a8603..b1e124c4a7579 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1481,10 +1481,10 @@ public static final class ProcessStartResult { * @return true if this thread belongs to a process * @hide */ - public static final boolean isThreadInProcess(int tid, int pid) { + public static final boolean isThreadInProcess(int pid, int tid) { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { - if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) { + if (Os.access("/proc/" + pid + "/task/" + tid, OsConstants.F_OK)) { return true; } else { return false; diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index a3b836adfc8b7..4ef1226a8933d 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -514,7 +514,7 @@ public static void processPackage(Context context, final Handler handler) throws IOException { String filename = packageFile.getCanonicalPath(); - if (!filename.startsWith("/data/")) { + if (!filename.startsWith("/data/") || !SystemProperties.get("persist.sys.recovery_update", "").equals("true")) { return; } @@ -629,7 +629,7 @@ public static void installPackage(Context context, File packageFile, boolean pro // If the package is on the /data partition, the package needs to // be processed (i.e. uncrypt'd). The caller specifies if that has // been done in 'processed' parameter. - if (filename.startsWith("/data/")) { + if (SystemProperties.get("persist.sys.recovery_update", "").equals("true") && filename.startsWith("/data/")) { if (processed) { if (!BLOCK_MAP_FILE.exists()) { Log.e(TAG, "Package claimed to have been processed but failed to find " @@ -672,6 +672,10 @@ public static void installPackage(Context context, File packageFile, boolean pro if (!rs.setupBcb(command)) { throw new IOException("Setup BCB failed"); } + Log.i(TAG, "Setting packageFile's read permission."); + if (!packageFile.setReadable(true, false)) { + Log.w(TAG, "Error making packageFile readable."); + } try { if (!rs.allocateSpaceForUpdate(packageFile)) { rs.clearBcb(); @@ -857,7 +861,7 @@ public static void scheduleUpdateOnBoot(Context context, File packageFile) throw // If the package is on the /data partition, use the block map file as // the package name instead. - if (filename.startsWith("/data/")) { + if (SystemProperties.get("persist.sys.recovery_update", "").equals("true") && filename.startsWith("/data/")) { filename = "@/cache/recovery/block.map"; } diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index d89c3d591d46e..1f71026eddd28 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -18,6 +18,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; import java.io.PrintWriter; @@ -240,8 +241,8 @@ public void onCallbackDied(E callback, Object cookie) { public int beginBroadcast() { synchronized (mCallbacks) { if (mBroadcastCount > 0) { - throw new IllegalStateException( - "beginBroadcast() called while already in a broadcast"); + Log.e(TAG, "beginBroadcast() called while already in a broadcast"); + return 0; } final int N = mBroadcastCount = mCallbacks.size(); @@ -304,8 +305,8 @@ public Object getBroadcastCookie(int index) { public void finishBroadcast() { synchronized (mCallbacks) { if (mBroadcastCount < 0) { - throw new IllegalStateException( - "finishBroadcast() called outside of a broadcast"); + Log.e(TAG, "finishBroadcast() called outside of a broadcast"); + return; } Object[] active = mActiveBroadcast; diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index ac2156e9e46ef..580e632b8d965 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -160,6 +160,10 @@ private Trace() { @UnsupportedAppUsage @SystemApi(client = MODULE_LIBRARIES) public static boolean isTagEnabled(long traceTag) { + if (!Build.IS_DEBUGGABLE) { + return false; + } + long tags = nativeGetEnabledTags(); return (tags & traceTag) != 0; } diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index b7e3068a437c4..16bb78e345bad 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -461,6 +461,17 @@ public void resetShouldSwitchSlotOnReboot() { } } + /** + * @hide + */ + public void setPerformanceMode(boolean enable) { + try { + mUpdateEngine.setPerformanceMode(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Unbinds the last bound callback function. */ diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index 13b22d3700301..a1ed2533f5442 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Objects; import java.util.UUID; @@ -515,7 +516,7 @@ private static void validateV4Signature(@Nullable byte[] v4signatureBytes) throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize); } if (hashingInfo.salt != null && hashingInfo.salt.length > 0) { - throw new IOException("Unsupported salt: " + hashingInfo.salt); + throw new IOException("Unsupported salt: " + Arrays.toString(hashingInfo.salt)); } if (hashingInfo.rawRootHash.length != INCFS_MAX_HASH_SIZE) { throw new IOException("rawRootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes"); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 497bfa6380bc1..a21e2ba9ee4cc 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1693,6 +1693,10 @@ public static boolean isEncrypted() { return RoSystemProperties.CRYPTO_ENCRYPTED; } + public static boolean inCryptKeeperBounce() { + return false; + } + /** {@hide} * Is this device file encrypted? * @return true for file encrypted. (Implies isEncrypted() == true) diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 6b540d72bba00..37dfa7de3394b 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -152,6 +152,11 @@ public final class PermissionManager { "permission grant or revoke changed gids"; private static final String SYSTEM_PKG = "android"; + private static final String BLUETOOTH_PKG = "com.android.bluetooth"; + private static final String PHONE_SERVICES_PKG = "com.android.phone"; + private static final String PRINT_SPOOLER_PKG = "com.android.printspooler"; + private static final String FUSED_LOCATION_PKG = "com.android.location.fused"; + private static final String CELL_BROADCAST_SERVICE_PKG = "com.android.cellbroadcastservice"; /** * Refuse to install package if groups of permissions are bad @@ -174,7 +179,7 @@ public final class PermissionManager { private static final int[] EXEMPTED_ROLES = {R.string.config_systemAmbientAudioIntelligence, R.string.config_systemUiIntelligence, R.string.config_systemAudioIntelligence, R.string.config_systemNotificationIntelligence, R.string.config_systemTextIntelligence, - R.string.config_systemVisualIntelligence}; + R.string.config_systemVisualIntelligence, R.string.config_systemTelephonyPackage}; private static final String[] INDICATOR_EXEMPTED_PACKAGES = new String[EXEMPTED_ROLES.length]; @@ -1159,6 +1164,21 @@ public static Set getIndicatorExemptedPackages(@NonNull Context context) updateIndicatorExemptedPackages(context); ArraySet pkgNames = new ArraySet<>(); pkgNames.add(SYSTEM_PKG); + // Scanning for Bluetooth devices when Bluetooth is enabled is considered + // to be location access. It shouldn't be shown for the OS implementation + // of Bluetooth. + pkgNames.add(BLUETOOTH_PKG); + // Phone services (not the Dialer) accesses detailed cellular information + // which is considered to be location information. It's a base OS component + // serving the intended purpose and shouldn't trigger spurious location + // indicator notices every time it retrieves cellular information. + pkgNames.add(PHONE_SERVICES_PKG); + // Location usage indicator can get triggered when sharing a file to a printer + pkgNames.add(PRINT_SPOOLER_PKG); + pkgNames.add(FUSED_LOCATION_PKG); + // indicator pops up when determining location during a geofenced alert + pkgNames.add(CELL_BROADCAST_SERVICE_PKG); + for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) { String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i]; if (exemptedPackage != null) { diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index f5f1c374b636b..f87559277ca3f 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -53,6 +53,7 @@ import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; @@ -75,17 +76,6 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener, AppOpsManager.OnOpStartedListener { - /** - * Whether to show the mic and camera icons. - */ - private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"; - - /** - * Whether to show the location indicators. - */ - private static final String PROPERTY_LOCATION_INDICATORS_ENABLED = - "location_indicators_enabled"; - /** * Whether to show the Permissions Hub. */ @@ -111,14 +101,21 @@ private static boolean shouldShowPermissionsHub() { PROPERTY_PERMISSIONS_HUB_2_ENABLED, false); } - private static boolean shouldShowIndicators() { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) || shouldShowPermissionsHub(); + private boolean shouldShowIndicators() { + return shouldShowCameraIndicator() || shouldShowLocationIndicator() || + shouldShowPermissionsHub(); } - private static boolean shouldShowLocationIndicator() { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_INDICATORS_ENABLED, false); + private boolean shouldShowCameraIndicator() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ENABLE_CAMERA_PRIVACY_INDICATOR, 1, + UserHandle.USER_CURRENT) == 1; + } + + private boolean shouldShowLocationIndicator() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ENABLE_LOCATION_PRIVACY_INDICATOR, 1, + UserHandle.USER_CURRENT) == 1; } private static long getRecentThreshold(Long now) { @@ -299,13 +296,16 @@ private void addLinkToChainIfNotPresentLocked(String op, String packageName, int return usages; } - List ops = new ArrayList<>(CAMERA_OPS); + List ops = new ArrayList<>(); + if (shouldShowCameraIndicator()) { + ops.addAll(CAMERA_OPS); + if (!isMicMuted) { + ops.addAll(MIC_OPS); + } + } if (shouldShowLocationIndicator()) { ops.addAll(LOCATION_OPS); } - if (!isMicMuted) { - ops.addAll(MIC_OPS); - } Map> rawUsages = getOpUsages(ops); diff --git a/core/java/android/pocket/IPocketCallback.aidl b/core/java/android/pocket/IPocketCallback.aidl new file mode 100644 index 0000000000000..53e5412f89beb --- /dev/null +++ b/core/java/android/pocket/IPocketCallback.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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 android.pocket; + +/** @hide */ +interface IPocketCallback { + + // notify when pocket state changes. + void onStateChanged(boolean isDeviceInPocket, int reason); + +} \ No newline at end of file diff --git a/core/java/android/pocket/IPocketService.aidl b/core/java/android/pocket/IPocketService.aidl new file mode 100644 index 0000000000000..783465774207b --- /dev/null +++ b/core/java/android/pocket/IPocketService.aidl @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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 android.pocket; + +import android.pocket.IPocketCallback; + +/** @hide */ +interface IPocketService { + + // add callback to get notified about pocket state. + void addCallback(IPocketCallback callback); + + // remove callback and stop getting notified about pocket state. + void removeCallback(IPocketCallback callback); + + // notify pocket service about intercative state changed. + // @see com.android.policy.PhoneWindowManager + void onInteractiveChanged(boolean interactive); + + // external processes can request changing listening state. + void setListeningExternal(boolean listen); + + // check if device is in pocket. + boolean isDeviceInPocket(); + + // Custom methods + void setPocketLockVisible(boolean visible); + boolean isPocketLockVisible(); + +} \ No newline at end of file diff --git a/core/java/android/pocket/PocketConstants.java b/core/java/android/pocket/PocketConstants.java new file mode 100644 index 0000000000000..70aa74a7f2a69 --- /dev/null +++ b/core/java/android/pocket/PocketConstants.java @@ -0,0 +1,19 @@ +package android.pocket; + +/** + * This class contains global pocket setup constants. + * @author Carlo Savignano + * @hide + */ + +public class PocketConstants { + + public static final boolean DEBUG = false; + public static final boolean DEBUG_SPEW = false; + + /** + * Whether to use proximity sensor to evaluate pocket state. + */ + public static final boolean ENABLE_PROXIMITY_JUDGE = true; + +} diff --git a/core/java/android/pocket/PocketManager.java b/core/java/android/pocket/PocketManager.java new file mode 100644 index 0000000000000..eef4436564ab8 --- /dev/null +++ b/core/java/android/pocket/PocketManager.java @@ -0,0 +1,233 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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 android.pocket; + +import android.content.Context; +import android.os.Handler; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.text.format.DateUtils; +import android.util.Log; +import android.util.Slog; + +/** + * A class that coordinates listening for pocket state. + *

+ * Use {@link android.content.Context#getSystemService(java.lang.String)} + * with argument {@link android.content.Context#POCKET_SERVICE} to get + * an instance of this class. + * + * Usage: import and create a final {@link IPocketCallback.Stub()} and implement your logic in + * {@link IPocketCallback#onStateChanged(boolean, int)}. Then add your callback to the pocket manager + * + * // define a final callback + * private final IPocketCallback mCallback = new IPocketCallback.Stub() { + * + * @Override + * public void onStateChanged(boolean isDeviceInPocket, int reason) { + * // Your method to handle logic outside of this callback, ideally with a handler + * // posting on UI Thread for view hierarchy operations or with its own background thread. + * handlePocketStateChanged(isDeviceInPocket, reason); + * } + * + * } + * + * // add callback to pocket manager + * private void addCallback() { + * PocketManager manager = (PocketManager) context.getSystemService(Context.POCKET_SERVICE); + * manager.addCallback(mCallback); + * } + * + * @author Carlo Savignano + * @hide + */ +public class PocketManager { + + private static final String TAG = PocketManager.class.getSimpleName(); + static final boolean DEBUG = false; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of the sensor. + * @see PocketService#handleDispatchCallbacks() + */ + public static final int REASON_SENSOR = 0; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of an error while accessing service. + * @see #addCallback(IPocketCallback) + * @see #removeCallback(IPocketCallback) + */ + public static final int REASON_ERROR = 1; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of a needed reset. + * @see PocketService#binderDied() + */ + public static final int REASON_RESET = 2; + + private Context mContext; + private IPocketService mService; + private PowerManager mPowerManager; + private Handler mHandler; + private boolean mPocketViewTimerActive; + + public PocketManager(Context context, IPocketService service) { + mContext = context; + mService = service; + if (mService == null) { + Slog.v(TAG, "PocketService was null"); + } + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mHandler = new Handler(); + } + + /** + * Add pocket state callback. + * @see PocketService#handleRemoveCallback(IPocketCallback) + */ + public void addCallback(final IPocketCallback callback) { + if (mService != null) try { + mService.addCallback(callback); + } catch (RemoteException e1) { + Log.w(TAG, "Remote exception in addCallback: ", e1); + if (callback != null){ + try { + callback.onStateChanged(false, REASON_ERROR); + } catch (RemoteException e2) { + Log.w(TAG, "Remote exception in callback.onPocketStateChanged: ", e2); + } + } + } + } + + /** + * Remove pocket state callback. + * @see PocketService#handleAddCallback(IPocketCallback) + */ + public void removeCallback(final IPocketCallback callback) { + if (mService != null) try { + mService.removeCallback(callback); + } catch (RemoteException e1) { + Log.w(TAG, "Remote exception in removeCallback: ", e1); + if (callback != null){ + try { + callback.onStateChanged(false, REASON_ERROR); + } catch (RemoteException e2) { + Log.w(TAG, "Remote exception in callback.onPocketStateChanged: ", e2); + } + } + } + } + + /** + * Notify service about device interactive state changed. + * {@link PhoneWindowManager#startedWakingUp()} + * {@link PhoneWindowManager#startedGoingToSleep(int)} + */ + public void onInteractiveChanged(boolean interactive) { + boolean isPocketViewShowing = (interactive && isDeviceInPocket()); + synchronized (mPocketLockTimeout) { + if (mPocketViewTimerActive != isPocketViewShowing) { + if (isPocketViewShowing) { + if (DEBUG) Log.v(TAG, "Setting pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); // remove any pending requests + mHandler.postDelayed(mPocketLockTimeout, 15 * DateUtils.SECOND_IN_MILLIS); + mPocketViewTimerActive = true; + } else { + if (DEBUG) Log.v(TAG, "Clearing pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + } + } + if (mService != null) try { + mService.onInteractiveChanged(interactive); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in addCallback: ", e); + } + } + + /** + * Request listening state change by, but not limited to, external process. + * @see PocketService#handleSetListeningExternal(boolean) + */ + public void setListeningExternal(boolean listen) { + if (mService != null) try { + mService.setListeningExternal(listen); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in setListeningExternal: ", e); + } + // Clear timeout when user hides pocket lock with long press power. + if (mPocketViewTimerActive && !listen) { + if (DEBUG) Log.v(TAG, "Clearing pocket timer due to override"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + } + + /** + * Return whether device is in pocket. + * @see PocketService#isDeviceInPocket() + * @return + */ + public boolean isDeviceInPocket() { + if (mService != null) try { + return mService.isDeviceInPocket(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isDeviceInPocket: ", e); + } + return false; + } + + class PocketLockTimeout implements Runnable { + @Override + public void run() { + mPowerManager.goToSleep(SystemClock.uptimeMillis()); + mPocketViewTimerActive = false; + } + } + + /** Custom methods **/ + + public void setPocketLockVisible(boolean visible) { + if (!visible){ + if (DEBUG) Log.v(TAG, "Clearing pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + if (mService != null) try { + mService.setPocketLockVisible(visible); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in setPocketLockVisible: ", e); + } + } + + public boolean isPocketLockVisible() { + if (mService != null) try { + return mService.isPocketLockVisible(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isPocketLockVisible: ", e); + } + return false; + } + + private PocketLockTimeout mPocketLockTimeout = new PocketLockTimeout(); + +} diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java index 7edc987ee0c13..fe53d4eeff647 100644 --- a/core/java/android/preference/GenericInflater.java +++ b/core/java/android/preference/GenericInflater.java @@ -406,7 +406,7 @@ public final T createItem(String name, String prefix, AttributeSet attrs) InflateException ie = new InflateException(attrs .getPositionDescription() + ": Error inflating class " - + constructor.getClass().getName()); + + constructor.getDeclaringClass().getName()); ie.initCause(e); throw ie; } diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 0a6a405fbce6f..bd7831998741d 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -113,6 +113,7 @@ public void onAudioVolumeGroupChanged(int group, int flags) { @UnsupportedAppUsage private final int mStreamType; private final int mMaxStreamVolume; + private final boolean mVoiceCapable; private boolean mAffectedByRingerMode; private boolean mNotificationOrRing; private final Receiver mReceiver = new Receiver(); @@ -206,6 +207,8 @@ public SeekBarVolumizer( } } mDefaultUri = defaultUri; + mVoiceCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); } private boolean hasAudioProductStrategies() { @@ -254,6 +257,11 @@ private static boolean isMediaStream(int stream) { return stream == AudioManager.STREAM_MUSIC; } + private boolean isNotificationStreamLinked() { + return mVoiceCapable && Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + } + public void setSeekBar(SeekBar seekBar) { if (mSeekBar != null) { mSeekBar.setOnSeekBarChangeListener(null); @@ -281,13 +289,19 @@ protected void updateSeekBar() { mSeekBar.setProgress(mLastAudibleStreamVolume, true); } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { mSeekBar.setProgress(0, true); + mSeekBar.setEnabled(isSeekBarEnabled()); } else if (mMuted) { mSeekBar.setProgress(0, true); } else { + mSeekBar.setEnabled(isSeekBarEnabled()); mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true); } } + private boolean isSeekBarEnabled() { + return !(mStreamType == AudioManager.STREAM_NOTIFICATION && isNotificationStreamLinked()); + } + @Override public boolean handleMessage(Message msg) { switch (msg.what) { @@ -434,7 +448,7 @@ public void revertVolume() { } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { - if (fromTouch) { + if (fromTouch && isSeekBarEnabled()) { postSetVolume(progress); } if (mCallback != null) { @@ -631,7 +645,8 @@ public void onReceive(Context context, Intent intent) { } private void updateVolumeSlider(int streamType, int streamValue) { - final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) + final boolean streamMatch = mNotificationOrRing && isNotificationStreamLinked() + ? isNotificationOrRing(streamType) : (streamType == mStreamType); if (mSeekBar != null && streamMatch && streamValue != -1) { final boolean muted = mAudioManager.isStreamMute(mStreamType) diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 0829d85801ac3..c2dbfb8925bac 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -646,6 +646,11 @@ public static boolean isStatusCompleted(int status) { */ public static final int STATUS_QUEUED_FOR_WIFI = 196; + /** + * This download is paused manually. + */ + public static final int STATUS_PAUSED_MANUAL = 197; + /** * This download couldn't be completed due to insufficient storage * space. Typically, this is because the SD card is full. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 805fdc44b2562..2841c1a6cfd6f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3615,6 +3615,8 @@ public static final class System extends NameValueTable { // At one time in System, then Global, but now back in Secure MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS); + + MOVED_TO_SECURE.add(Secure.VOLUME_LINK_NOTIFICATION); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -4514,6 +4516,13 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String PEAK_REFRESH_RATE = "peak_refresh_rate"; + /** + * Whether refresh rate should be switched to 60Hz on power save mode. + * @hide + */ + @Readable + public static final String LOW_POWER_REFRESH_RATE = "low_power_refresh_rate"; + /** * The amount of time in milliseconds before the device goes to sleep or begins * to dream after a period of inactivity. This value is also known as the @@ -5090,6 +5099,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String DATE_FORMAT = "date_format"; + /** + * Defines the screen-off animation to display + * @hide + */ + public static final String SCREEN_OFF_ANIMATION = "screen_off_animation"; + /** * Whether the setup wizard has been run before (on first boot), or if * it still needs to be run. @@ -5265,6 +5280,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; + /** + * Whether Proximity on Wake is Enabled or not + * @hide + */ + public static final String PROXIMITY_ON_WAKE = "proximity_on_wake"; + /** * Show pointer location on screen? * 0 = no @@ -5469,24 +5490,1221 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean * 1 - Show percentage * @hide */ - @Readable - public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent"; + @Readable + public static final String SHOW_BATTERY_PERCENT = "dummy_show_battery_percent"; + + /** + * Whether or not to enable multiple audio focus. + * When enabled, requires more management by user over application playback activity, + * for instance pausing media apps when another starts. + * @hide + */ + @Readable + public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled"; + + /** + * @hide + */ + public static final String SCREENSHOT_SHUTTER_SOUND = "screenshot_shutter_sound"; + + /** + * Swipe to screenshot + * @hide + */ + public static final String SWIPE_TO_SCREENSHOT = "swipe_to_screenshot"; + + /** + * Whether to play notification sound and vibration if screen is ON + * 0 - never + * 1 - always + * @hide + */ + public static final String NOTIFICATION_SOUND_VIB_SCREEN_ON = "notification_sound_vib_screen_on"; + + /** + * Whether to show heads up only for dialer and sms apps + * @hide + */ + public static final String LESS_BORING_HEADS_UP = "less_boring_heads_up"; + + /** + * Whether the phone vibrates on call connect + * @hide + */ + public static final String VIBRATE_ON_CONNECT = "vibrate_on_connect"; + + /** + * Whether the phone vibrates on call waiting + * @hide + */ + public static final String VIBRATE_ON_CALLWAITING = "vibrate_on_callwaiting"; + + /** + * Whether the phone vibrates on disconnect + * @hide + */ + public static final String VIBRATE_ON_DISCONNECT = "vibrate_on_disconnect"; + + /** + * Whether to wake the display when plugging or unplugging the charger + * @hide + */ + public static final String WAKE_WHEN_PLUGGED_OR_UNPLUGGED = "wake_when_plugged_or_unplugged"; + + /** + * Adaptive playback + * Automatically pause media when the volume is muted and + * will resume automatically when volume is restored. + * 0 = disabled + * 1 = enabled + * @hide + */ + public static final String ADAPTIVE_PLAYBACK_ENABLED = "adaptive_playback_enabled"; + + /** + * Adaptive playback's timeout in ms + * @hide + */ + public static final String ADAPTIVE_PLAYBACK_TIMEOUT = "adaptive_playback_timeout"; + + /** + * IMPORTANT: If you add a new public settings you also have to add it to + * PUBLIC_SETTINGS below. If the new setting is hidden you have to add + * it to PRIVATE_SETTINGS below. Also add a validator that can validate + * the setting value. See an example above. + */ + + /** + * Whether the battery light should be enabled (if hardware supports it) + * The value is boolean (1 or 0). + * @hide + */ + public static final String BATTERY_LIGHT_ENABLED = "battery_light_enabled"; + + /** + * Whether the full battery light should be enabled + * The value is boolean (1 or 0). + * @hide + */ + public static final String BATTERY_FULL_LIGHT_ENABLED = "battery_full_light_enabled"; + + /** + * Whether to show battery light when DND mode is active + * @hide + */ + public static final String BATTERY_LIGHT_ALLOW_ON_DND = "battery_light_allow_on_dnd"; + + /** + * Whether to show blinking light when battery is low + * @hide + */ + public static final String BATTERY_LIGHT_LOW_BLINKING = "battery_light_low_blinking"; + + /** + * Low battery charging color + * @hide + */ + public static final String BATTERY_LIGHT_LOW_COLOR = "battery_light_low_color"; + + /** + * Medium battery charging color + * @hide + */ + public static final String BATTERY_LIGHT_MEDIUM_COLOR = "battery_light_medium_color"; + + /** + * Full battery charging color + * @hide + */ + public static final String BATTERY_LIGHT_FULL_COLOR = "battery_light_full_color"; + + /** + * Really full 100 battery charging color + * @hide + */ + public static final String BATTERY_LIGHT_REALLYFULL_COLOR = "battery_light_reallyfull_color"; + + /** + * Whether to show or hide alert slider notifications on supported devices + * @hide + */ + public static final String ALERT_SLIDER_NOTIFICATIONS = "alert_slider_notifications"; + + /** + * Whether or not volume button music controls should be enabled to seek media tracks + * @hide + */ + public static final String VOLBTN_MUSIC_CONTROLS = "volbtn_music_controls"; + + /** + * Whether to allow one finger quick settings expansion on the side of the statusbar. + * 0 = 0ff, 1 = right, 2 = left, 3 = both + * @hide + */ + public static final String STATUS_BAR_QUICK_QS_PULLDOWN = "qs_quick_pulldown"; + + /** + * Enable statusbar double tap gesture to put device to sleep + * @hide + */ + public static final String DOUBLE_TAP_SLEEP_GESTURE = "double_tap_sleep_gesture"; + + /** + * Double tap on lockscreen to sleep + * @hide + */ + public static final String DOUBLE_TAP_SLEEP_LOCKSCREEN = "double_tap_sleep_lockscreen"; + + /** + * Use doubletap as doze pulse triggers + * @hide + */ + public static final String DOZE_TRIGGER_DOUBLETAP = "doze_trigger_doubletap"; + + /** + * Whether to launch default music player when headset plugged in + * @hide + */ + public static final String HEADSET_CONNECT_PLAYER = "headset_connect_player"; + + /** + * Whether to enable fingerprint wake-and-unlock. + * @hide + */ + public static final String FINGERPRINT_WAKE_UNLOCK = "fingerprint_wake_unlock"; + + /** + * Whether to scramble a pin unlock layout + * @hide + */ + public static final String LOCKSCREEN_PIN_SCRAMBLE_LAYOUT = + "lockscreen_scramble_pin_layout"; + + /** + * Whether to use the custom quick unlock screen control + * @hide + */ + public static final String LOCKSCREEN_QUICK_UNLOCK_CONTROL = + "lockscreen_quick_unlock_control"; + + /** + * Whether to inverse keys in navigation bar + * Default 0 + * @hide + */ + public static final String NAVIGATION_BAR_INVERSE = "navigation_bar_inverse"; + + /** + * Wheter to show network traffic indicator in statusbar + * @hide + */ + public static final String NETWORK_TRAFFIC_STATE = "network_traffic_state"; + + /** + * Network traffic inactivity threshold + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD = "network_traffic_autohide_threshold"; + + /** + * @hide + */ + public static final String USE_TILES_ON_SECURE_KEYGUARD = "use_tiles_on_secure_keyguard"; + + /** + * @hide + */ + public static final String USE_OLD_MOBILETYPE = "use_old_mobiletype"; + + /** + * Whether to display roaming indicator for roaming signal connection + * @hide + */ + public static final String ROAMING_INDICATOR_ICON = "roaming_indicator_icon"; + + /** + * Whether to display 4G icon instead LTE + * @hide + */ + public static final String SHOW_FOURG_ICON = "show_fourg_icon"; + + /** + * Whether to display cross sign for a data disabled connection + * @hide + */ + public static final String DATA_DISABLED_ICON = "data_disabled_icon"; + + /** + * Keyguard weather temperature + * 0: Hide the temperature + * 1: Display the temperature with scale and image + * 2: Display the temperature without scale and with image + * 3: Display the temparature with scale and without image + * 4: Display the temperature without scale and without image + * 5: Display the temperature image + * @hide + */ + public static final String KG_SHOW_WEATHER_TEMP = "kg_show_weather_temp"; + + /** + * Launcher weather temperature + * 0: Hide the temperature + * 1: Display the temperature with scale and image + * 2: Display the temperature without scale and with image + * 3: Display the temparature with scale and without image + * 4: Display the temperature without scale and without image + * 5: Display the temperature image + * @hide + */ + public static final String LAUNCHER_SHOW_WEATHER_TEMP = "launcher_show_weather_temp"; + + /** + * Whether to enable statusbar brightness control + * @hide + */ + @Readable + public static final String STATUS_BAR_BRIGHTNESS_CONTROL = "status_bar_brightness_control"; + + /** + * Switch between filters for lockscreen album art + * @hide + */ + public static final String LOCKSCREEN_ALBUMART_FILTER = "lockscreen_albumart_filter"; + + /** + * Lockscreen album art blur level + * @hide + */ + public static final String LS_MEDIA_FILTER_BLUR_RADIUS = "ls_media_filter_blur_radius"; + + /** + * Whether to show VoLTE icon or not + * @hide + */ + public static final String SHOW_VOLTE_ICON = "show_volte_icon"; + + /** + * VoLTE icon style + * @hide + */ + public static final String VOLTE_ICON_STYLE = "volte_icon_style"; + + /** + * Battery style + * @hide + */ + public static final String STATUS_BAR_BATTERY_STYLE = "status_bar_battery_style"; + + /** + * Statusbar Battery % + * 0: Hide the battery percentage + * 1: Display the battery percentage inside the icon + * 2: Display the battery percentage next to Icon + * @hide + */ + public static final String STATUS_BAR_SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent"; + + /** + * Show battery percentage when charging + * @hide + */ + public static final String STATUS_BAR_BATTERY_TEXT_CHARGING = "status_bar_battery_text_charging"; + + /** + * QS battery style + * @hide + */ + public static final String QS_BATTERY_STYLE = "qs_battery_style"; + + /** + * QS battery % + * 0: Hide the battery percentage + * 1: Display the battery percentage inside the icon + * 2: Display the battery percentage next to the icon + * @hide + */ + public static final String QS_SHOW_BATTERY_PERCENT = "qs_show_battery_percent"; + + /** + * Statusbar clock background + * 0 - hide accented chip + * 1 - show accented chip (default) + * @hide + */ + public static final String STATUSBAR_CLOCK_CHIP = "statusbar_clock_chip"; + + /** + * Whether the phone ringtone should be played in an increasing manner + * 0 = 0ff, 1 = on + * @hide + */ + public static final String INCREASING_RING = "increasing_ring"; + + /** + * Start volume fraction for increasing ring volume + * @hide + */ + public static final String INCREASING_RING_START_VOLUME = "increasing_ring_start_vol"; + + /** + * Ramp up time (seconds) for increasing ring + * @hide + */ + public static final String INCREASING_RING_RAMP_UP_TIME = "increasing_ring_ramp_up_time"; + + /** + * Whether to show the battery info on the lockscreen while charging + * @hide + */ + @Readable + public static final String LOCKSCREEN_BATTERY_INFO = "lockscreen_battery_info"; + + /** + * @hide + */ + public static final String UDFPS_ANIM = "udfps_anim"; + + /** + * @hide + */ + public static final String UDFPS_ANIM_STYLE = "udfps_anim_style"; + + /** + * @hide + */ + public static final String UDFPS_ICON = "udfps_icon"; + + /** + * FOD pressed color + * @hide + */ + public static final String UDFPS_COLOR = "udfps_color"; + + /** + * @hide + */ + public static final String OMNI_CUSTOM_FP_ICON_ENABLED = "custom_fp_icon_enabled"; + + /** + * @hide + */ + public static final String OMNI_CUSTOM_FP_ICON = "custom_fingerprint_icon"; + + /** + * Whether to enable the ripple animation on fingerprint unlock + * @hide + */ + public static final String ENABLE_RIPPLE_EFFECT = "enable_ripple_effect"; + + /** + * @hide + */ + public static final String QS_TRANSPARENCY = "qs_transparency"; + + /** + * Whether to take partial screenshot with volume down + power click. + * @hide + */ + public static final String CLICK_PARTIAL_SCREENSHOT = "click_partial_screenshot"; + + /** + * boolean value. toggles compact layout in navigation bar + * @hide + */ + public static final String NAV_BAR_COMPACT_LAYOUT = "navigation_bar_compact_layout"; + + /** + * Hide Statusbar on LockScreen + * @hide + */ + public static final String HIDE_LOCKSCREEN_STATUS_BAR = "hide_lockscreen_status_bar"; + + /** + * Whether to display sound panel in the power menu + * @hide + */ + public static final String POWERMENU_SOUNDPANEL = "powermenu_soundpanel"; + + /** + * Whether to display screenshot in the power menu + * @hide + */ + public static final String POWERMENU_SCREENSHOT = "powermenu_screenshot"; + + /** + * Whether to display settings in the power menu + * @hide + */ + public static final String POWERMENU_SETTINGS = "powermenu_settings"; + + /** + * Whether to display lock in the power menu + * @hide + */ + public static final String POWERMENU_LOCKDOWN = "powermenu_lockdown"; + + /** + * Whether to display airplane in the power menu + * @hide + */ + public static final String POWERMENU_AIRPLANE = "powermenu_airplane"; + + /** + * Whether to display reboot in the power menu + * @hide + */ + public static final String POWERMENU_RESTART = "powermenu_restart"; + + /** + * Whether to display reboot in the power menu + * @hide + */ + public static final String POWERMENU_ADVANCED = "powermenu_advanced"; + + /** + * Whether to display power in the power menu + * @hide + */ + public static final String POWERMENU_POWER = "powermenu_power"; + + /** + * Whether to display the users option in the power menu + * @hide + */ + public static final String POWERMENU_USERS = "powermenu_users"; + + /** + * Whether to display the users option in the power menu + * @hide + */ + public static final String POWERMENU_LOGOUT = "powermenu_logout"; + + /** + * Whether to display the users option in the power menu + * @hide + */ + public static final String POWERMENU_EMERGENCY = "powermenu_emergency"; + + /** + * Whether to display the torch option in the power menu + * @hide + */ + public static final String POWERMENU_TORCH = "powermenu_torch"; + + /** + * Disable power menu on secure lock screens + * + * @hide + */ + public static final String LOCK_POWER_MENU_DISABLED = "lockscreen_power_menu_disabled"; + + /** + * Whether to show QS panel footer warnings like for active VPN + * @hide + */ + public static final String QS_FOOTER_WARNINGS = "qs_footer_warnings"; + + /** + * Volume keys control cursor in text fields (default is 0) + * 0 - Disabled + * 1 - Volume up/down moves cursor left/right + * 2 - Volume up/down moves cursor right/left + * @hide + */ + @Readable + public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + + /** + * Volume rocker wake + * @hide + */ + public static final String VOLUME_ROCKER_WAKE = "volume_rocker_wake"; + + /** + * Disable hw buttons - actions, brightness, haptic feedback, overflow menu + * @hide + */ + public static final String HARDWARE_KEYS_DISABLE = "hardware_keys_disable"; + + /** + * Whether allowing pocket service to register sensors and dispatch informations. + * 0 = disabled + * 1 = enabled + * @author Carlo Savignano + * @hide + */ + public static final String POCKET_JUDGE = "pocket_judge"; + + /** + * Color temperature of the display during the day + * @hide + */ + public static final String DISPLAY_TEMPERATURE_DAY = "display_temperature_day"; + + /** + * Color temperature of the display at night + * @hide + */ + public static final String DISPLAY_TEMPERATURE_NIGHT = "display_temperature_night"; + + /** + * Display color temperature adjustment mode, one of DAY (default), NIGHT, or AUTO. + * @hide + */ + public static final String DISPLAY_TEMPERATURE_MODE = "display_temperature_mode"; + + /** + * Automatic outdoor mode + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_AUTO_OUTDOOR_MODE = "display_auto_outdoor_mode"; + + /** + * Reader mode + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_READING_MODE = "display_reading_mode"; + + /** + * Use display power saving features such as CABC or CABL + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_CABC = "display_low_power"; + + /** + * Use color enhancement feature of display + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_COLOR_ENHANCE = "display_color_enhance"; + + /** + * Use auto contrast optimization feature of display + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_AUTO_CONTRAST = "display_auto_contrast"; + + /** + * Manual display color adjustments (RGB values as floats, separated by spaces) + * @hide + */ + public static final String DISPLAY_COLOR_ADJUSTMENT = "display_color_adjustment"; + + /** + * The current custom picture adjustment values as a delimited string + * @hide + */ + public static final String DISPLAY_PICTURE_ADJUSTMENT = + "display_picture_adjustment"; + + /** + * Did we tell about how they can stop breaking their eyes? + * @hide + */ + public static final String LIVE_DISPLAY_HINTED = "live_display_hinted"; + + /** + * Anti flicker + * 0 = 0ff, 1 = on + * @hide + */ + public static final String DISPLAY_ANTI_FLICKER = "display_anti_flicker"; + + /** + * Whether or not to vibrate when a touchscreen gesture is detected + * @hide + */ + public static final String TOUCHSCREEN_GESTURE_HAPTIC_FEEDBACK = "touchscreen_gesture_haptic_feedback"; + + /** + * Whether the HighTouchPollingRate is activated or not. + * 0 = off, 1 = on + * @hide + */ + public static final String HIGH_TOUCH_POLLING_RATE_ENABLE = + "high_touch_polling_rate_enable"; + + /** + * Whether the HighTouchSensitivity is activated or not. + * 0 = off, 1 = on + * @hide + */ + public static final String HIGH_TOUCH_SENSITIVITY_ENABLE = + "high_touch_sensitivity_enable"; + + /** @hide */ + public static final String BACK_GESTURE_HEIGHT = "back_gesture_height"; + + /** + * Whether to show the kill app button in notification guts + * @hide + */ + public static final String NOTIFICATION_GUTS_KILL_APP_BUTTON = + "notification_guts_kill_app_button"; + + /** + * Toast icon + * @hide + */ + @Readable + public static final String TOAST_ICON = "toast_icon"; + + /** + * reTicker Status + * @hide + */ + public static final String RETICKER_STATUS = "reticker_status"; + + /** + * reTicker Colored + * @hide + */ + public static final String RETICKER_COLORED = "reticker_colored"; + + /** + * Restyled reTicker + * @hide + */ + public static final String NEW_RETICKER = "new_reticker"; + + /** + * Makes reticker look like OneUI's headsup + * @hide + */ + public static final String NEW_RETICKER_ANIMATION = "new_reticker_animation"; + + /** + * GameSpace: List of added games by user + * @hide + */ + @Readable + public static final String GAMESPACE_GAME_LIST = "gamespace_game_list"; + + /** + * GameSpace: Whether fullscreen intent will be suppressed while in game session + * @hide + */ + @Readable + public static final String GAMESPACE_SUPPRESS_FULLSCREEN_INTENT = "gamespace_suppress_fullscreen_intent"; + + /** + * Whether to show the clock in the right or left position or show it in the center + * 0: show the clock in the right position (LTR) + * 1: show the clock in the center + * 2: show the clock in the left position (LTR) + * default: 0 + * @hide + */ + public static final String STATUS_BAR_CLOCK = "status_bar_clock"; + + /** + * Display style of AM/PM next to clock in status bar + * 0: Normal display (Eclair stock) + * 1: Small display (Froyo stock) + * 2: No display (Gingerbread/ICS stock) + * default: 2 + * @hide + */ + public static final String STATUS_BAR_AM_PM = "status_bar_am_pm"; + + /** + * Whether to hide clock when launcher is visible + * default: false + * @hide + */ + public static final String STATUS_BAR_CLOCK_AUTO_HIDE_LAUNCHER = "status_bar_clock_auto_hide_launcher"; + + /** + * Whether to show seconds next to clock in status bar + * 0 - hide (default) + * 1 - show + * @hide + */ + public static final String STATUS_BAR_CLOCK_SECONDS = "status_bar_clock_seconds"; + + /** + * Shows custom date before clock time + * 0 - No Date + * 1 - Small Date + * 2 - Normal Date + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_DISPLAY = "status_bar_clock_date_display"; + + /** + * Sets the date string style + * 0 - Regular style + * 1 - Lowercase + * 2 - Uppercase + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_STYLE = "status_bar_clock_date_style"; + + /** + * Position of date + * 0 - Left of clock + * 1 - Right of clock + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_POSITION = "status_bar_clock_date_position"; + + /** + * Stores the java DateFormat string for the date + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_FORMAT = "status_bar_clock_date_format"; + + /** + * Whether to auto hide clock + * @hide + */ + public static final String STATUS_BAR_CLOCK_AUTO_HIDE = "status_bar_clock_auto_hide"; + + /** @hide */ + public static final String STATUS_BAR_CLOCK_AUTO_HIDE_HDURATION = "status_bar_clock_auto_hide_hduration"; + + /** @hide */ + public static final String STATUS_BAR_CLOCK_AUTO_HIDE_SDURATION = "status_bar_clock_auto_hide_sduration"; + + /** + * Whether to display QS clock + * @hide + */ + public static final String SHOW_QS_CLOCK = "show_qs_clock"; + + /** + * Whether to display QS date + * @hide + */ + public static final String SHOW_QS_DATE = "show_qs_date"; + + /** + * Whether to show data usage in qs footer + * @hide + */ + public static final String QS_FOOTER_DATA_USAGE = "qs_footer_data_usage"; + + /** + * Whether edge light is enabled. + * Default 0 + * @hide + */ + public static final String EDGE_LIGHT_ENABLED = "edge_light_enabled"; + + /** + * Whether to show edge light for all pulse events and not just for notifications. + * Default 0 + * @hide + */ + public static final String EDGE_LIGHT_ALWAYS_TRIGGER_ON_PULSE = "edge_light_always_trigger_on_pulse"; + + /** + * Whether to repeat edge light animation until pulse timeout. + * Default 0 + * @hide + */ + public static final String EDGE_LIGHT_REPEAT_ANIMATION = "edge_light_repeat_animation"; + + /** + * Color mode of edge light. + * 0: Accent + * 1: Notification + * 2: Wallpaper + * 3: Custom + * Default 0 + * @hide + */ + public static final String EDGE_LIGHT_COLOR_MODE = "edge_light_color_mode"; + + /** + * Custom color (hex value) for edge light. + * Default #FFFFFF + * @hide + */ + public static final String EDGE_LIGHT_CUSTOM_COLOR = "edge_light_custom_color"; + + /** + * Clock font size + * @hide + */ + public static final String STATUS_BAR_CLOCK_SIZE = "status_bar_clock_size"; + + /** + * Clock font size QS + * @hide + */ + public static final String QS_HEADER_CLOCK_SIZE = "qs_header_clock_size"; + + /** + * Settings Dashboard Style + * @hide + */ + public static final String SETTINGS_DASHBOARD_STYLE = "settings_dashboard_style"; + + /** + * @hide + */ + public static final String SMALL_CLOCK_DOUBLE_LINE = "small_clock_double_line"; + + /** + * Gestures nav: left long back swipe action + * @hide + */ + public static final String LEFT_LONG_BACK_SWIPE_ACTION = "left_long_back_swipe_action"; + + /** + * Gestures nav: left long back swipe app action + * @hide + */ + public static final String LEFT_LONG_BACK_SWIPE_APP_ACTION = "left_long_back_swipe_app_action"; + + /** + * Display friendly name of custom application launched when + * using the long left back swipe app action + * @hide + */ + public static final String LEFT_LONG_BACK_SWIPE_APP_FR_ACTION = "left_long_back_swipe_app_fr_action"; + + /** + * Launch custom activity when using the long left back swipe app action + * + * @hide + */ + public static final String LEFT_LONG_BACK_SWIPE_APP_ACTIVITY_ACTION = "left_long_back_swipe_app_activity_action"; + + /** + * Gestures nav: right long back swipe action + * @hide + */ + public static final String RIGHT_LONG_BACK_SWIPE_ACTION = "right_long_back_swipe_action"; + + /** + * Gestures nav: right long back swipe app action + * @hide + */ + public static final String RIGHT_LONG_BACK_SWIPE_APP_ACTION = "right_long_back_swipe_app_action"; + + /** + * Display friendly name of custom application launched when + * using the long right back swipe app action + * @hide + */ + public static final String RIGHT_LONG_BACK_SWIPE_APP_FR_ACTION = "right_long_back_swipe_app_fr_action"; + + /** + * Launch custom activity when using the long right back swipe app action + * + * @hide + */ + public static final String RIGHT_LONG_BACK_SWIPE_APP_ACTIVITY_ACTION = "right_long_back_swipe_app_activity_action"; + + /** + * Gestures nav: long back swipe timeout + * @hide + */ + public static final String LONG_BACK_SWIPE_TIMEOUT = "long_back_swipe_timeout"; + + /** + * Gestures nav: whether to use extended swipe instead of timeout to trigger custom actions + * @hide + */ + public static final String BACK_SWIPE_EXTENDED = "back_swipe_extended"; + + /** + * Gestures nav: left long back swipe action + * @hide + */ + public static final String LEFT_VERTICAL_BACK_SWIPE_ACTION = "left_vertical_back_swipe_action"; + + /** + * Gestures nav: left long back swipe app action + * @hide + */ + public static final String LEFT_VERTICAL_BACK_SWIPE_APP_ACTION = "left_vertical_back_swipe_app_action"; + + /** + * Display friendly name of custom application launched when + * using the long left back swipe app action + * @hide + */ + public static final String LEFT_VERTICAL_BACK_SWIPE_APP_FR_ACTION = "left_vertical_back_swipe_app_fr_action"; + + /** + * Launch custom activity when using the long left back swipe app action + * + * @hide + */ + public static final String LEFT_VERTICAL_BACK_SWIPE_APP_ACTIVITY_ACTION = "left_vertical_back_swipe_app_activity_action"; + + /** + * Gestures nav: right long back swipe action + * @hide + */ + public static final String RIGHT_VERTICAL_BACK_SWIPE_ACTION = "right_vertical_back_swipe_action"; + + /** + * Gestures nav: right long back swipe app action + * @hide + */ + public static final String RIGHT_VERTICAL_BACK_SWIPE_APP_ACTION = "right_vertical_back_swipe_app_action"; + + /** + * Display friendly name of custom application launched when + * using the long right back swipe app action + * @hide + */ + public static final String RIGHT_VERTICAL_BACK_SWIPE_APP_FR_ACTION = "right_vertical_back_swipe_app_fr_action"; + + /** + * Launch custom activity when using the long right back swipe app action + * + * @hide + */ + public static final String RIGHT_VERTICAL_BACK_SWIPE_APP_ACTIVITY_ACTION = "right_vertical_back_swipe_app_activity_action"; + + + /** + * Settings Base Layout + * @hide + */ + public static final String ALT_SETTINGS_LAYOUT = "alt_settings_layout"; + + /** + * Settings Homepage Layout + * @hide + */ + public static final String USE_STOCK_LAYOUT = "use_stock_layout"; + + /** + * Settings UserCard + * @hide + */ + public static final String DISABLE_USERCARD = "disable_usercard"; + + /** + * Force full screen for devices with cutout + * @hide + */ + public static final String FORCE_FULLSCREEN_CUTOUT_APPS = "force_full_screen_cutout_apps"; + + + /** + * Whether to show VoWiFi icon or not + * @hide + */ + public static final String VOWIFI_ICON = "vowifi_icon"; + + /** + * VoWiFi icon style + * @hide + */ + public static final String VOWIFI_ICON_STYLE = "vowifi_icon_style"; + + /** + * Whether StatusBar icons should use app icon + * @hide + */ + public static final String STATUSBAR_COLORED_ICONS = "statusbar_colored_icons"; + + /** + * Show the pending notification counts as overlays on the status bar + * @hide + */ + public static final String STATUSBAR_NOTIF_COUNT = "statusbar_notif_count"; + + /** + * Enable/disable Bluetooth Battery bar + * @hide + */ + public static final String BLUETOOTH_SHOW_BATTERY = "bluetooth_show_battery"; + + /** + * Whether the torch launch gesture to double tap or long press the power button when the + * screen is off should be enabled. * + * 0: disabled + * 1: double tap power for torch + * 2: long tap power for torch + * @hide + */ + public static final String TORCH_POWER_BUTTON_GESTURE = "torch_power_button_gesture"; + + /** + * Change quick settings tiles animation style + * + * @hide + */ + public static final String ANIM_TILE_STYLE = "anim_tile_style"; + + /** + * Change quick settings tiles animation duration + * + * @hide + */ + public static final String ANIM_TILE_DURATION = "anim_tile_duration"; + + /** + * Change quick settings tiles interpolator + * + * @hide + */ + public static final String ANIM_TILE_INTERPOLATOR = "anim_tile_interpolator"; + + /** + * Transparent power menu and dialogs + * @hide + */ + public static final String TRANSPARENT_POWER_MENU = "transparent_power_menu"; + + /** + * Dim amount around power/reboot menu dialogs + * @hide + */ + public static final String TRANSPARENT_POWER_DIALOG_DIM = "transparent_power_dialog_dim"; + + /** + * Whether to display set of animations when prompting the power menu + * + * @hide + */ + public static final String POWER_MENU_ANIMATIONS = "power_menu_animations"; + + /** + * @hide + */ + public static final String VOLUME_MEDIA_OUTPUT_TOGGLE = "volume_media_output_toggle"; + + /** + * Show app volume rows in volume panel + * @hide + */ + public static final String SHOW_APP_VOLUME = "show_app_volume"; + + /** + * Whether auto brightness is applied one shot when screen is turned on. + * Default 0 + * @hide + */ + public static final String AUTO_BRIGHTNESS_ONE_SHOT = "auto_brightness_one_shot"; + + /** + * Whether to play haptic feedback when touching udfps icon + * @hide + */ + public static final String UDFPS_HAPTIC_FEEDBACK = "udfps_haptic_feedback"; + + /** + * Whether to enable or disable vibration on succesful fingerprint auth + * @hide + */ + public static final String UDFPS_SUCCESS_VIBRATE = "udfps_success_vib"; + + /** + * Whether to enable or disable vibration on failed fingerprint auth + * @hide + */ + public static final String UDFPS_ERROR_VIBRATE = "udfps_error_vib"; + + /** + * @hide + */ + public static final String SMART_CHARGING = "smart_charging"; + + /** + * @hide + */ + public static final String SMART_CHARGING_RESET_STATS = "smart_charging_reset_stats"; + + /** + * @hide + */ + public static final String SMART_CHARGING_LEVEL = "smart_charging_level"; + + /** + * @hide + */ + public static final String SMART_CHARGING_RESUME_LEVEL = "smart_charging_resume_level"; + + /** + * @hide + */ + public static final String QS_PANEL_STYLE = "qs_panel_style"; + + /** + * @hide + */ + public static final String QS_LAYOUT_COLUMNS_LANDSCAPE = "qs_layout_columns_landscape"; + + /** + * @hide + */ + public static final String QS_LAYOUT_COLUMNS = "qs_layout_columns"; + + /** + * @hide + */ + public static final String QS_TILE_VERTICAL_LAYOUT = "qs_tile_vertical_layout"; + + /** + * @hide + */ + public static final String QS_TILE_LABEL_HIDE = "qs_tile_label_hide"; + + /** + * @hide + */ + public static final String QS_TILE_LABEL_SIZE = "qs_tile_label_size"; + + /** + * Force show navigation bar setting. + * @hide + */ + public static final String FORCE_SHOW_NAVBAR = "force_show_navbar"; + + /** + * @hide + */ + public static final String RECENTS_LOCKED_TASKS = "recents_locked_tasks"; + + /** + * Whether to show IME space + * @hide + */ + public static final String HIDE_IME_SPACE_ENABLE = "hide_ime_space_enable"; + + /** + * Whether the button backlight is only lit when pressed (and not when screen is touched) + * The value is boolean (1 or 0). + * @hide + */ + public static final String BUTTON_BACKLIGHT_ONLY_WHEN_PRESSED = + "button_backlight_only_when_pressed"; + + /** + * QS weather temperature + * 0: Hide the temperature + * 1: Display the temperature with scale and image + * 2: Display the temperature without scale and with image + * 3: Display the temparature with scale and without image + * 4: Display the temperature without scale and without image + * 5: Display the temperature image + * @hide + */ + public static final String QS_SHOW_WEATHER_TEMP = "qs_show_weather_temp"; - /** - * Whether or not to enable multiple audio focus. - * When enabled, requires more management by user over application playback activity, - * for instance pausing media apps when another starts. + /** * @hide */ - @Readable - public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled"; + public static final String OMNIJAWS_WEATHER_ICON_PACK = "omnijaws_weather_icon_pack"; /** - * IMPORTANT: If you add a new public settings you also have to add it to - * PUBLIC_SETTINGS below. If the new setting is hidden you have to add - * it to PRIVATE_SETTINGS below. Also add a validator that can validate - * the setting value. See an example above. + * Date font size QS + * @hide */ + public static final String QS_WEATHER_POSITION = "qs_weather_position"; /** * Keys we no longer back up under the current schema, but want to continue to @@ -5607,9 +6825,35 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean PRIVATE_SETTINGS.add(POINTER_SPEED); PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED); PRIVATE_SETTINGS.add(EGG_MODE); - PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT); PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE); PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_ENABLED); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_ALLOW_ON_DND); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_LOW_BLINKING); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_LOW_COLOR); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_MEDIUM_COLOR); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_FULL_COLOR); + PRIVATE_SETTINGS.add(BATTERY_LIGHT_REALLYFULL_COLOR); + PRIVATE_SETTINGS.add(VIBRATE_ON_CONNECT); + PRIVATE_SETTINGS.add(VIBRATE_ON_CALLWAITING); + PRIVATE_SETTINGS.add(VIBRATE_ON_DISCONNECT); + PRIVATE_SETTINGS.add(HEADSET_CONNECT_PLAYER); + PRIVATE_SETTINGS.add(VOLTE_ICON_STYLE); + PRIVATE_SETTINGS.add(INCREASING_RING); + PRIVATE_SETTINGS.add(INCREASING_RING_START_VOLUME); + PRIVATE_SETTINGS.add(INCREASING_RING_RAMP_UP_TIME); + PRIVATE_SETTINGS.add(NAV_BAR_COMPACT_LAYOUT); + PRIVATE_SETTINGS.add(VOLUME_KEY_CURSOR_CONTROL); + PRIVATE_SETTINGS.add(VOLUME_ROCKER_WAKE); + PRIVATE_SETTINGS.add(POCKET_JUDGE); + PRIVATE_SETTINGS.add(HIGH_TOUCH_POLLING_RATE_ENABLE); + PRIVATE_SETTINGS.add(HIGH_TOUCH_SENSITIVITY_ENABLE); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_CHIP); + PRIVATE_SETTINGS.add(FORCE_FULLSCREEN_CUTOUT_APPS); + PRIVATE_SETTINGS.add(TRANSPARENT_POWER_MENU); + PRIVATE_SETTINGS.add(TRANSPARENT_POWER_DIALOG_DIM); + PRIVATE_SETTINGS.add(BUTTON_BACKLIGHT_ONLY_WHEN_PRESSED); + PRIVATE_SETTINGS.add(OMNIJAWS_WEATHER_ICON_PACK); } /** @@ -8849,6 +10093,28 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String DOZE_ALWAYS_ON = "doze_always_on"; + /** + * Indicates whether doze turns on automatically + * 0 = disabled (default) + * 1 = from sunset to sunrise + * 2 = custom time + * 3 = from sunset till a time + * 4 = from a time till sunrise + * @hide + */ + @Readable + public static final String DOZE_ALWAYS_ON_AUTO_MODE = "doze_always_on_auto_mode"; + + /** + * The custom time {@link DOZE_ALWAYS_ON} should be on at + * Only relevant when {@link DOZE_ALWAYS_ON_AUTO_MODE} is set to 2 and above + * 0 = Disabled (default) + * format: HH:mm,HH:mm (since,till) + * @hide + */ + @Readable + public static final String DOZE_ALWAYS_ON_AUTO_TIME = "doze_always_on_auto_time"; + /** * Whether the device should pulse on pick up gesture. * @hide @@ -10475,6 +11741,12 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String TAP_GESTURE = "tap_gesture"; + /** + * Whether tethering is allowed to use VPN upstreams + */ + @SuppressLint("NoSettingsProvider") + public static final String TETHERING_ALLOW_VPN_UPSTREAMS = "tethering_allow_vpn_upstreams"; + /** * Controls whether the people strip is enabled. * @hide @@ -10672,6 +11944,197 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val */ public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; + /** + * Whether to show media art on lockscreen + * Boolean setting. 0 = off, 1 = on. + * @hide + */ + public static final String LOCKSCREEN_MEDIA_METADATA = "lockscreen_media_metadata"; + + /** + * Volume panel on left + * @hide + */ + public static final String VOLUME_PANEL_ON_LEFT = "volume_panel_on_left"; + + /** + * Boolean value whether to link ringtone and notification volume + * @hide + */ + public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + + /** + * Whether to show or hide the arrow for back gesture + * @hide + */ + public static final String SHOW_BACK_ARROW_GESTURE = "show_back_arrow_gesture"; + + /** + * Whether to turn on black theme + * @hide + */ + public static final String SYSTEM_CUSTOM_THEME = "system_custom_theme"; + + /** + * Whether to show privacy indicator for location + * @hide + */ + public static final String ENABLE_LOCATION_PRIVACY_INDICATOR = "enable_location_privacy_indicator"; + + /** + * Whether to show privacy indicator for camera + * @hide + */ + public static final String ENABLE_CAMERA_PRIVACY_INDICATOR = "enable_camera_privacy_indicator"; + + /** + * Whether to show privacy indicator for media projection + * @hide + */ + public static final String ENABLE_PROJECTION_PRIVACY_INDICATOR = "enable_projection_privacy_indicator"; + + /** + * Change fonts for the system lockscreen date + * + * @hide + */ + public static final String KG_CUSTOM_DATE_FONT = "kg_custom_date_font"; + + /** + * Pulse navbar music visualizer + * @hide + */ + public static final String NAVBAR_PULSE_ENABLED = "navbar_pulse_enabled"; + + /** + * Pulse ambient music visualizer + * @hide + */ + public static final String AMBIENT_PULSE_ENABLED = "ambient_pulse_enabled"; + + /** + * Pulse lockscreen music visualizer + * @hide + */ + public static final String LOCKSCREEN_PULSE_ENABLED = "lockscreen_pulse_enabled"; + + /** + * Pulse navbar music visualizer color type + * @hide + */ + public static final String PULSE_COLOR_MODE = "pulse_color_mode"; + + /** + * Pulse music visualizer user defined color + * @hide + */ + public static final String PULSE_COLOR_USER = "pulse_color_user"; + + /** + * Pulse lavalamp animation speed + * @hide + */ + public static final String PULSE_LAVALAMP_SPEED = "pulse_lavalamp_speed"; + + /** + * Pulse renderer implementation + * @hide + */ + public static final String PULSE_RENDER_STYLE = "pulse_render_style"; + + /** + * Custom Pulse Widths + * @hide + */ + public static final String PULSE_CUSTOM_DIMEN = "pulse_custom_dimen"; + + /** + * Custom Spacing Between Pulse Bars + * @hide + */ + public static final String PULSE_CUSTOM_DIV = "pulse_custom_div"; + + /** + * Custom Pulse Block Size + * @hide + */ + public static final String PULSE_FILLED_BLOCK_SIZE = "pulse_filled_block_size"; + + /** + * Custom Spacing Between Pulse Blocks + * @hide + */ + public static final String PULSE_EMPTY_BLOCK_SIZE = "pulse_empty_block_size"; + + /** + * Custom Pulse Sanity Levels + * @hide + */ + public static final String PULSE_CUSTOM_FUDGE_FACTOR = "pulse_custom_fudge_factor"; + + /** + * Pulse Fudge Factor + * @hide + */ + public static final String PULSE_SOLID_FUDGE_FACTOR = "pulse_solid_fudge_factor"; + + /** + * Pulse Solid units count + * @hide + */ + public static final String PULSE_SOLID_UNITS_COUNT = "pulse_solid_units_count"; + + /** + * Pulse Solid units opacity + * @hide + */ + public static final String PULSE_SOLID_UNITS_OPACITY = "pulse_solid_units_opacity"; + + /** + * Pulse Solid units rounded + * @hide + */ + public static final String PULSE_SOLID_UNITS_ROUNDED = "pulse_solid_units_rounded"; + + /** + * Pulse uses FFT averaging + * @hide + */ + public static final String PULSE_SMOOTHING_ENABLED = "pulse_smoothing_enabled"; + + /** + * @hide + */ + public static final String KG_CUSTOM_CLOCK_COLOR = "kg_custom_clock_color"; + + /** + * @hide + */ + public static final String KG_CUSTOM_CLOCK_COLOR_ENABLED = "kg_custom_clock_color_enabled"; + + /** + * @hide + */ + public static final String KG_CUSTOM_CLOCK_FONT = "kg_custom_clock_font"; + + /** + * Pulse gravity + * @hide + */ + public static final String PULSE_CUSTOM_GRAVITY = "pulse_custom_gravity"; + + /** + * Enable and disable QS Panel visualizer + * @hide + */ + public static final String VISUALIZER_CENTER_MIRRORED = "visualizer_center_mirrored"; + + /** + * Pulse vertical mirror + * @hide + */ + public static final String PULSE_VERTICAL_MIRROR = "pulse_vertical_mirror"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -10726,6 +12189,27 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String GAME_DASHBOARD_ALWAYS_ON = "game_dashboard_always_on"; + /** + * The time in ms to keep the button backlight on after pressing a button. + * A value of 0 will keep the buttons on for as long as the screen is on. + * @hide + */ + public static final String BUTTON_BACKLIGHT_TIMEOUT = "button_backlight_timeout"; + + /** + * The button brightness to be used while the screen is on or after a button press, + * depending on the value of {@link BUTTON_BACKLIGHT_TIMEOUT}. + * Valid value range is between 0 and {@link PowerManager#getMaximumButtonBrightness()} + * @hide + */ + public static final String BUTTON_BRIGHTNESS = "button_brightness"; + + /** + * The keyboard brightness to be used while the screen is on. + * Valid value range is between 0 and {@link PowerManager#getMaximumKeyboardBrightness()} + * @hide + */ + public static final String KEYBOARD_BRIGHTNESS = "keyboard_brightness"; /** * For this device state, no specific auto-rotation lock setting should be applied. @@ -10781,6 +12265,19 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String DEVICE_STATE_ROTATION_LOCK = "device_state_rotation_lock"; + /** + * Face Unlock Method + * @hide + */ + public static final String FACE_UNLOCK_METHOD = "face_unlock_method"; + + /** + * Toggle the media squiggle animation + * 0 = 0ff, 1 = on + * @hide + */ + public static final String SHOW_MEDIA_SQUIGGLE_ANIMATION = "show_media_squiggle_animation"; + /** * Control whether communal mode is allowed on this device. * @@ -10819,6 +12316,136 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String LAUNCHER_TASKBAR_EDUCATION_SHOWING = "launcher_taskbar_education_showing"; + /** + * Whether UDFPS is active while the screen is off. + * + *

1 if true, 0 or unset otherwise. + * + * @hide + */ + public static final String SCREEN_OFF_UDFPS_ENABLED = "screen_off_udfps_enabled"; + + /** + * Gesture navbar length mode. + * Supported modes: 0 for hidden, 1 for normal length, 2 for medium and 3 for long. + * Default 0. + * @hide + */ + public static final String GESTURE_NAVBAR_LENGTH_MODE = "gesture_navbar_length_mode"; + + /** + * Whether to disable qs on secure lockscreen. + * Default 0 + * @hide + */ + public static final String SECURE_LOCKSCREEN_QS_DISABLED = "secure_lockscreen_qs_disabled"; + + /** + * Whether to show the brightness slider in quick settings panel. + * @hide + */ + public static final String QS_SHOW_BRIGHTNESS_SLIDER = "qs_show_brightness_slider"; + + /** + * Whether to show the brightness slider in quick settings panel. + * 0 = Top, 1 = Bottom + * @hide + */ + public static final String QS_BRIGHTNESS_SLIDER_POSITION = "qs_brightness_slider_position"; + + /** + * Whether to show the auto brightness icon in quick settings panel. + * @hide + */ + public static final String QS_SHOW_AUTO_BRIGHTNESS = "qs_show_auto_brightness"; + + /** + * Control whether FLAG_SECURE is ignored for all windows. + * @hide + */ + @Readable + public static final String WINDOW_IGNORE_SECURE = "window_ignore_secure"; + + /** + * Whether touch hovering is enabled on supported hardware + * @hide + */ + public static final String FEATURE_TOUCH_HOVERING = "feature_touch_hovering"; + + /** + * Whether key swap is enabled on supported hardware + * @hide + */ + public static final String SWAP_CAPACITIVE_KEYS = "swap_capacitive_keys"; + + /** + * Whether to enable DOZE only when charging + * @hide + */ + public static final String DOZE_ON_CHARGE = "doze_on_charge"; + + /** + * Whether to enable Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_ENABLED = "sleep_mode_enabled"; + + /** + * Indicates whether Sleep Mode turns on automatically + * 0 = disabled (default) + * 1 = from sunset to sunrise + * 2 = custom time + * 3 = from sunset till a time + * 4 = from a time till sunrise + * @hide + */ + public static final String SLEEP_MODE_AUTO_MODE = "sleep_mode_auto_mode"; + + /** + * The custom time {@link SLEEP_MODE_ENABLED} should be on at + * Only relevant when {@link SLEEP_MODE_AUTO_MODE} is set to 2 and above + * 0 = Disabled (default) + * format: HH:mm,HH:mm (since,till) + * @hide + */ + public static final String SLEEP_MODE_AUTO_TIME = "sleep_mode_auto_time"; + + /** + * Whether to disable Wi-Fi in Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_WIFI_TOGGLE = "sleep_mode_wifi_toggle"; + + /** + * Whether to disable Bluetooth in Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_BLUETOOTH_TOGGLE = "sleep_mode_bluetooth_toggle"; + + /** + * Whether to disable Mobile data in Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_CELLULAR_TOGGLE = "sleep_mode_cellular_toggle"; + + /** + * Whether to disable Location in Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_LOCATION_TOGGLE = "sleep_mode_location_toggle"; + + /** + * Whether to disable Sensors in Sleep Mode + * @hide + */ + public static final String SLEEP_MODE_SENSORS_TOGGLE = "sleep_mode_sensors_toggle"; + + /** + * Ringer in Sleep Mode (0:OFF, 1:Vibrate, 2:DND, 3:Silent) + * @hide + */ + public static final String SLEEP_MODE_RINGER_MODE = "sleep_mode_ringer_mode"; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. @@ -18096,6 +19723,24 @@ private Panel() { @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VOLUME = "android.settings.panel.action.VOLUME"; + + /** + * Activity Action: Show a settings dialog containing controls for Mobile Data. + *

+ * Input: Nothing. + *

+ * Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MOBILE_DATA = + "android.settings.panel.action.MOBILE_DATA"; + + /** + * @hide + */ + public static final String ACTION_APP_VOLUME = + "android.settings.panel.action.APP_VOLUME"; } /** diff --git a/core/java/android/security/net/config/PinSet.java b/core/java/android/security/net/config/PinSet.java index d3c975eb31014..ff66b6c3be496 100644 --- a/core/java/android/security/net/config/PinSet.java +++ b/core/java/android/security/net/config/PinSet.java @@ -17,8 +17,10 @@ package android.security.net.config; import android.util.ArraySet; + import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; /** @hide */ public final class PinSet { @@ -26,6 +28,7 @@ public final class PinSet { new PinSet(Collections.emptySet(), Long.MAX_VALUE); public final long expirationTime; public final Set pins; + private final Set algorithms; public PinSet(Set pins, long expirationTime) { if (pins == null) { @@ -33,14 +36,12 @@ public PinSet(Set pins, long expirationTime) { } this.pins = pins; this.expirationTime = expirationTime; + this.algorithms = pins.stream() + .map(pin -> pin.digestAlgorithm) + .collect(Collectors.toCollection(ArraySet::new)); } Set getPinAlgorithms() { - // TODO: Cache this. - Set algorithms = new ArraySet(); - for (Pin pin : pins) { - algorithms.add(pin.digestAlgorithm); - } return algorithms; } } diff --git a/core/java/android/service/autofill/OptionalValidators.java b/core/java/android/service/autofill/OptionalValidators.java index 7189c886aa069..204353942b03a 100644 --- a/core/java/android/service/autofill/OptionalValidators.java +++ b/core/java/android/service/autofill/OptionalValidators.java @@ -25,6 +25,8 @@ import com.android.internal.util.Preconditions; +import java.util.Arrays; + /** * Compound validator that returns {@code true} on {@link #isValid(ValueFinder)} if any * of its subvalidators returns {@code true} as well. @@ -61,7 +63,8 @@ public boolean isValid(@NonNull ValueFinder finder) { public String toString() { if (!sDebug) return super.toString(); - return new StringBuilder("OptionalValidators: [validators=").append(mValidators) + return new StringBuilder("OptionalValidators: [validators=") + .append(Arrays.toString(mValidators)) .append("]") .toString(); } diff --git a/core/java/android/service/autofill/RequiredValidators.java b/core/java/android/service/autofill/RequiredValidators.java index 619eba0a79ade..054582e01f71d 100644 --- a/core/java/android/service/autofill/RequiredValidators.java +++ b/core/java/android/service/autofill/RequiredValidators.java @@ -25,6 +25,8 @@ import com.android.internal.util.Preconditions; +import java.util.Arrays; + /** * Compound validator that only returns {@code true} on {@link #isValid(ValueFinder)} if all * of its subvalidators return {@code true} as well. @@ -60,7 +62,8 @@ public boolean isValid(@NonNull ValueFinder finder) { public String toString() { if (!sDebug) return super.toString(); - return new StringBuilder("RequiredValidators: [validators=").append(mValidators) + return new StringBuilder("RequiredValidators: [validators=") + .append(Arrays.toString(mValidators)) .append("]") .toString(); } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 66188cd197210..f8dfef59ad33c 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -71,10 +71,13 @@ public class StatusBarNotification implements Parcelable { private Context mContext; // used for inflation & icon expansion + private boolean mIsContentSecure; + /** @hide */ public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, int initialPid, Notification notification, UserHandle user, - String overrideGroupKey, long postTime) { + String overrideGroupKey, long postTime, + boolean isContentSecure) { if (pkg == null) throw new NullPointerException(); if (notification == null) throw new NullPointerException(); @@ -90,6 +93,7 @@ public StatusBarNotification(String pkg, String opPkg, int id, this.overrideGroupKey = overrideGroupKey; this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = isContentSecure; } /** @@ -137,6 +141,7 @@ public StatusBarNotification(Parcel in) { } this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = in.readBoolean(); } /** @@ -237,6 +242,7 @@ public void writeToParcel(Parcel out, int flags) { } else { out.writeInt(0); } + out.writeBoolean(mIsContentSecure); } public int describeContents() { @@ -276,7 +282,8 @@ public StatusBarNotification clone() { StatusBarNotification cloneShallow(Notification notification) { StatusBarNotification result = new StatusBarNotification(this.pkg, this.opPkg, this.id, this.tag, this.uid, this.initialPid, - notification, this.user, this.overrideGroupKey, this.postTime); + notification, this.user, this.overrideGroupKey, + this.postTime, mIsContentSecure); result.setInstanceId(this.mInstanceId); return result; } @@ -550,4 +557,23 @@ private String shortenTag(String logTag) { return logTag.substring(0, MAX_LOG_TAG_LENGTH - hash.length() - 1) + "-" + hash; } + + /** + * Set whether the notification content is secure. + * + * @param isContentSecure whether the content is secure. + * @hide + */ + public void setIsContentSecure(boolean isContentSecure) { + mIsContentSecure = isContentSecure; + } + + /** + * Check whether the notification content is secure. + * + * @return true if content is secure, false otherwise. + */ + public boolean getIsContentSecure() { + return mIsContentSecure; + } } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index b783f6b8fd51b..634b86a1d44de 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -73,6 +73,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -597,7 +598,7 @@ void sendPickOptionResult(boolean finished, VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface - + " finished=" + finished + " selections=" + selections + + " finished=" + finished + " selections=" + Arrays.toString(selections) + " result=" + result); if (finished) { finishRequest(); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 1e22856c1bde0..77c7a85b69128 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -2363,7 +2363,7 @@ public void executeMessage(Message message) { return; } case MSG_UPDATE_SURFACE: - mEngine.updateSurface(true, false, false); + mEngine.updateSurface(true, false, true/*false*/); break; case MSG_ZOOM: mEngine.setZoom(Float.intBitsToFloat(message.arg1)); diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java index 3bde32b1e5f2b..3177c5c8509e1 100644 --- a/core/java/android/speech/tts/FileSynthesisCallback.java +++ b/core/java/android/speech/tts/FileSynthesisCallback.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; +import java.util.Arrays; /** * Speech synthesis request that writes the audio to a WAV file. @@ -152,8 +153,8 @@ public int start(int sampleRateInHz, int audioFormat, int channelCount) { @Override public int audioAvailable(byte[] buffer, int offset, int length) { if (DBG) { - Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset - + "," + length + ")"); + Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + Arrays.toString(buffer) + + "," + offset + "," + length + ")"); } FileChannel fileChannel = null; synchronized (mStateLock) { diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index a8aea7c1eb599..ef63c8ff751d3 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -507,7 +507,7 @@ private static String parseEnginePrefFromList(String prefValue, String engineNam for (String value : prefValues) { final int delimiter = value.indexOf(':'); if (delimiter > 0) { - if (engineName.equals(value.substring(0, delimiter))) { + if ((value.substring(0, delimiter)).equals(engineName)) { return value.substring(delimiter + 1); } } @@ -560,7 +560,7 @@ private String updateValueInCommaSeparatedList(String list, String key, for (String value : prefValues) { final int delimiter = value.indexOf(':'); if (delimiter > 0) { - if (key.equals(value.substring(0, delimiter))) { + if (value.substring(0, delimiter).equals(key)) { if (first) { first = false; } else { diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index c0bc9912af3db..155f508f62425 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -23,6 +23,7 @@ import libcore.util.EmptyArray; +import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Map; @@ -231,7 +232,7 @@ private void allocArrays(final int size) { array[0] = array[1] = null; mTwiceBaseCacheSize--; if (DEBUG) { - Log.d(TAG, "Retrieving 2x cache " + mHashes + Log.d(TAG, "Retrieving 2x cache " + Arrays.toString(mHashes) + " now have " + mTwiceBaseCacheSize + " entries"); } return; @@ -258,7 +259,7 @@ private void allocArrays(final int size) { array[0] = array[1] = null; mBaseCacheSize--; if (DEBUG) { - Log.d(TAG, "Retrieving 1x cache " + mHashes + Log.d(TAG, "Retrieving 1x cache " + Arrays.toString(mHashes) + " now have " + mBaseCacheSize + " entries"); } return; @@ -295,8 +296,10 @@ private static void freeArrays(final int[] hashes, final Object[] array, final i } mTwiceBaseCache = array; mTwiceBaseCacheSize++; - if (DEBUG) Log.d(TAG, "Storing 2x cache " + array - + " now have " + mTwiceBaseCacheSize + " entries"); + if (DEBUG) { + Log.d(TAG, "Storing 2x cache " + Arrays.toString(array) + + " now have " + mTwiceBaseCacheSize + " entries"); + } } } } else if (hashes.length == BASE_SIZE) { @@ -309,8 +312,10 @@ private static void freeArrays(final int[] hashes, final Object[] array, final i } mBaseCache = array; mBaseCacheSize++; - if (DEBUG) Log.d(TAG, "Storing 1x cache " + array - + " now have " + mBaseCacheSize + " entries"); + if (DEBUG) { + Log.d(TAG, "Storing 1x cache " + Arrays.toString(array) + + " now have " + mBaseCacheSize + " entries"); + } } } } diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index b5c75b9276b70..73114e26e4178 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -23,6 +23,7 @@ import libcore.util.EmptyArray; import java.lang.reflect.Array; +import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; @@ -194,8 +195,8 @@ private void allocArrays(final int size) { array[0] = array[1] = null; sTwiceBaseCacheSize--; if (DEBUG) { - Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have " - + sTwiceBaseCacheSize + " entries"); + Log.d(TAG, "Retrieving 2x cache " + Arrays.toString(mHashes) + + " now have " + sTwiceBaseCacheSize + " entries"); } return; } @@ -221,8 +222,8 @@ private void allocArrays(final int size) { array[0] = array[1] = null; sBaseCacheSize--; if (DEBUG) { - Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have " - + sBaseCacheSize + " entries"); + Log.d(TAG, "Retrieving 1x cache " + Arrays.toString(mHashes) + + " now have " + sBaseCacheSize + " entries"); } return; } @@ -259,8 +260,8 @@ private static void freeArrays(final int[] hashes, final Object[] array, final i sTwiceBaseCache = array; sTwiceBaseCacheSize++; if (DEBUG) { - Log.d(TAG, "Storing 2x cache " + array + " now have " + sTwiceBaseCacheSize - + " entries"); + Log.d(TAG, "Storing 2x cache " + Arrays.toString(array) + " now have " + + sTwiceBaseCacheSize + " entries"); } } } @@ -275,7 +276,7 @@ private static void freeArrays(final int[] hashes, final Object[] array, final i sBaseCache = array; sBaseCacheSize++; if (DEBUG) { - Log.d(TAG, "Storing 1x cache " + array + " now have " + Log.d(TAG, "Storing 1x cache " + Arrays.toString(array) + " now have " + sBaseCacheSize + " entries"); } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index d2a18dd843135..f2a7a90d5bb45 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -512,9 +512,6 @@ private static void closeQuietly(StrictJarFile jarFile) { * {@code targetSdk}. */ public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) { - if (targetSdk >= Build.VERSION_CODES.R) { - return SignatureSchemeVersion.SIGNING_BLOCK_V2; - } return SignatureSchemeVersion.JAR; } diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java index 9789b10a0a617..9a15cd5dbfff3 100644 --- a/core/java/android/util/proto/ProtoInputStream.java +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; /** * Class to read to a protobuf stream. @@ -968,26 +970,17 @@ private void assertFreshData() { public String dumpDebugData() { StringBuilder sb = new StringBuilder(); - sb.append("\nmFieldNumber : 0x" + Integer.toHexString(mFieldNumber)); - sb.append("\nmWireType : 0x" + Integer.toHexString(mWireType)); - sb.append("\nmState : 0x" + Integer.toHexString(mState)); - sb.append("\nmDiscardedBytes : 0x" + Integer.toHexString(mDiscardedBytes)); - sb.append("\nmOffset : 0x" + Integer.toHexString(mOffset)); - sb.append("\nmExpectedObjectTokenStack : "); - if (mExpectedObjectTokenStack == null) { - sb.append("null"); - } else { - sb.append(mExpectedObjectTokenStack); - } - sb.append("\nmDepth : 0x" + Integer.toHexString(mDepth)); - sb.append("\nmBuffer : "); - if (mBuffer == null) { - sb.append("null"); - } else { - sb.append(mBuffer); - } - sb.append("\nmBufferSize : 0x" + Integer.toHexString(mBufferSize)); - sb.append("\nmEnd : 0x" + Integer.toHexString(mEnd)); + sb.append("\nmFieldNumber : 0x").append(Integer.toHexString(mFieldNumber)); + sb.append("\nmWireType : 0x").append(Integer.toHexString(mWireType)); + sb.append("\nmState : 0x").append(Integer.toHexString(mState)); + sb.append("\nmDiscardedBytes : 0x").append(Integer.toHexString(mDiscardedBytes)); + sb.append("\nmOffset : 0x").append(Integer.toHexString(mOffset)); + sb.append("\nmExpectedObjectTokenStack : ") + .append(Objects.toString(mExpectedObjectTokenStack)); + sb.append("\nmDepth : 0x").append(Integer.toHexString(mDepth)); + sb.append("\nmBuffer : ").append(Arrays.toString(mBuffer)); + sb.append("\nmBufferSize : 0x").append(Integer.toHexString(mBufferSize)); + sb.append("\nmEnd : 0x").append(Integer.toHexString(mEnd)); return sb.toString(); } diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java index 8464d2d808d65..58d991370f56a 100644 --- a/core/java/android/util/proto/ProtoUtils.java +++ b/core/java/android/util/proto/ProtoUtils.java @@ -20,6 +20,7 @@ import android.util.Duration; import java.io.IOException; +import java.util.Arrays; /** * This class contains a list of helper functions to write common proto in @@ -91,27 +92,27 @@ public static String currentFieldToString(ProtoInputStream proto) throws IOExcep final int wireType = proto.getWireType(); long fieldConstant; - sb.append("Offset : 0x" + Integer.toHexString(proto.getOffset())); - sb.append("\nField Number : 0x" + Integer.toHexString(proto.getFieldNumber())); + sb.append("Offset : 0x").append(Integer.toHexString(proto.getOffset())); + sb.append("\nField Number : 0x").append(Integer.toHexString(proto.getFieldNumber())); sb.append("\nWire Type : "); switch (wireType) { case ProtoStream.WIRE_TYPE_VARINT: - sb.append("varint"); fieldConstant = ProtoStream.makeFieldId(fieldNumber, ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64); - sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant))); + sb.append("varint\nField Value : 0x"); + sb.append(Long.toHexString(proto.readLong(fieldConstant))); break; case ProtoStream.WIRE_TYPE_FIXED64: - sb.append("fixed64"); fieldConstant = ProtoStream.makeFieldId(fieldNumber, ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64); - sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant))); + sb.append("fixed64\nField Value : 0x"); + sb.append(Long.toHexString(proto.readLong(fieldConstant))); break; case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED: - sb.append("length delimited"); fieldConstant = ProtoStream.makeFieldId(fieldNumber, ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES); - sb.append("\nField Bytes : " + proto.readBytes(fieldConstant)); + sb.append("length delimited\nField Bytes : "); + sb.append(Arrays.toString(proto.readBytes(fieldConstant))); break; case ProtoStream.WIRE_TYPE_START_GROUP: sb.append("start group"); @@ -120,13 +121,13 @@ public static String currentFieldToString(ProtoInputStream proto) throws IOExcep sb.append("end group"); break; case ProtoStream.WIRE_TYPE_FIXED32: - sb.append("fixed32"); fieldConstant = ProtoStream.makeFieldId(fieldNumber, ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32); - sb.append("\nField Value : 0x" + Integer.toHexString(proto.readInt(fieldConstant))); + sb.append("fixed32\nField Value : 0x"); + sb.append(Integer.toHexString(proto.readInt(fieldConstant))); break; default: - sb.append("unknown(" + proto.getWireType() + ")"); + sb.append("unknown(").append(proto.getWireType()).append(")"); } return sb.toString(); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 52d222b19b6ac..c9ebc8c24d218 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -56,6 +56,8 @@ import java.util.Arrays; import java.util.List; +import android.app.ActivityThread; + /** * Provides information about the size and density of a logical display. *

@@ -1479,6 +1481,15 @@ public void getRealSize(Point outSize) { if (shouldReportMaxBounds()) { final Rect bounds = mResources.getConfiguration() .windowConfiguration.getMaxBounds(); + if (ActivityThread.applyDssScaleIfNeeded(mDisplayInfo, bounds)) { + outSize.x = mDisplayInfo.logicalWidth; + outSize.y = mDisplayInfo.logicalHeight; + if (DEBUG) { + Log.d(TAG, "getRealSize determined from max bounds with DSS scale: " + + outSize); + } + return; + } outSize.x = bounds.width(); outSize.y = bounds.height(); if (DEBUG) { @@ -1548,6 +1559,16 @@ public void getRealMetrics(DisplayMetrics outMetrics) { synchronized (mLock) { updateDisplayInfoLocked(); if (shouldReportMaxBounds()) { + if (ActivityThread.applyDssScaleIfNeeded(mDisplayInfo, + mResources.getConfiguration().windowConfiguration.getMaxBounds())) { + mDisplayInfo.getLogicalMetrics(outMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + if (DEBUG) { + Log.d(TAG, "getRealMetrics determined from max bounds with DSS scale: " + + outMetrics); + } + return; + } mDisplayInfo.getMaxBoundsMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, mResources.getConfiguration()); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index fb562d8e97db8..30057ddf0ddbc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -22,6 +22,7 @@ import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import android.app.IAssistDataReceiver; +import android.content.Intent; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -398,6 +399,11 @@ interface IWindowManager */ oneway void hideTransientBars(int displayId); + /** + * Send some ActionHandler commands to WindowManager. + */ + void sendCustomAction(in Intent intent); + /** * Called by System UI to notify of changes to the visibility of Recents. */ @@ -955,4 +961,9 @@ interface IWindowManager * means the recents app can control the SystemUI flags, and vice-versa. */ void setRecentsAppBehindSystemBars(boolean behindSystemBars); + + /** + * Notifies DisplayPolicy that overlays changed. + */ + void onOverlayChanged(); } diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 7d5603994efaf..b96440e20a199 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -337,6 +337,15 @@ public final class InputDevice implements Parcelable { */ public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE; + /** + * The input source is a specific virtual event sent from navigation bar. + * + * @see com.android.systemui.statusbar.policy.KeyButtonView#sendEvent() + * @author Carlo Savignano + * @hide + */ + public static final int SOURCE_NAVIGATION_BAR = 0x08000000 | SOURCE_CLASS_BUTTON; + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index df78827534a61..cd2ea43112648 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -804,67 +804,75 @@ public final View createView(@NonNull Context viewContext, @NonNull String name, throws ClassNotFoundException, InflateException { Objects.requireNonNull(viewContext); Objects.requireNonNull(name); - Constructor constructor = sConstructorMap.get(name); - if (constructor != null && !verifyClassLoader(constructor)) { - constructor = null; - sConstructorMap.remove(name); - } + String prefixedName = prefix != null ? (prefix + name) : name; Class clazz = null; try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); - if (constructor == null) { - // Class not found in the cache, see if it's real, and try to add it - clazz = Class.forName(prefix != null ? (prefix + name) : name, false, - mContext.getClassLoader()).asSubclass(View.class); - - if (mFilter != null && clazz != null) { - boolean allowed = mFilter.onLoadClass(clazz); - if (!allowed) { - failNotAllowed(name, prefix, viewContext, attrs); - } + // Opportunistically create view directly instead of using reflection + View view = tryCreateViewDirect(prefixedName, viewContext, attrs); + if (view == null) { + Constructor constructor = sConstructorMap.get(name); + if (constructor != null && !verifyClassLoader(constructor)) { + constructor = null; + sConstructorMap.remove(name); } - constructor = clazz.getConstructor(mConstructorSignature); - constructor.setAccessible(true); - sConstructorMap.put(name, constructor); - } else { - // If we have a filter, apply it to cached constructor - if (mFilter != null) { - // Have we seen this name before? - Boolean allowedState = mFilterMap.get(name); - if (allowedState == null) { - // New class -- remember whether it is allowed - clazz = Class.forName(prefix != null ? (prefix + name) : name, false, - mContext.getClassLoader()).asSubclass(View.class); - - boolean allowed = clazz != null && mFilter.onLoadClass(clazz); - mFilterMap.put(name, allowed); + + if (constructor == null) { + // Class not found in the cache, see if it's real, and try to add it + clazz = Class.forName(prefixedName, false, + mContext.getClassLoader()).asSubclass(View.class); + + if (mFilter != null && clazz != null) { + boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, viewContext, attrs); } - } else if (allowedState.equals(Boolean.FALSE)) { - failNotAllowed(name, prefix, viewContext, attrs); + } + constructor = clazz.getConstructor(mConstructorSignature); + constructor.setAccessible(true); + sConstructorMap.put(name, constructor); + } else { + // If we have a filter, apply it to cached constructor + if (mFilter != null) { + // Have we seen this name before? + Boolean allowedState = mFilterMap.get(name); + if (allowedState == null) { + // New class -- remember whether it is allowed + clazz = Class.forName(prefixedName, false, + mContext.getClassLoader()).asSubclass(View.class); + + boolean allowed = clazz != null && mFilter.onLoadClass(clazz); + mFilterMap.put(name, allowed); + if (!allowed) { + failNotAllowed(name, prefix, viewContext, attrs); + } + } else if (allowedState.equals(Boolean.FALSE)) { + failNotAllowed(name, prefix, viewContext, attrs); + } } } - } - Object lastContext = mConstructorArgs[0]; - mConstructorArgs[0] = viewContext; - Object[] args = mConstructorArgs; - args[1] = attrs; + Object lastContext = mConstructorArgs[0]; + mConstructorArgs[0] = viewContext; + Object[] args = mConstructorArgs; + args[1] = attrs; - try { - final View view = constructor.newInstance(args); - if (view instanceof ViewStub) { - // Use the same context when inflating ViewStub later. - final ViewStub viewStub = (ViewStub) view; - viewStub.setLayoutInflater(cloneInContext((Context) args[0])); + try { + view = constructor.newInstance(args); + } finally { + mConstructorArgs[0] = lastContext; } - return view; - } finally { - mConstructorArgs[0] = lastContext; } + + if (view instanceof ViewStub) { + // Use the same context when inflating ViewStub later. + final ViewStub viewStub = (ViewStub) view; + viewStub.setLayoutInflater(cloneInContext((Context) viewContext)); + } + + return view; } catch (NoSuchMethodException e) { final InflateException ie = new InflateException( getParserStateDescription(viewContext, attrs) @@ -1363,4 +1371,121 @@ protected void dispatchDraw(Canvas canvas) { } } } + + // Some of the views included here are deprecated, but apps still use them. + @SuppressWarnings("deprecation") + private static View tryCreateViewDirect(String name, Context context, AttributeSet attributeSet) { + // This contains all the framework views used in a set of 113 real-world apps, sorted by + // number of occurrences. While views with only 1 occurrence are unlikely to be worth + // optimizing, it doesn't hurt to include them because switch-case is compiled into a table + // lookup after calling String#hashCode(). + switch (name) { + case "android.widget.LinearLayout": // 13486 occurrences + return new android.widget.LinearLayout(context, attributeSet); + case "android.widget.View": // 6930 occurrences + case "android.webkit.View": // 63 occurrences + case "android.view.View": // 63 occurrences + case "android.app.View": // 62 occurrences + return new android.view.View(context, attributeSet); + case "android.widget.FrameLayout": // 6447 occurrences + return new android.widget.FrameLayout(context, attributeSet); + case "android.widget.ViewStub": // 5613 occurrences + case "android.view.ViewStub": // 228 occurrences + case "android.app.ViewStub": // 227 occurrences + case "android.webkit.ViewStub": // 226 occurrences + return new android.view.ViewStub(context, attributeSet); + case "android.widget.TextView": // 4722 occurrences + return new android.widget.TextView(context, attributeSet); + case "android.widget.ImageView": // 3044 occurrences + return new android.widget.ImageView(context, attributeSet); + case "android.widget.RelativeLayout": // 2665 occurrences + return new android.widget.RelativeLayout(context, attributeSet); + case "android.widget.Space": // 1694 occurrences + return new android.widget.Space(context, attributeSet); + case "android.widget.ProgressBar": // 770 occurrences + return new android.widget.ProgressBar(context, attributeSet); + case "android.widget.Button": // 382 occurrences + return new android.widget.Button(context, attributeSet); + case "android.widget.ImageButton": // 265 occurrences + return new android.widget.ImageButton(context, attributeSet); + case "android.widget.Switch": // 145 occurrences + return new android.widget.Switch(context, attributeSet); + case "android.widget.DateTimeView": // 117 occurrences + return new android.widget.DateTimeView(context, attributeSet); + case "android.widget.Toolbar": // 86 occurrences + return new android.widget.Toolbar(context, attributeSet); + case "android.widget.HorizontalScrollView": // 68 occurrences + return new android.widget.HorizontalScrollView(context, attributeSet); + case "android.widget.ScrollView": // 67 occurrences + return new android.widget.ScrollView(context, attributeSet); + case "android.widget.NotificationHeaderView": // 65 occurrences + case "android.webkit.NotificationHeaderView": // 65 occurrences + case "android.view.NotificationHeaderView": // 65 occurrences + case "android.app.NotificationHeaderView": // 65 occurrences + return new android.view.NotificationHeaderView(context, attributeSet); + case "android.widget.ListView": // 58 occurrences + return new android.widget.ListView(context, attributeSet); + case "android.widget.QuickContactBadge": // 50 occurrences + return new android.widget.QuickContactBadge(context, attributeSet); + case "android.widget.SeekBar": // 40 occurrences + return new android.widget.SeekBar(context, attributeSet); + case "android.widget.CheckBox": // 38 occurrences + return new android.widget.CheckBox(context, attributeSet); + case "android.widget.GridLayout": // 16 occurrences + return new android.widget.GridLayout(context, attributeSet); + case "android.widget.TableRow": // 15 occurrences + return new android.widget.TableRow(context, attributeSet); + case "android.widget.RadioGroup": // 15 occurrences + return new android.widget.RadioGroup(context, attributeSet); + case "android.widget.Chronometer": // 15 occurrences + return new android.widget.Chronometer(context, attributeSet); + case "android.widget.ViewFlipper": // 13 occurrences + return new android.widget.ViewFlipper(context, attributeSet); + case "android.widget.Spinner": // 9 occurrences + return new android.widget.Spinner(context, attributeSet); + case "android.widget.ViewSwitcher": // 8 occurrences + return new android.widget.ViewSwitcher(context, attributeSet); + case "android.widget.TextSwitcher": // 8 occurrences + return new android.widget.TextSwitcher(context, attributeSet); + case "android.widget.SurfaceView": // 8 occurrences + case "android.webkit.SurfaceView": // 1 occurrence + case "android.view.SurfaceView": // 1 occurrence + case "android.app.SurfaceView": // 1 occurrence + return new android.view.SurfaceView(context, attributeSet); + case "android.widget.CheckedTextView": // 8 occurrences + return new android.widget.CheckedTextView(context, attributeSet); + case "android.preference.PreferenceFrameLayout": // 8 occurrences + return new android.preference.PreferenceFrameLayout(context, attributeSet); + case "android.widget.TwoLineListItem": // 7 occurrences + return new android.widget.TwoLineListItem(context, attributeSet); + case "android.widget.TableLayout": // 5 occurrences + return new android.widget.TableLayout(context, attributeSet); + case "android.widget.EditText": // 5 occurrences + return new android.widget.EditText(context, attributeSet); + case "android.widget.TabWidget": // 3 occurrences + return new android.widget.TabWidget(context, attributeSet); + case "android.widget.TabHost": // 3 occurrences + return new android.widget.TabHost(context, attributeSet); + case "android.widget.ZoomButton": // 2 occurrences + return new android.widget.ZoomButton(context, attributeSet); + case "android.widget.TextureView": // 2 occurrences + case "android.webkit.TextureView": // 2 occurrences + case "android.app.TextureView": // 2 occurrences + case "android.view.TextureView": // 2 occurrences + return new android.view.TextureView(context, attributeSet); + case "android.widget.ExpandableListView": // 2 occurrences + return new android.widget.ExpandableListView(context, attributeSet); + case "android.widget.ViewAnimator": // 1 occurrence + return new android.widget.ViewAnimator(context, attributeSet); + case "android.widget.TextClock": // 1 occurrence + return new android.widget.TextClock(context, attributeSet); + case "android.widget.AutoCompleteTextView": // 1 occurrence + return new android.widget.AutoCompleteTextView(context, attributeSet); + case "android.widget.WebView": // 1 occurrence + case "android.webkit.WebView": // 1 occurrence + return new android.webkit.WebView(context, attributeSet); + } + + return null; + } } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index a1ce39e974e30..6a23abade6866 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1934,7 +1934,7 @@ public boolean equals(@Nullable Object o) { @Override public int hashCode() { - return Objects.hash(supportedDisplayModes, activeDisplayModeId, activeDisplayModeId, + return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId, activeColorMode, hdrCapabilities); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ba6ba63e4ed05..570ff0870de66 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -199,6 +199,8 @@ import java.util.function.Consumer; import java.util.function.Predicate; +import android.app.ActivityThread; + /** *

* This class represents the basic building block for user interface components. A View @@ -15531,6 +15533,13 @@ public void getWindowVisibleDisplayFrame(Rect outRect) { mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect); return; } + + ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread != null) { + float dssScale = activityThread.getDssScale(); + outRect.scale(dssScale); + } + // The view is not attached to a display so we don't have a context. // Make a best guess about the display size. Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 638b8f9f9b404..123739c736c69 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -31,6 +31,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.StrictMode; +import android.os.SystemProperties; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.SparseArray; @@ -51,7 +52,7 @@ public class ViewConfiguration { /** * Duration of the fade when scrollbars fade away in milliseconds */ - private static final int SCROLL_BAR_FADE_DURATION = 250; + private static final int SCROLL_BAR_FADE_DURATION = 280; /** * Default delay before the scrollbars fade in milliseconds @@ -67,14 +68,14 @@ public class ViewConfiguration { * Defines the duration in milliseconds of the pressed state in child * components. */ - private static final int PRESSED_STATE_DURATION = 64; + private static final int PRESSED_STATE_DURATION = 56; /** * Defines the default duration in milliseconds before a press turns into * a long press * @hide */ - public static final int DEFAULT_LONG_PRESS_TIMEOUT = 400; + public static final int DEFAULT_LONG_PRESS_TIMEOUT = 300; /** * Defines the default duration in milliseconds between the first tap's up event and the second @@ -92,7 +93,7 @@ public class ViewConfiguration { * appropriate button to bring up the global actions dialog (power off, * lock screen, etc). */ - private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500; + private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 250; /** * Defines the duration in milliseconds a user needs to hold down the @@ -117,14 +118,14 @@ public class ViewConfiguration { * is a tap or a scroll. If the user does not move within this interval, it is * considered to be a tap. */ - private static final int TAP_TIMEOUT = 100; + private static final int TAP_TIMEOUT = 96; /** * Defines the duration in milliseconds we will wait to see if a touch event * is a jump tap. If the user does not complete the jump tap within this interval, it is * considered to be a tap. */ - private static final int JUMP_TAP_TIMEOUT = 500; + private static final int JUMP_TAP_TIMEOUT = 250; /** * Defines the duration in milliseconds between the first tap's up event and @@ -158,12 +159,12 @@ public class ViewConfiguration { * Defines the duration in milliseconds we want to display zoom controls in response * to a user panning within an application. */ - private static final int ZOOM_CONTROLS_TIMEOUT = 3000; + private static final int ZOOM_CONTROLS_TIMEOUT = 1500; /** * Inset in dips to look for touchable content when the user touches the edge of the screen */ - private static final int EDGE_SLOP = 12; + private static final int EDGE_SLOP = 6; /** * Distance a touch can wander before we think the user is scrolling in dips. @@ -217,12 +218,12 @@ public class ViewConfiguration { /** * Minimum velocity to initiate a fling, as measured in dips per second */ - private static final int MINIMUM_FLING_VELOCITY = 50; + private static final int MINIMUM_FLING_VELOCITY = 60; /** * Maximum velocity to initiate a fling, as measured in dips per second */ - private static final int MAXIMUM_FLING_VELOCITY = 8000; + private static final int MAXIMUM_FLING_VELOCITY = 16000; /** * Delay before dispatching a recurring accessibility event in milliseconds. @@ -237,13 +238,13 @@ public class ViewConfiguration { * should be at least equal to the size of the screen in ARGB888 format. */ @Deprecated - private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888 + private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 854 * 4; // ARGB8888 /** * The coefficient of friction applied to flings/scrolls. */ @UnsupportedAppUsage - private static final float SCROLL_FRICTION = 0.015f; + private static final float SCROLL_FRICTION = 0.006f; /** * Max distance in dips to overscroll for edge effects @@ -483,10 +484,49 @@ private ViewConfiguration(@NonNull @UiContext Context context) { mDoubleTapTouchSlop = mTouchSlop; - mMinimumFlingVelocity = res.getDimensionPixelSize( - com.android.internal.R.dimen.config_viewMinFlingVelocity); - mMaximumFlingVelocity = res.getDimensionPixelSize( - com.android.internal.R.dimen.config_viewMaxFlingVelocity); + // Modification by xdevs23 for better responsiveness using + // system.prop + String minFlingVeloProp = "ro.min.fling_velocity", // Min fling prop + maxFlingVeloProp = "ro.max.fling_velocity"; // Max fling prop + // Get the properties + String minFlingVeloSysProp = SystemProperties.get(minFlingVeloProp), + maxFlingVeloSysProp = SystemProperties.get(maxFlingVeloProp); + boolean isMaxFlingVeloPredefined = false, + isMinFlingVeloPredefined = false; + int minFlingVeloTmp = 0, + maxFlingVeloTmp = 0; + + // Check whether the property values are valid + if(minFlingVeloSysProp != null && (!minFlingVeloSysProp.isEmpty()) && + isNumeric(minFlingVeloSysProp)) { + minFlingVeloTmp = Integer.parseInt(minFlingVeloSysProp); + isMinFlingVeloPredefined = true; + } + + if(maxFlingVeloSysProp != null && (!maxFlingVeloSysProp.isEmpty()) && + isNumeric(maxFlingVeloSysProp)) { + maxFlingVeloTmp = Integer.parseInt(maxFlingVeloSysProp); + isMaxFlingVeloPredefined = true; + } + + // Use config values if no prop available or invalid + if(!isMinFlingVeloPredefined && minFlingVeloTmp == 0) + minFlingVeloTmp = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_viewMinFlingVelocity); + if(!isMaxFlingVeloPredefined && maxFlingVeloTmp == 0) + maxFlingVeloTmp = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_viewMaxFlingVelocity); + + // Check again for availability, otherwise use default values + if(minFlingVeloTmp * maxFlingVeloTmp == 0) { + minFlingVeloTmp = MINIMUM_FLING_VELOCITY; + maxFlingVeloTmp = MAXIMUM_FLING_VELOCITY; + } + + // Assign the final variables + mMinimumFlingVelocity = minFlingVeloTmp; + mMaximumFlingVelocity = maxFlingVeloTmp; + mGlobalActionsKeyTimeout = res.getInteger( com.android.internal.R.integer.config_globalActionsKeyTimeout); @@ -512,6 +552,18 @@ private ViewConfiguration(@NonNull @UiContext Context context) { com.android.internal.R.bool.config_preferKeepClearForFocus); } + /** + * @hide + */ + public static boolean isNumeric(String string) { + try { + Integer.parseInt(string); + } catch(NumberFormatException e) { + return false; + } + return true; + } + /** * Returns a configuration for the specified visual {@link Context}. The configuration depends * on various parameters of the {@link Context}, like the dimension of the display or the @@ -522,7 +574,7 @@ private ViewConfiguration(@NonNull @UiContext Context context) { * {@link Context#createWindowContext(int, Bundle)}. */ // TODO(b/182007470): Use @ConfigurationContext instead - public static ViewConfiguration get(@NonNull @UiContext Context context) { + public static synchronized ViewConfiguration get(@NonNull @UiContext Context context) { StrictMode.assertConfigurationContext(context, "ViewConfiguration"); final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 9f0ad1169a8e5..cb768bfbcdd17 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2892,7 +2892,7 @@ private void resetTouchState() { * Returns true if the flag was previously set. */ private static boolean resetCancelNextUpFlag(@NonNull View view) { - if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { + if (view != null && (view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; return true; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a13872eef6b80..14c1193ee00e8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6543,6 +6543,10 @@ private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mHandwritingInitiator.onTouchEvent(event); + if (event.getPointerCount() == 3 && isSwipeToScreenshotGestureActive()) { + event.setAction(MotionEvent.ACTION_CANCEL); + } + mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; boolean handled = mView.dispatchPointerEvent(event); @@ -6624,7 +6628,6 @@ private boolean updatePointerIcon(MotionEvent event) { } if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) { // E.g. when moving window divider with mouse - Slog.d(mTag, "updatePointerIcon called with position out of bounds"); return false; } final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); @@ -7268,7 +7271,7 @@ final class SyntheticTouchNavigationHandler extends Handler { // probably not be set to anything less than about 4. // If fling accuracy is a problem then consider tuning the tick distance instead. private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f; - private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f; + private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 24f; // Fling velocity decay factor applied after each new key is emitted. // This parameter controls the deceleration and overall duration of the fling. @@ -10980,4 +10983,12 @@ void mergeSync(int syncId, SurfaceSyncer otherSyncer) { } mSurfaceSyncer.merge(mSyncId, syncId, otherSyncer); } + + private boolean isSwipeToScreenshotGestureActive() { + try { + return ActivityManager.getService().isSwipeToScreenshotGestureActive(); + } catch (RemoteException e) { + return false; + } + } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 02027e4a39693..22e605bdc3ad1 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.annotation.ColorInt; @@ -50,6 +51,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.provider.Settings; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionManager; @@ -1274,6 +1276,10 @@ public void clearFlags(int flags) { * @see #clearFlags */ public void setFlags(int flags, int mask) { + if ((mask & FLAG_SECURE) != 0 && Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WINDOW_IGNORE_SECURE, 0) == 1) { + mask &= ~FLAG_SECURE; + } final WindowManager.LayoutParams attrs = getAttributes(); attrs.flags = (attrs.flags&~mask) | (flags&mask); mForcedWindowFlags |= mask; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 231ae084dd6c4..ab80151ff7929 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -661,6 +661,7 @@ public abstract class AbsListView extends AdapterView implements Te private int mMinimumVelocity; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051740) private int mMaximumVelocity; + private int mDecacheThreshold; private float mVelocityScale = 1.0f; final boolean[] mIsScrap = new boolean[1]; @@ -934,6 +935,7 @@ private void initAbsListView() { mVerticalScrollFactor = configuration.getScaledVerticalScrollFactor(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mDecacheThreshold = mMaximumVelocity / 2; mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance(); @@ -4743,7 +4745,7 @@ public void run() { // Keep the fling alive a little longer postDelayed(this, FLYWHEEL_TIMEOUT); } else { - endFling(); + endFling(false); // Don't disable the scrolling cache right after it was enabled mTouchMode = TOUCH_MODE_SCROLL; reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); } @@ -4759,6 +4761,11 @@ public void run() { // Use AbsListView#fling(int) instead @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void start(int initialVelocity) { + if (Math.abs(initialVelocity) > mDecacheThreshold) { + // For long flings, scrolling cache causes stutter, so don't use it + clearScrollingCache(); + } + int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; mScroller.setInterpolator(null); @@ -4839,6 +4846,10 @@ void startScroll(int distance, int duration, boolean linear, // To interrupt a fling early you should use smoothScrollBy(0,0) instead @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void endFling() { + endFling(true); + } + + void endFling(boolean clearCache) { mTouchMode = TOUCH_MODE_REST; removeCallbacks(this); @@ -4847,7 +4858,8 @@ void endFling() { if (!mSuppressIdleStateChangeCall) { reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } - clearScrollingCache(); + if (clearCache) + clearScrollingCache(); mScroller.abortAnimation(); if (mFlingStrictSpan != null) { diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 6281ee9d05d10..3946fb6217209 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -1013,7 +1013,15 @@ private void trackTouchEvent(MotionEvent event) { progress += scale * range + getMin(); setHotspot(x, y); - setProgressInternal(Math.round(progress), true, false); + setProgressInternal(updateTouchProgress(getProgress(), + Math.round(progress)), true, false); + } + + /** + * @hide + */ + protected int updateTouchProgress(int lastProgress, int newProgress) { + return newProgress; } /** diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index f554f895a9819..172d702c358d7 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -448,7 +448,9 @@ public long getItemId(int position) { } final T item = getItem(position); - if (item instanceof CharSequence) { + if (item == null) { + text.setText(""); + } else if (item instanceof CharSequence) { text.setText((CharSequence) item); } else { text.setText(item.toString()); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index a5be202ec7706..7c722e9f723aa 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -190,7 +190,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { for (int i = 0; i < count; i++) { final View child = getChildAt(i); - if (mMeasureAllChildren || child.getVisibility() != GONE) { + if (child != null && (mMeasureAllChildren || child.getVisibility() != GONE)) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, @@ -281,7 +281,7 @@ void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftG for (int i = 0; i < count; i++) { final View child = getChildAt(i); - if (child.getVisibility() != GONE) { + if (child != null && child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int width = child.getMeasuredWidth(); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index c1ec168af1452..ac31a2648d983 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1532,6 +1532,7 @@ private PopupBackgroundView createBackgroundView(View contentView) { final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams( MATCH_PARENT, height); backgroundView.addView(contentView, listParams); + backgroundView.setClipToOutline(true); return backgroundView; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 2879cd888d2d8..bc7e31fac222a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1598,7 +1598,13 @@ public BitmapCache() { public BitmapCache(Parcel source) { mBitmaps = source.createTypedArrayList(Bitmap.CREATOR); - mBitmapHashes = source.readSparseIntArray(); + mBitmapHashes = new SparseIntArray(); + for (int i = 0; i < mBitmaps.size(); i++) { + Bitmap b = mBitmaps.get(i); + if (b != null) { + mBitmapHashes.put(b.hashCode(), i); + } + } } public int getBitmapId(Bitmap b) { @@ -1614,7 +1620,7 @@ public int getBitmapId(Bitmap b) { b = b.asShared(); } mBitmaps.add(b); - mBitmapHashes.put(mBitmaps.size() - 1, hash); + mBitmapHashes.put(hash, mBitmaps.size() - 1); mBitmapMemory = -1; return (mBitmaps.size() - 1); } @@ -1631,7 +1637,6 @@ public Bitmap getBitmapForId(int id) { public void writeBitmapsToParcel(Parcel dest, int flags) { dest.writeTypedList(mBitmaps, flags); - dest.writeSparseIntArray(mBitmapHashes); } public int getBitmapMemory() { diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index eccff0692fabd..322c32d95bcbe 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -22,11 +22,14 @@ import android.app.INotificationManager; import android.app.ITransientNotificationCallback; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.RemoteException; +import android.provider.Settings; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -228,6 +231,23 @@ public void show(View view, IBinder token, IBinder windowToken, int duration, in adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset, horizontalMargin, verticalMargin, removeWindowAnimations); + + ImageView appIcon = (ImageView) mView.findViewById(android.R.id.icon); + if (appIcon != null) { + if ((Settings.System.getInt(mContext.getContentResolver(), Settings.System.TOAST_ICON, 1) == 1)) { + PackageManager pm = mContext.getPackageManager(); + Drawable icon = null; + try { + icon = pm.getApplicationIcon(mPackageName); + } catch (PackageManager.NameNotFoundException e) { + // nothing to do + } + appIcon.setImageDrawable(icon); + } else { + appIcon.setVisibility(View.GONE); + } + } + addToastView(); trySendAccessibilityEvent(mView, mPackageName); if (callback != null) { diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 629a1b36b9e67..c1fa18f049223 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -189,4 +189,7 @@ interface IBatteryStats { void resetBattery(boolean forceUpdate); /** Exposed as a test API. */ void suspendBatteryInput(); + + /** {@hide} */ + void resetStatistics(); } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 87e8ac1be611a..72b9cd272d026 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -473,7 +473,10 @@ public void setCombinedState(int state, long now) { } } mCurCombinedState = state; - mStats.mUidStates.get(mUid).updateCombinedState(state, now); + final UidState uidState = mStats.mUidStates.get(mUid); + if (uidState != null) { + uidState.updateCombinedState(state, now); + } } } diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java index dff9551c0c078..98f14a5c50600 100644 --- a/core/java/com/android/internal/graphics/ColorUtils.java +++ b/core/java/com/android/internal/graphics/ColorUtils.java @@ -21,6 +21,8 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.graphics.Color; +import android.util.Log; +import android.util.MathUtils; import com.android.internal.graphics.cam.Cam; @@ -31,6 +33,8 @@ */ public final class ColorUtils { + private static final String TAG = "ColorUtils"; + private static final double XYZ_WHITE_REFERENCE_X = 95.047; private static final double XYZ_WHITE_REFERENCE_Y = 100; private static final double XYZ_WHITE_REFERENCE_Z = 108.883; @@ -95,8 +99,10 @@ public static double calculateLuminance(@ColorInt int color) { */ public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } if (Color.alpha(foreground) < 255) { // If the foreground is translucent, composite the foreground over the background @@ -148,8 +154,10 @@ public static int calculateMinimumBackgroundAlpha(@ColorInt int foreground, public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background, float minContrastRatio) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } ContrastCalculator contrastCalculator = (fg, bg, alpha) -> { @@ -605,8 +613,59 @@ private static double pivotXyzComponent(double component) { : (XYZ_KAPPA * component + 16) / 116; } + private static float cube(float x) { + return x * x * x; + } + + // Linear -> sRGB + private static float srgbTransfer(float x) { + if (x >= 0.0031308f) { + return 1.055f * (float) Math.pow(x, 1.0f / 2.4f) - 0.055f; + } else { + return 12.92f * x; + } + } + + // sRGB -> linear + private static float srgbTransferInv(float x) { + if (x >= 0.04045f) { + return (float) Math.pow((x + 0.055f) / 1.055f, 2.4f); + } else { + return x / 12.92f; + } + } + + private static float srgbRed(@ColorInt int color) { + return srgbTransferInv(((float) Color.red(color)) / 255.0f); + } + + private static float srgbGreen(@ColorInt int color) { + return srgbTransferInv(((float) Color.green(color)) / 255.0f); + } + + private static float srgbBlue(@ColorInt int color) { + return srgbTransferInv(((float) Color.blue(color)) / 255.0f); + } + + private static int srgbTransferToInt(float c) { + return MathUtils.constrain(Math.round(srgbTransfer(c) * 255.0f), 0, 255); + } + + private static float rgbToOklabLp(float r, float g, float b) { + return (float) Math.cbrt(0.4122214708f * r + 0.5363325363f * g + 0.0514459929f * b); + } + + private static float rgbToOklabMp(float r, float g, float b) { + return (float) Math.cbrt(0.2119034982f * r + 0.6806995451f * g + 0.1073969566f * b); + } + + private static float rgbToOklabSp(float r, float g, float b) { + return (float) Math.cbrt(0.0883024619f * r + 0.2817188376f * g + 0.6299787005f * b); + } + /** * Blend between two ARGB colors using the given ratio. + * This uses Oklab internally in order to perform a perceptually-uniform blend. * *

A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend, * 1.0 will result in {@code color2}.

@@ -620,10 +679,29 @@ public static int blendARGB(@ColorInt int color1, @ColorInt int color2, @FloatRange(from = 0.0, to = 1.0) float ratio) { final float inverseRatio = 1 - ratio; float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio; - float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio; - float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio; - float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio; - return Color.argb((int) a, (int) r, (int) g, (int) b); + + float r1 = srgbRed(color1); + float g1 = srgbGreen(color1); + float b1 = srgbBlue(color1); + float lp1 = rgbToOklabLp(r1, g1, b1); + float mp1 = rgbToOklabMp(r1, g1, b1); + float sp1 = rgbToOklabSp(r1, g1, b1); + + float r2 = srgbRed(color2); + float g2 = srgbGreen(color2); + float b2 = srgbBlue(color2); + float lp2 = rgbToOklabLp(r2, g2, b2); + float mp2 = rgbToOklabMp(r2, g2, b2); + float sp2 = rgbToOklabSp(r2, g2, b2); + + float l = cube(lp1 * inverseRatio + lp2 * ratio); + float m = cube(mp1 * inverseRatio + mp2 * ratio); + float s = cube(sp1 * inverseRatio + sp2 * ratio); + int r = srgbTransferToInt(+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s); + int g = srgbTransferToInt(-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s); + int b = srgbTransferToInt(-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s); + + return Color.argb((int) a, r, g, b); } /** @@ -696,4 +774,4 @@ private interface ContrastCalculator { double calculateContrast(int foreground, int background, int alpha); } -} \ No newline at end of file +} diff --git a/core/java/com/android/internal/graphics/color/CieLab.java b/core/java/com/android/internal/graphics/color/CieLab.java new file mode 100644 index 0000000000000..79a98ebca80e0 --- /dev/null +++ b/core/java/com/android/internal/graphics/color/CieLab.java @@ -0,0 +1,44 @@ +/* + * 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.internal.graphics.color; + +public final class CieLab { + private CieLab() { } + + public static double lToZcamJz(double l) { + double fInvLp = fInv((l + 16.0) / 116.0); + + double xRel = Illuminants.D65_X * fInvLp; + double yRel = Illuminants.D65_Y * fInvLp; + double zRel = Illuminants.D65_Z * fInvLp; + + double x = xRel * CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE; + double y = yRel * CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE; + double z = zRel * CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE; + + Zcam zcam = new Zcam(new CieXyzAbs(x, y, z)); + return zcam.lightness; + } + + private static double fInv(double x) { + if (x > 6.0/29.0) { + return x * x * x; + } else { + return (108.0/841.0) * (x - 4.0/29.0); + } + } +} diff --git a/core/java/com/android/internal/graphics/color/CieXyzAbs.java b/core/java/com/android/internal/graphics/color/CieXyzAbs.java new file mode 100644 index 0000000000000..b2dda3028ae40 --- /dev/null +++ b/core/java/com/android/internal/graphics/color/CieXyzAbs.java @@ -0,0 +1,125 @@ +/* + * 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.internal.graphics.color; + +/** + * CIE 1931 XYZ interchange color with absolute luminance specified in nits (cd/m^2). + * This is similar to colorkt's CieXyzAbs class, but it also does the jobs of CieXyz, LinearSrgb, + * and Srgb in order to reduce garbage object creation. + */ +public final class CieXyzAbs { + public static final double DEFAULT_SDR_WHITE_LUMINANCE = 200.0; // cd/m^2 + + public double x; + public double y; + public double z; + + public CieXyzAbs(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public CieXyzAbs(int rgb8) { + int r8 = (rgb8 >> 16) & 0xff; + int g8 = (rgb8 >> 8) & 0xff; + int b8 = rgb8 & 0xff; + + double r = srgbFInv(((double) r8) / 255.0); + double g = srgbFInv(((double) g8) / 255.0); + double b = srgbFInv(((double) b8) / 255.0); + + double xRel = 0.41245643908969226 * r + 0.357576077643909 * g + 0.18043748326639897 * b; + double yRel = 0.21267285140562256 * r + 0.715152155287818 * g + 0.07217499330655959 * b; + double zRel = 0.019333895582329303 * r + 0.11919202588130297 * g + 0.950304078536368 * b; + + this.x = xRel * DEFAULT_SDR_WHITE_LUMINANCE; + this.y = yRel * DEFAULT_SDR_WHITE_LUMINANCE; + this.z = zRel * DEFAULT_SDR_WHITE_LUMINANCE; + } + + public int toRgb8() { + double xRel = x / DEFAULT_SDR_WHITE_LUMINANCE; + double yRel = y / DEFAULT_SDR_WHITE_LUMINANCE; + double zRel = z / DEFAULT_SDR_WHITE_LUMINANCE; + + double r = xyzRelToR(xRel, yRel, zRel); + double g = xyzRelToG(xRel, yRel, zRel); + double b = xyzRelToB(xRel, yRel, zRel); + + int r8 = ((int) Math.round(srgbF(r) * 255.0) & 0xff) << 16; + int g8 = ((int) Math.round(srgbF(g) * 255.0) & 0xff) << 8; + int b8 = ((int) Math.round(srgbF(b) * 255.0) & 0xff); + + return r8 | g8 | b8; + } + + /*package-private*/ boolean isInGamut() { + // I don't like this duplicated code, but the alternative would be to create lots of unnecessary + // garbage array objects for gamut mapping every time a color is processed. + double xRel = x / DEFAULT_SDR_WHITE_LUMINANCE; + double yRel = y / DEFAULT_SDR_WHITE_LUMINANCE; + double zRel = z / DEFAULT_SDR_WHITE_LUMINANCE; + + double r = xyzRelToR(xRel, yRel, zRel); + double g = xyzRelToG(xRel, yRel, zRel); + double b = xyzRelToB(xRel, yRel, zRel); + + return inGamut(r) && inGamut(g) && inGamut(b); + } + + // This matrix (along with the inverse above) has been optimized to minimize chroma in CIELCh + // when converting neutral sRGB colors to CIELAB. The maximum chroma for sRGB neutral colors 0-255 is + // 5.978733960281817e-14. + // + // Calculated with https://github.com/facelessuser/coloraide/blob/master/tools/calc_xyz_transform.py + // Using D65 xy chromaticities from the sRGB spec: x = 0.3127, y = 0.3290 + // Always keep in sync with Illuminants.D65. + private static double xyzRelToR(double x, double y, double z) { + return 3.2404541621141045 * x + -1.5371385127977162 * y + -0.4985314095560159 * z; + } + + private static double xyzRelToG(double x, double y, double z) { + return -0.969266030505187 * x + 1.8760108454466944 * y + 0.04155601753034983 * z; + } + + private static double xyzRelToB(double x, double y, double z) { + return 0.05564343095911474 * x + -0.2040259135167538 * y + 1.0572251882231787 * z; + } + + // Linear -> sRGB + private static double srgbF(double x) { + if (x >= 0.0031308) { + return 1.055 * Math.pow(x, 1.0 / 2.4) - 0.055; + } else { + return 12.92 * x; + } + } + + // sRGB -> linear + private static double srgbFInv(double x) { + if (x >= 0.04045) { + return Math.pow((x + 0.055) / 1.055, 2.4); + } else { + return x / 12.92; + } + } + + private static boolean inGamut(double x) { + return x >= 0.0 && x <= 1.0; + } +} diff --git a/core/java/com/android/internal/graphics/color/Illuminants.java b/core/java/com/android/internal/graphics/color/Illuminants.java new file mode 100644 index 0000000000000..ad97e0accf590 --- /dev/null +++ b/core/java/com/android/internal/graphics/color/Illuminants.java @@ -0,0 +1,30 @@ +/* + * 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.internal.graphics.color; + +public final class Illuminants { + // D65 as defined in ASTM E308 + public static double D65_X = 0.95047; + public static double D65_Y = 1.0; + public static double D65_Z = 1.08883; + + private Illuminants() { } + + public static CieXyzAbs getD65Abs(double luminance) { + return new CieXyzAbs(D65_X * luminance, D65_Y * luminance, D65_Z * luminance); + } +} diff --git a/core/java/com/android/internal/graphics/color/Zcam.java b/core/java/com/android/internal/graphics/color/Zcam.java new file mode 100644 index 0000000000000..696d8f184c796 --- /dev/null +++ b/core/java/com/android/internal/graphics/color/Zcam.java @@ -0,0 +1,242 @@ +/* + * 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.internal.graphics.color; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Minimal implementation of ZCAM that only includes lightness, chroma, and hue. + * + * This implements the bare minimum of ZCAM necessary for ColorStateList modulation. + * It has fewer object-oriented abstractions and lacks the more sophisticated + * architecture and organization of colorkt; however, this can be called many times + * during UI view inflation, so performance is imperative. + */ +public final class Zcam { + private static final double B = 1.15; + private static final double G = 0.66; + private static final double C1 = 3424.0 / 4096; + private static final double C2 = 2413.0 / 128; + private static final double C3 = 2392.0 / 128; + private static final double ETA = 2610.0 / 16384; + private static final double RHO = 1.7 * 2523.0 / 32; + private static final double EPSILON = 3.7035226210190005e-11; + + public double lightness; + public double chroma; + public double hue; + + public final ViewingConditions viewingConditions; + + public Zcam(double lightness, double chroma, double hue) { + this(lightness, chroma, hue, ViewingConditions.DEFAULT); + } + + public Zcam(CieXyzAbs xyz) { + this(xyz, ViewingConditions.DEFAULT); + } + + public Zcam(double lightness, double chroma, double hue, ViewingConditions cond) { + this.lightness = lightness; + this.chroma = chroma; + this.hue = hue; + this.viewingConditions = cond; + } + + public Zcam(CieXyzAbs xyz, ViewingConditions cond) { + /* Step 2 */ + // Achromatic response + double[] Izazbz = xyzToIzazbz(xyz); + double Iz = Izazbz[0]; + double az = Izazbz[1]; + double bz = Izazbz[2]; + + /* Step 3 */ + // Hue angle + double hz = Math.toDegrees(Math.atan2(bz, az)); + double hp = (hz < 0) ? hz + 360.0 : hz; + + /* Step 4 */ + // Eccentricity factor + double ez = hpToEz(hp); + + /* Step 5 */ + // Brightness + double Qz = izToQz(Iz, cond); + double Qz_w = cond.Qz_w; + + // Lightness + double Jz = 100.0 * (Qz / Qz_w); + + // Colorfulness + double Mz = 100.0 * Math.pow(square(az) + square(bz), 0.37) * + ((Math.pow(ez, 0.068) * cond.ez_coeff) / cond.Mz_denom); + + // Chroma + double Cz = 100.0 * (Mz / Qz_w); + + /* Step 6 is missing because this implementation doesn't support 2D attributes */ + + this.lightness = Jz; + this.chroma = Cz; + this.hue = hp; + this.viewingConditions = cond; + } + + public CieXyzAbs toXyzAbs() { + return toXyzAbs(lightness, chroma, hue, viewingConditions); + } + + /*package-private*/ static CieXyzAbs toXyzAbs(double lightness, double chroma, double hue) { + return toXyzAbs(lightness, chroma, hue, ViewingConditions.DEFAULT); + } + + private static CieXyzAbs toXyzAbs(double lightness, double chroma, double hue, ViewingConditions cond) { + double Qz_w = cond.Qz_w; + + /* Step 1 */ + // Achromatic response + double Iz = Math.pow((lightness * Qz_w) / (cond.Iz_coeff * 100.0), + cond.Qz_denom / (1.6 * cond.surroundFactor)); + + /* Step 2 is missing because we use chroma as the input */ + + /* Step 3 is missing because hue composition is not supported */ + + /* Step 4 */ + double Mz = (chroma * Qz_w) / 100.0; + double ez = hpToEz(hue); + double Cz_p = Math.pow((Mz * cond.Mz_denom) / + // Paper specifies pow(1.3514) but this extra precision is necessary for accurate inversion + (100.0 * Math.pow(ez, 0.068) * cond.ez_coeff), 1.0 / 0.37 / 2); + double hueRad = Math.toRadians(hue); + double az = Cz_p * Math.cos(hueRad); + double bz = Cz_p * Math.sin(hueRad); + + /* Step 5 */ + double I = Iz + EPSILON; + + double r = pq(I + 0.2772100865*az + 0.1160946323*bz); + double g = pq(I); + double b = pq(I + 0.0425858012*az + -0.7538445799*bz); + + double xp = 1.9242264358*r + -1.0047923126*g + 0.0376514040*b; + double yp = 0.3503167621*r + 0.7264811939*g + -0.0653844229*b; + double z = -0.0909828110*r + -0.3127282905*g + 1.5227665613*b; + + double x = (xp + (B - 1)*z) / B; + double y = (yp + (G - 1)*x) / G; + + return new CieXyzAbs(x, y, z); + } + + // Transfer function and inverse + private static double pq(double x) { + double num = C1 - Math.pow(x, 1.0/RHO); + double denom = C3*Math.pow(x, 1.0/RHO) - C2; + + return 10000.0 * Math.pow(num / denom, 1.0/ETA); + } + private static double pqInv(double x) { + double num = C1 + C2*Math.pow(x / 10000, ETA); + double denom = 1.0 + C3*Math.pow(x / 10000, ETA); + + return Math.pow(num / denom, RHO); + } + + // Intermediate conversion, also used in ViewingConditions + private static double[] xyzToIzazbz(CieXyzAbs xyz) { + // This equation (#4) is wrong in the paper; below is the correct version. + // It can be derived from the inverse model (supplementary paper) or the original Jzazbz paper. + double xp = B*xyz.x - (B-1)*xyz.z; + double yp = G*xyz.y - (G-1)*xyz.x; + + double rp = pqInv( 0.41478972*xp + 0.579999*yp + 0.0146480*xyz.z); + double gp = pqInv(-0.20151000*xp + 1.120649*yp + 0.0531008*xyz.z); + double bp = pqInv(-0.01660080*xp + 0.264800*yp + 0.6684799*xyz.z); + + double az = 3.524000*rp + -4.066708*gp + 0.542708*bp; + double bz = 0.199076*rp + 1.096799*gp + -1.295875*bp; + double Iz = gp - EPSILON; + + return new double[]{Iz, az, bz}; + } + + // Shared between forward and inverse models + private static double hpToEz(double hp) { + return 1.015 + Math.cos(Math.toRadians(89.038 + hp)); + } + + private static double izToQz(double Iz, ViewingConditions cond) { + return cond.Iz_coeff * Math.pow(Iz, (1.6 * cond.surroundFactor) / cond.Qz_denom); + } + + private static double square(double x) { + return x * x; + } + + public static class ViewingConditions { + public static final double SURROUND_DARK = 0.525; + public static final double SURROUND_DIM = 0.59; + public static final double SURROUND_AVERAGE = 0.69; + + public static final ViewingConditions DEFAULT = new ViewingConditions( + SURROUND_AVERAGE, + 0.4 * CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE, + // Mid-gray background: CIELAB L = 50 + 0.18418651851244416 * CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE, + Illuminants.getD65Abs(CieXyzAbs.DEFAULT_SDR_WHITE_LUMINANCE) + ); + + public final double surroundFactor; + public final double adaptingLuminance; + public final double backgroundLuminance; + public final CieXyzAbs referenceWhite; + + @VisibleForTesting + public final double Iz_coeff; + @VisibleForTesting + public final double ez_coeff; + @VisibleForTesting + public final double Qz_denom; + @VisibleForTesting + public final double Mz_denom; + @VisibleForTesting + public final double Qz_w; + + public ViewingConditions(double surroundFactor, double adaptingLuminance, + double backgroundLuminance, CieXyzAbs referenceWhite) { + this.surroundFactor = surroundFactor; + this.adaptingLuminance = adaptingLuminance; + this.backgroundLuminance = backgroundLuminance; + this.referenceWhite = referenceWhite; + + double F_b = Math.sqrt(backgroundLuminance / referenceWhite.y); + double F_l = 0.171 * Math.cbrt(adaptingLuminance) * (1.0 - Math.exp(-48.0 / 9.0 * adaptingLuminance)); + + this.Iz_coeff = 2700.0 * Math.pow(surroundFactor, 2.2) * Math.pow(F_b, 0.5) * Math.pow(F_l, 0.2); + this.ez_coeff = Math.pow(F_l, 0.2); + this.Qz_denom = Math.pow(F_b, 0.12); + + double Iz_w = xyzToIzazbz(referenceWhite)[0]; + this.Mz_denom = Math.pow(Iz_w, 0.78) * Math.pow(F_b, 0.1); + + // Depends on coefficients computed above + this.Qz_w = izToQz(Iz_w, this); + } + } +} diff --git a/core/java/com/android/internal/graphics/color/ZcamGamut.java b/core/java/com/android/internal/graphics/color/ZcamGamut.java new file mode 100644 index 0000000000000..3e86668c2c520 --- /dev/null +++ b/core/java/com/android/internal/graphics/color/ZcamGamut.java @@ -0,0 +1,85 @@ +/* + * 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.internal.graphics.color; + +public final class ZcamGamut { + private static final double EPSILON = 0.0001; + + private ZcamGamut() { } + + private static double evalLine(double slope, double intercept, double x) { + return slope * x + intercept; + } + + private static int clip(double l1, double c1, double hue, double l0) { + CieXyzAbs result = Zcam.toXyzAbs(l1, c1, hue); + if (result.isInGamut()) { + return result.toRgb8(); + } + + // Avoid searching black and white for performance + if (l1 <= EPSILON) { + return 0x000000; + } else if (l1 >= 100.0 - EPSILON) { + return 0xffffff; + } + + // Chroma is always 0 so the reference point is guaranteed to be within gamut + double c0 = 0.0; + + // Create a line - x=C, y=L - intersecting a hue plane + // In theory, we could have a divide-by-zero error here if c1=0. However, that's not a problem because + // all colors with chroma = 0 should be in gamut, so this loop never runs. Even if this loop somehow + // ends up running for such a color, it would just result in a slow search that doesn't converge because + // the NaN causes isInGamut() to return false. + double slope = (l1 - l0) / (c1 - c0); + double intercept = l0 - slope * c0; + + double lo = 0.0; + double hi = c1; + + while (Math.abs(hi - lo) > EPSILON) { + double midC = (lo + hi) / 2.0; + double midL = evalLine(slope, intercept, midC); + + result = Zcam.toXyzAbs(midL, midC, hue); + + if (!result.isInGamut()) { + // If this color isn't in gamut, pivot left to get an in-gamut color. + hi = midC; + } else { + // If this color is in gamut, test a point to the right that should be just outside the gamut. + // If the test point is *not* in gamut, we know that this color is right at the edge of the gamut. + double midC2 = midC + EPSILON; + double midL2 = evalLine(slope, intercept, midC2); + + CieXyzAbs ptOutside = Zcam.toXyzAbs(midL2, midC2, hue); + if (ptOutside.isInGamut()) { + lo = midC; + } else { + break; + } + } + } + + return result.toRgb8(); + } + + public static int clipToRgb8(Zcam color) { + return clip(color.lightness, color.chroma, color.hue, color.lightness); + } +} diff --git a/core/java/com/android/internal/graphics/drawable/RoundedCornerProgressDrawable.java b/core/java/com/android/internal/graphics/drawable/RoundedCornerProgressDrawable.java new file mode 100644 index 0000000000000..2c794dbbebfc5 --- /dev/null +++ b/core/java/com/android/internal/graphics/drawable/RoundedCornerProgressDrawable.java @@ -0,0 +1,90 @@ +/* + * 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.internal.graphics.drawable; + +import android.content.pm.ActivityInfo; +import android.content.res.Resources; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.DrawableWrapper; +import android.graphics.drawable.InsetDrawable; + +public final class RoundedCornerProgressDrawable extends InsetDrawable { + public RoundedCornerProgressDrawable() { + this(null); + } + + public RoundedCornerProgressDrawable(Drawable drawable) { + super(drawable, 0); + } + + public int getChangingConfigurations() { + return super.getChangingConfigurations() | ActivityInfo.CONFIG_DENSITY; + } + + public Drawable.ConstantState getConstantState() { + return new RoundedCornerState(super.getConstantState()); + } + + protected void onBoundsChange(Rect rect) { + super.onBoundsChange(rect); + onLevelChange(getLevel()); + } + + public boolean onLayoutDirectionChanged(int level) { + onLevelChange(getLevel()); + return super.onLayoutDirectionChanged(level); + } + + protected boolean onLevelChange(int n) { + Drawable drawable = getDrawable(); + Rect bounds; + if (drawable == null) { + bounds = null; + } else { + bounds = drawable.getBounds(); + } + int height = getBounds().height(); + int level = (getBounds().width() - getBounds().height()) * n / 10000; + drawable = getDrawable(); + if (drawable != null) { + drawable.setBounds(getBounds().left, bounds.top, getBounds().left + (height + level), bounds.bottom); + } + return super.onLevelChange(level); + } + + private static final class RoundedCornerState extends Drawable.ConstantState { + private final Drawable.ConstantState mWrappedState; + + public RoundedCornerState(Drawable.ConstantState wrappedState) { + mWrappedState = wrappedState; + } + + public int getChangingConfigurations() { + return mWrappedState.getChangingConfigurations(); + } + + public Drawable newDrawable() { + return newDrawable(null, null); + } + + public Drawable newDrawable(Resources resources, Resources.Theme theme) { + Drawable drawable = mWrappedState.newDrawable(resources, theme); + return (Drawable) new RoundedCornerProgressDrawable(((DrawableWrapper) drawable).getDrawable()); + } + } +} diff --git a/core/java/com/android/internal/lineage/app/LineageContextConstants.java b/core/java/com/android/internal/lineage/app/LineageContextConstants.java new file mode 100644 index 0000000000000..5400ddf45ff2f --- /dev/null +++ b/core/java/com/android/internal/lineage/app/LineageContextConstants.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015, The CyanogenMod 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.internal.lineage.app; + +import android.annotation.SdkConstant; + +/** + * @hide + * TODO: We need to somehow make these managers accessible via getSystemService + */ +public final class LineageContextConstants { + + /** + * @hide + */ + private LineageContextConstants() { + // Empty constructor + } + + /** + * Use with {@link android.content.Context#getSystemService} to retrieve a + * {@link com.android.internal.lineage.LineageHardwareManager} to manage the extended + * hardware features of the device. + * + * @see android.content.Context#getSystemService + * @see com.android.internal.lineage.LineageHardwareManager + * + * @hide + */ + public static final String LINEAGE_HARDWARE_SERVICE = "lineagehardware"; + + /** + * Manages display color adjustments + * + * @hide + */ + public static final String LINEAGE_LIVEDISPLAY_SERVICE = "lineagelivedisplay"; + +} diff --git a/core/java/com/android/internal/lineage/hardware/AdaptiveBacklight.java b/core/java/com/android/internal/lineage/hardware/AdaptiveBacklight.java new file mode 100644 index 0000000000000..c66cdd97e6568 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/AdaptiveBacklight.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import android.util.Log; + +import com.android.internal.util.cherish.FileUtils; + +/** + * Adaptive backlight support (this refers to technologies like NVIDIA SmartDimmer, + * QCOM CABL or Samsung CABC). + */ +public class AdaptiveBacklight { + + private static final String TAG = "AdaptiveBacklight"; + + private static final String FILE_CABC = "/sys/class/graphics/fb0/cabc"; + + /** + * Whether device supports an adaptive backlight technology. + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return FileUtils.isFileReadable(FILE_CABC) && FileUtils.isFileWritable(FILE_CABC); + } + + /** + * This method return the current activation status of the adaptive backlight technology. + * + * @return boolean Must be false when adaptive backlight is not supported or not activated, or + * the operation failed while reading the status; true in any other case. + */ + public static boolean isEnabled() { + return Integer.parseInt(FileUtils.readOneLine(FILE_CABC)) > 0; + } + + /** + * This method allows to setup adaptive backlight technology status. + * + * @param status The new adaptive backlight status + * @return boolean Must be false if adaptive backlight is not supported or the operation + * failed; true in any other case. + */ + public static boolean setEnabled(boolean status) { + return FileUtils.writeLine(FILE_CABC, status ? "1" : "0"); + } +} diff --git a/core/java/com/android/internal/lineage/hardware/AutoContrast.java b/core/java/com/android/internal/lineage/hardware/AutoContrast.java new file mode 100644 index 0000000000000..15d25a0eb3f06 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/AutoContrast.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.internal.lineage.hardware; + +import com.android.internal.util.cherish.FileUtils; + +import android.util.Log; + +/** + * Auto Contrast Optimization + */ +public class AutoContrast { + + private static final String TAG = "AutoContrast"; + + private static final String FILE_ACO = "/sys/class/graphics/fb0/aco"; + + /** + * Whether device supports ACO + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return FileUtils.isFileReadable(FILE_ACO) && FileUtils.isFileWritable(FILE_ACO); + } + + /** + * This method return the current activation status of ACO + * + * @return boolean Must be false when ACO is not supported or not activated, or + * the operation failed while reading the status; true in any other case. + */ + public static boolean isEnabled() { + try { + return Integer.parseInt(FileUtils.readOneLine(FILE_ACO)) > 0; + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + } + return false; + } + + /** + * This method allows to setup ACO + * + * @param status The new ACO status + * @return boolean Must be false if ACO is not supported or the operation + * failed; true in any other case. + */ + public static boolean setEnabled(boolean status) { + return FileUtils.writeLine(FILE_ACO, status ? "1" : "0"); + } + + /** + * Whether adaptive backlight (CABL / CABC) is required to be enabled + * + * @return boolean False if adaptive backlight is not a dependency + */ + public static boolean isAdaptiveBacklightRequired() { + return false; + } +} diff --git a/core/java/com/android/internal/lineage/hardware/ColorBalance.java b/core/java/com/android/internal/lineage/hardware/ColorBalance.java new file mode 100644 index 0000000000000..96d748e088dc1 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/ColorBalance.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +/** + * Color balance support + * + * Color balance controls allow direct adjustment of display color temperature + * using a range of values. A zero implies no adjustment, negative values + * move towards warmer temperatures, and positive values move towards + * cool temperatures. + */ +public class ColorBalance { + + /** + * Whether device supports color balance control + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return false; + } + + /** + * This method returns the current color balance value + * + * @return int Zero when no adjustment is made, negative values move + * towards warmer temperatures, positive values move towards cooler temperatures. + */ + public static int getValue() { + return 0; + } + + /** + * This method allows to set the display color balance + * + * @param value + * @return boolean Must be false if feature is not supported or the operation + * failed; true in any other case. + */ + public static boolean setValue(int value) { + return false; + } + + /** + * Get the minimum allowed color adjustment value + * @return int + */ + public static int getMinValue() { + return 0; + } + + /** + * Get the maximum allowed color adjustment value + * @return int + */ + public static int getMaxValue() { + return 0; + } +} diff --git a/core/java/com/android/internal/lineage/hardware/ColorEnhancement.java b/core/java/com/android/internal/lineage/hardware/ColorEnhancement.java new file mode 100644 index 0000000000000..c60a5e73cdba3 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/ColorEnhancement.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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.internal.lineage.hardware; + +import com.android.internal.util.cherish.FileUtils; + +import android.util.Log; + +/** + * Color enhancement support + */ +public class ColorEnhancement { + + private static final String TAG = "ColorEnhancement"; + + private static final String FILE_CE = "/sys/class/graphics/fb0/color_enhance"; + + /** + * Whether device supports an color enhancement technology. + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return FileUtils.isFileReadable(FILE_CE) && FileUtils.isFileWritable(FILE_CE); + } + + /** + * This method return the current activation status of the color enhancement technology. + * + * @return boolean Must be false when color enhancement is not supported or not activated, or + * the operation failed while reading the status; true in any other case. + */ + public static boolean isEnabled() { + try { + return Integer.parseInt(FileUtils.readOneLine(FILE_CE)) > 0; + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + } + return false; + } + + /** + * This method allows to setup color enhancement technology status. + * + * @param status The new color enhancement status + * @return boolean Must be false if adaptive backlight is not supported or the operation + * failed; true in any other case. + */ + public static boolean setEnabled(boolean status) { + return FileUtils.writeLine(FILE_CE, status ? "1" : "0"); + } +} diff --git a/core/java/com/android/internal/lineage/hardware/DisplayColorCalibration.java b/core/java/com/android/internal/lineage/hardware/DisplayColorCalibration.java new file mode 100644 index 0000000000000..17527e27f53e2 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/DisplayColorCalibration.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import com.android.internal.util.cherish.FileUtils; + +public class DisplayColorCalibration { + + private static final String TAG = "DisplayColorCalibration"; + + private static final String COLOR_FILE = "/sys/class/graphics/fb0/rgb"; + + private static final int MIN = 255; + private static final int MAX = 32768; + + public static boolean isSupported() { + return FileUtils.isFileReadable(COLOR_FILE) && FileUtils.isFileWritable(COLOR_FILE); + } + + public static int getMaxValue() { + return MAX; + } + + public static int getMinValue() { + return MIN; + } + + public static int getDefValue() { + return getMaxValue(); + } + + public static String getCurColors() { + return FileUtils.readOneLine(COLOR_FILE); + } + + public static boolean setColors(String colors) { + return FileUtils.writeLine(COLOR_FILE, colors); + } + +} diff --git a/core/java/com/android/internal/lineage/hardware/DisplayMode.aidl b/core/java/com/android/internal/lineage/hardware/DisplayMode.aidl new file mode 100644 index 0000000000000..bc44763f0f766 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/DisplayMode.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.internal.lineage.hardware; + +parcelable DisplayMode; diff --git a/core/java/com/android/internal/lineage/hardware/DisplayMode.java b/core/java/com/android/internal/lineage/hardware/DisplayMode.java new file mode 100644 index 0000000000000..8a72d124e1b39 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/DisplayMode.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.internal.lineage.hardware; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.lineage.util.Concierge; +import com.android.internal.lineage.util.Concierge.ParcelInfo; + +/** + * Display Modes API + * + * A device may implement a list of preset display modes for different + * viewing intents, such as movies, photos, or extra vibrance. These + * modes may have multiple components such as gamma correction, white + * point adjustment, etc, but are activated by a single control point. + * + * This API provides support for enumerating and selecting the + * modes supported by the hardware. + * + * A DisplayMode is referenced by it's identifier and carries an + * associated name (up to the user to translate this value). + */ +public class DisplayMode implements Parcelable { + public final int id; + public final String name; + + public DisplayMode(int id, String name) { + this.id = id; + this.name = name; + } + + private DisplayMode(Parcel parcel) { + // Read parcelable version via the Concierge + ParcelInfo parcelInfo = Concierge.receiveParcel(parcel); + int parcelableVersion = parcelInfo.getParcelVersion(); + + // temp vars + int tmpId = -1; + String tmpName = null; + + tmpId = parcel.readInt(); + if (parcel.readInt() != 0) { + tmpName = parcel.readString(); + } + + // set temps + this.id = tmpId; + this.name = tmpName; + + // Complete parcel info for the concierge + parcelInfo.complete(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + // Tell the concierge to prepare the parcel + ParcelInfo parcelInfo = Concierge.prepareParcel(out); + + out.writeInt(id); + if (name != null) { + out.writeInt(1); + out.writeString(name); + } else { + out.writeInt(0); + } + + // Complete the parcel info for the concierge + parcelInfo.complete(); + } + + /** @hide */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public DisplayMode createFromParcel(Parcel in) { + return new DisplayMode(in); + } + + @Override + public DisplayMode[] newArray(int size) { + return new DisplayMode[size]; + } + }; + +} diff --git a/core/java/com/android/internal/lineage/hardware/DisplayModeControl.java b/core/java/com/android/internal/lineage/hardware/DisplayModeControl.java new file mode 100644 index 0000000000000..1489d8fffd8e0 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/DisplayModeControl.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +/** + * Display Modes API + * + * A device may implement a list of preset display modes for different + * viewing intents, such as movies, photos, or extra vibrance. These + * modes may have multiple components such as gamma correction, white + * point adjustment, etc, but are activated by a single control point. + * + * This API provides support for enumerating and selecting the + * modes supported by the hardware. + */ + +public class DisplayModeControl { + + /** + * All HAF classes should export this boolean. + * Real implementations must, of course, return true + */ + public static boolean isSupported() { + return false; + } + + /** + * Get the list of available modes. A mode has an integer + * identifier and a string name. + * + * It is the responsibility of the upper layers to + * map the name to a human-readable format or perform translation. + */ + public static DisplayMode[] getAvailableModes() { + return new DisplayMode[0]; + } + + /** + * Get the name of the currently selected mode. This can return + * null if no mode is selected. + */ + public static DisplayMode getCurrentMode() { + return null; + } + + /** + * Selects a mode from the list of available modes by it's + * string identifier. Returns true on success, false for + * failure. It is up to the implementation to determine + * if this mode is valid. + */ + public static boolean setMode(DisplayMode mode, boolean makeDefault) { + return false; + } + + /** + * Gets the preferred default mode for this device by it's + * string identifier. Can return null if there is no default. + */ + public static DisplayMode getDefaultMode() { + return null; + } +} diff --git a/core/java/com/android/internal/lineage/hardware/HIDLHelper.java b/core/java/com/android/internal/lineage/hardware/HIDLHelper.java new file mode 100644 index 0000000000000..e512478b5829c --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/HIDLHelper.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import android.util.Range; + +import com.android.internal.lineage.hardware.DisplayMode; +import com.android.internal.lineage.hardware.HSIC; + +import java.util.ArrayList; + +class HIDLHelper { + + static TouchscreenGesture[] fromHIDLGestures( + ArrayList gestures) { + int size = gestures.size(); + TouchscreenGesture[] r = new TouchscreenGesture[size]; + for (int i = 0; i < size; i++) { + vendor.lineage.touch.V1_0.Gesture g = gestures.get(i); + r[i] = new TouchscreenGesture(g.id, g.name, g.keycode); + } + return r; + } + + static vendor.lineage.touch.V1_0.Gesture toHIDLGesture(TouchscreenGesture gesture) { + vendor.lineage.touch.V1_0.Gesture g = new vendor.lineage.touch.V1_0.Gesture(); + g.id = gesture.id; + g.name = gesture.name; + g.keycode = gesture.keycode; + return g; + } + + static DisplayMode[] fromHIDLModes( + ArrayList modes) { + int size = modes.size(); + DisplayMode[] r = new DisplayMode[size]; + for (int i = 0; i < size; i++) { + vendor.lineage.livedisplay.V2_0.DisplayMode m = modes.get(i); + r[i] = new DisplayMode(m.id, m.name); + } + return r; + } + + static DisplayMode fromHIDLMode( + vendor.lineage.livedisplay.V2_0.DisplayMode mode) { + return new DisplayMode(mode.id, mode.name); + } + + static HSIC fromHIDLHSIC(vendor.lineage.livedisplay.V2_0.HSIC hsic) { + return new HSIC(hsic.hue, hsic.saturation, hsic.intensity, + hsic.contrast, hsic.saturationThreshold); + } + + static vendor.lineage.livedisplay.V2_0.HSIC toHIDLHSIC(HSIC hsic) { + vendor.lineage.livedisplay.V2_0.HSIC h = new vendor.lineage.livedisplay.V2_0.HSIC(); + h.hue = hsic.getHue(); + h.saturation = hsic.getSaturation(); + h.intensity = hsic.getIntensity(); + h.contrast = hsic.getContrast(); + h.saturationThreshold = hsic.getSaturationThreshold(); + return h; + } + + static Range fromHIDLRange(vendor.lineage.livedisplay.V2_0.Range range) { + return new Range(range.min, range.max); + } + + static Range fromHIDLRange(vendor.lineage.livedisplay.V2_0.FloatRange range) { + return new Range(range.min, range.max); + } + +} diff --git a/core/java/com/android/internal/lineage/hardware/HSIC.aidl b/core/java/com/android/internal/lineage/hardware/HSIC.aidl new file mode 100644 index 0000000000000..e8b2e4d205225 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/HSIC.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.internal.lineage.hardware; + +parcelable HSIC; diff --git a/core/java/com/android/internal/lineage/hardware/HSIC.java b/core/java/com/android/internal/lineage/hardware/HSIC.java new file mode 100644 index 0000000000000..d7531a68206c3 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/HSIC.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.lineage.hardware; + +import android.graphics.Color; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.Locale; + +public class HSIC implements Parcelable { + + private final float mHue; + private final float mSaturation; + private final float mIntensity; + private final float mContrast; + private final float mSaturationThreshold; + + public HSIC(float hue, float saturation, float intensity, + float contrast, float saturationThreshold) { + mHue = hue; + mSaturation = saturation; + mIntensity = intensity; + mContrast = contrast; + mSaturationThreshold = saturationThreshold; + } + + public float getHue() { + return mHue; + } + + public float getSaturation() { + return mSaturation; + } + + public float getIntensity() { + return mIntensity; + } + + public float getContrast() { + return mContrast; + } + + public float getSaturationThreshold() { + return mSaturationThreshold; + } + + public String flatten() { + return String.format(Locale.US, "%f|%f|%f|%f|%f", mHue, mSaturation, + mIntensity, mContrast, mSaturationThreshold); + } + + public static HSIC unflattenFrom(String flat) throws NumberFormatException { + final String[] unflat = TextUtils.split(flat, "\\|"); + if (unflat.length != 4 && unflat.length != 5) { + throw new NumberFormatException("Failed to unflatten HSIC values: " + flat); + } + return new HSIC(Float.parseFloat(unflat[0]), Float.parseFloat(unflat[1]), + Float.parseFloat(unflat[2]), Float.parseFloat(unflat[3]), + unflat.length == 5 ? Float.parseFloat(unflat[4]) : 0.0f); + } + + public int[] toRGB() { + final int c = Color.HSVToColor(toFloatArray()); + return new int[] { Color.red(c), Color.green(c), Color.blue(c) }; + } + + public float[] toFloatArray() { + return new float[] { mHue, mSaturation, mIntensity, mContrast, mSaturationThreshold }; + } + + public static HSIC fromFloatArray(float[] hsic) { + if (hsic.length == 5) { + return new HSIC(hsic[0], hsic[1], hsic[2], hsic[3], hsic[4]); + } else if (hsic.length == 4) { + return new HSIC(hsic[0], hsic[1], hsic[2], hsic[3], 0.0f); + } + return null; + } + + @Override + public String toString() { + return String.format(Locale.US, "HSIC={ hue=%f saturation=%f intensity=%f " + + "contrast=%f saturationThreshold=%f }", + mHue, mSaturation, mIntensity, mContrast, mSaturationThreshold); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeFloatArray(toFloatArray()); + } + + /** + * @hide + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public HSIC createFromParcel(Parcel in) { + float[] fromParcel = new float[5]; + in.readFloatArray(fromParcel); + return HSIC.fromFloatArray(fromParcel); + } + + @Override + public HSIC[] newArray(int size) { + return new HSIC[size]; + } + }; +}; diff --git a/core/java/com/android/internal/lineage/hardware/ILineageHardwareService.aidl b/core/java/com/android/internal/lineage/hardware/ILineageHardwareService.aidl new file mode 100644 index 0000000000000..d25dbb98e180e --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/ILineageHardwareService.aidl @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2015-2016 The CyanogenMod Project + * 2017-2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import com.android.internal.lineage.hardware.DisplayMode; +import com.android.internal.lineage.hardware.HSIC; + +/** @hide */ +interface ILineageHardwareService { + + int getSupportedFeatures(); + boolean get(int feature); + boolean set(int feature, boolean enable); + + int[] getDisplayColorCalibration(); + boolean setDisplayColorCalibration(in int[] rgb); + + boolean requireAdaptiveBacklightForSunlightEnhancement(); + + DisplayMode[] getDisplayModes(); + DisplayMode getCurrentDisplayMode(); + DisplayMode getDefaultDisplayMode(); + boolean setDisplayMode(in DisplayMode mode, boolean makeDefault); + + boolean isSunlightEnhancementSelfManaged(); + + int getColorBalanceMin(); + int getColorBalanceMax(); + int getColorBalance(); + boolean setColorBalance(int value); + + HSIC getPictureAdjustment(); + HSIC getDefaultPictureAdjustment(); + boolean setPictureAdjustment(in HSIC hsic); + float[] getPictureAdjustmentRanges(); +} diff --git a/core/java/com/android/internal/lineage/hardware/ILiveDisplayService.aidl b/core/java/com/android/internal/lineage/hardware/ILiveDisplayService.aidl new file mode 100644 index 0000000000000..57a2302756e47 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/ILiveDisplayService.aidl @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2016, The CyanogenMod Project + * 2021 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import com.android.internal.lineage.hardware.HSIC; +import com.android.internal.lineage.hardware.LiveDisplayConfig; + +/** @hide */ +interface ILiveDisplayService { + LiveDisplayConfig getConfig(); + + int getMode(); + boolean setMode(int mode); + + float[] getColorAdjustment(); + boolean setColorAdjustment(in float[] adj); + + boolean isAutoContrastEnabled(); + boolean setAutoContrastEnabled(boolean enabled); + + boolean isCABCEnabled(); + boolean setCABCEnabled(boolean enabled); + + boolean isColorEnhancementEnabled(); + boolean setColorEnhancementEnabled(boolean enabled); + + int getDayColorTemperature(); + boolean setDayColorTemperature(int temperature); + + int getNightColorTemperature(); + boolean setNightColorTemperature(int temperature); + + int getColorTemperature(); + + boolean isAutomaticOutdoorModeEnabled(); + boolean setAutomaticOutdoorModeEnabled(boolean enabled); + + HSIC getPictureAdjustment(); + HSIC getDefaultPictureAdjustment(); + boolean setPictureAdjustment(in HSIC adj); + boolean isNight(); + + boolean isAntiFlickerEnabled(); + boolean setAntiFlickerEnabled(boolean enabled); +} diff --git a/core/java/com/android/internal/lineage/hardware/LineageHardwareManager.java b/core/java/com/android/internal/lineage/hardware/LineageHardwareManager.java new file mode 100644 index 0000000000000..c9322bfbcc3f2 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/LineageHardwareManager.java @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2015-2016 The CyanogenMod Project + * 2017-2021 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.lineage.hardware; + +import android.content.Context; +import android.hidl.base.V1_0.IBase; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Range; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +import com.android.internal.lineage.app.LineageContextConstants; +import com.android.internal.lineage.hardware.DisplayMode; +import com.android.internal.lineage.hardware.HIDLHelper; +import com.android.internal.lineage.hardware.HSIC; + +import vendor.lineage.livedisplay.V2_0.IAdaptiveBacklight; +import vendor.lineage.livedisplay.V2_0.IAutoContrast; +import vendor.lineage.livedisplay.V2_0.IColorBalance; +import vendor.lineage.livedisplay.V2_0.IColorEnhancement; +import vendor.lineage.livedisplay.V2_0.IDisplayColorCalibration; +import vendor.lineage.livedisplay.V2_0.IDisplayModes; +import vendor.lineage.livedisplay.V2_0.IPictureAdjustment; +import vendor.lineage.livedisplay.V2_0.IReadingEnhancement; +import vendor.lineage.livedisplay.V2_0.ISunlightEnhancement; +import vendor.lineage.livedisplay.V2_1.IAntiFlicker; +import vendor.lineage.touch.V1_0.IGloveMode; +import vendor.lineage.touch.V1_0.IHighTouchPollingRate; +import vendor.lineage.touch.V1_0.IKeyDisabler; +import vendor.lineage.touch.V1_0.IKeySwapper; +import vendor.lineage.touch.V1_0.IStylusMode; +import vendor.lineage.touch.V1_0.ITouchscreenGesture; + +import java.io.UnsupportedEncodingException; +import java.lang.IllegalArgumentException; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Manages access to LineageOS hardware extensions + * + *

+ * This manager requires the HARDWARE_ABSTRACTION_ACCESS permission. + *

+ * To get the instance of this class, utilize LineageHardwareManager#getInstance(Context context) + */ +public final class LineageHardwareManager { + private static final String TAG = "LineageHardwareManager"; + + // The VisibleForTesting annotation is to ensure Proguard doesn't remove these + // fields, as they might be used via reflection. When the @Keep annotation in + // the support library is properly handled in the platform, we should change this. + + /** + * High Touch Polling Rate + */ + @VisibleForTesting + public static final int FEATURE_HIGH_TOUCH_POLLING_RATE = 0x8; + + /** + * High touch sensitivity for touch panels + */ + @VisibleForTesting + public static final int FEATURE_HIGH_TOUCH_SENSITIVITY = 0x10; + + /** + * Hardware navigation key disablement + */ + @VisibleForTesting + public static final int FEATURE_KEY_DISABLE = 0x20; + + /** + * Hardware navigation key swapping + */ + @VisibleForTesting + public static final int FEATURE_KEY_SWAP = 0x40; + + /** + * Touchscreen hovering + */ + @VisibleForTesting + public static final int FEATURE_TOUCH_HOVERING = 0x800; + + /** + * Touchscreen gesture + */ + @VisibleForTesting + public static final int FEATURE_TOUCHSCREEN_GESTURES = 0x80000; + + /** + * Adaptive backlight support (this refers to technologies like NVIDIA SmartDimmer, + * QCOM CABL or Samsung CABC) + */ + @VisibleForTesting + public static final int FEATURE_ADAPTIVE_BACKLIGHT = 0x1; + + /** + * Color enhancement support + */ + @VisibleForTesting + public static final int FEATURE_COLOR_ENHANCEMENT = 0x2; + + /** + * Display RGB color calibration + */ + @VisibleForTesting + public static final int FEATURE_DISPLAY_COLOR_CALIBRATION = 0x4; + + /** + * Increased display readability in bright light + */ + @VisibleForTesting + public static final int FEATURE_SUNLIGHT_ENHANCEMENT = 0x100; + + /** + * Auto contrast + */ + @VisibleForTesting + public static final int FEATURE_AUTO_CONTRAST = 0x1000; + + /** + * Display modes + */ + @VisibleForTesting + public static final int FEATURE_DISPLAY_MODES = 0x2000; + + /** + * Reading mode + */ + @VisibleForTesting + public static final int FEATURE_READING_ENHANCEMENT = 0x4000; + + /** + * Color balance + */ + @VisibleForTesting + public static final int FEATURE_COLOR_BALANCE = 0x20000; + + /** + * HSIC picture adjustment + */ + @VisibleForTesting + public static final int FEATURE_PICTURE_ADJUSTMENT = 0x40000; + + /** + * Anti flicker mode + */ + @VisibleForTesting + public static final int FEATURE_ANTI_FLICKER = 0x200000; + + private static final List BOOLEAN_FEATURES = Arrays.asList( + FEATURE_HIGH_TOUCH_POLLING_RATE, + FEATURE_HIGH_TOUCH_SENSITIVITY, + FEATURE_KEY_DISABLE, + FEATURE_KEY_SWAP, + FEATURE_TOUCH_HOVERING, + FEATURE_ADAPTIVE_BACKLIGHT, + FEATURE_ANTI_FLICKER, + FEATURE_AUTO_CONTRAST, + FEATURE_COLOR_ENHANCEMENT, + FEATURE_SUNLIGHT_ENHANCEMENT, + FEATURE_READING_ENHANCEMENT + ); + + private static ILineageHardwareService sService; + private static LineageHardwareManager sLineageHardwareManagerInstance; + + private Context mContext; + + private final ArrayMap mDisplayModeMappings = new ArrayMap(); + private final boolean mFilterDisplayModes; + + // HIDL hals + private HashMap mHIDLMap = new HashMap(); + + /** + * @hide to prevent subclassing from outside of the framework + */ + private LineageHardwareManager(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + sService = getService(); + + if (!checkService()) { + Log.wtf(TAG, "Unable to get LineageHardwareService. The service either" + + " crashed, was not started, or the interface has been called to early in" + + " SystemServer init"); + } + + final String[] mappings = mContext.getResources().getStringArray( + com.android.internal.R.array.config_displayModeMappings); + if (mappings != null && mappings.length > 0) { + for (String mapping : mappings) { + String[] split = mapping.split(":"); + if (split.length == 2) { + mDisplayModeMappings.put(split[0], split[1]); + } + } + } + mFilterDisplayModes = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_filterDisplayModes); + } + + /** + * Get or create an instance of the {@link com.android.internal.lineage.hardware.LineageHardwareManager} + * @param context + * @return {@link LineageHardwareManager} + */ + public static LineageHardwareManager getInstance(Context context) { + if (sLineageHardwareManagerInstance == null) { + sLineageHardwareManagerInstance = new LineageHardwareManager(context); + } + return sLineageHardwareManagerInstance; + } + + /** @hide */ + public static ILineageHardwareService getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(LineageContextConstants.LINEAGE_HARDWARE_SERVICE); + if (b != null) { + sService = ILineageHardwareService.Stub.asInterface(b); + return sService; + } + return null; + } + + /** + * Determine if a Lineage Hardware feature is supported on this device + * + * @param feature The Lineage Hardware feature to query + * + * @return true if the feature is supported, false otherwise. + */ + public boolean isSupported(int feature) { + return isSupportedHIDL(feature) || isSupportedLegacy(feature); + } + + private boolean isSupportedHIDL(int feature) { + if (!mHIDLMap.containsKey(feature)) { + mHIDLMap.put(feature, getHIDLService(feature)); + } + return mHIDLMap.get(feature) != null; + } + + private boolean isSupportedLegacy(int feature) { + try { + if (checkService()) { + return feature == (sService.getSupportedFeatures() & feature); + } + } catch (RemoteException e) { + } + return false; + } + + private IBase getHIDLService(int feature) { + try { + switch (feature) { + case FEATURE_HIGH_TOUCH_POLLING_RATE: + return IHighTouchPollingRate.getService(true); + case FEATURE_HIGH_TOUCH_SENSITIVITY: + return IGloveMode.getService(true); + case FEATURE_KEY_DISABLE: + return IKeyDisabler.getService(true); + case FEATURE_KEY_SWAP: + return IKeySwapper.getService(true); + case FEATURE_TOUCH_HOVERING: + return IStylusMode.getService(true); + case FEATURE_TOUCHSCREEN_GESTURES: + return ITouchscreenGesture.getService(true); + case FEATURE_ADAPTIVE_BACKLIGHT: + return IAdaptiveBacklight.getService(true); + case FEATURE_ANTI_FLICKER: + return IAntiFlicker.getService(true); + case FEATURE_AUTO_CONTRAST: + return IAutoContrast.getService(true); + case FEATURE_COLOR_BALANCE: + return IColorBalance.getService(true); + case FEATURE_COLOR_ENHANCEMENT: + return IColorEnhancement.getService(true); + case FEATURE_DISPLAY_COLOR_CALIBRATION: + return IDisplayColorCalibration.getService(true); + case FEATURE_DISPLAY_MODES: + return IDisplayModes.getService(true); + case FEATURE_PICTURE_ADJUSTMENT: + return IPictureAdjustment.getService(true); + case FEATURE_READING_ENHANCEMENT: + return IReadingEnhancement.getService(true); + case FEATURE_SUNLIGHT_ENHANCEMENT: + return ISunlightEnhancement.getService(true); + } + } catch (NoSuchElementException | RemoteException e) { + } + return null; + } + + /** + * String version for preference constraints + * + * @hide + */ + public boolean isSupported(String feature) { + if (!feature.startsWith("FEATURE_")) { + return false; + } + try { + Field f = getClass().getField(feature); + if (f != null) { + return isSupported((int) f.get(null)); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.d(TAG, e.getMessage(), e); + } + + return false; + } + /** + * Determine if the given feature is enabled or disabled. + * + * Only used for features which have simple enable/disable controls. + * + * @param feature the Lineage Hardware feature to query + * + * @return true if the feature is enabled, false otherwise. + */ + public boolean get(int feature) { + if (!BOOLEAN_FEATURES.contains(feature)) { + throw new IllegalArgumentException(feature + " is not a boolean"); + } + + try { + if (isSupportedHIDL(feature)) { + IBase obj = mHIDLMap.get(feature); + switch (feature) { + case FEATURE_HIGH_TOUCH_POLLING_RATE: + IHighTouchPollingRate highTouchPollingRate = (IHighTouchPollingRate) obj; + return highTouchPollingRate.isEnabled(); + case FEATURE_HIGH_TOUCH_SENSITIVITY: + IGloveMode gloveMode = (IGloveMode) obj; + return gloveMode.isEnabled(); + case FEATURE_KEY_DISABLE: + IKeyDisabler keyDisabler = (IKeyDisabler) obj; + return keyDisabler.isEnabled(); + case FEATURE_KEY_SWAP: + IKeySwapper keySwapper = (IKeySwapper) obj; + return keySwapper.isEnabled(); + case FEATURE_TOUCH_HOVERING: + IStylusMode stylusMode = (IStylusMode) obj; + return stylusMode.isEnabled(); + case FEATURE_ADAPTIVE_BACKLIGHT: + IAdaptiveBacklight adaptiveBacklight = (IAdaptiveBacklight) obj; + return adaptiveBacklight.isEnabled(); + case FEATURE_ANTI_FLICKER: + IAntiFlicker antiFlicker = (IAntiFlicker) obj; + return antiFlicker.isEnabled(); + case FEATURE_AUTO_CONTRAST: + IAutoContrast autoContrast = (IAutoContrast) obj; + return autoContrast.isEnabled(); + case FEATURE_COLOR_ENHANCEMENT: + IColorEnhancement colorEnhancement = (IColorEnhancement) obj; + return colorEnhancement.isEnabled(); + case FEATURE_SUNLIGHT_ENHANCEMENT: + ISunlightEnhancement sunlightEnhancement = (ISunlightEnhancement) obj; + return sunlightEnhancement.isEnabled(); + case FEATURE_READING_ENHANCEMENT: + IReadingEnhancement readingEnhancement = (IReadingEnhancement) obj; + return readingEnhancement.isEnabled(); + } + } else if (checkService()) { + return sService.get(feature); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * Enable or disable the given feature + * + * Only used for features which have simple enable/disable controls. + * + * @param feature the Lineage Hardware feature to set + * @param enable true to enable, false to disale + * + * @return true if the feature is enabled, false otherwise. + */ + public boolean set(int feature, boolean enable) { + if (!BOOLEAN_FEATURES.contains(feature)) { + throw new IllegalArgumentException(feature + " is not a boolean"); + } + + try { + if (isSupportedHIDL(feature)) { + IBase obj = mHIDLMap.get(feature); + switch (feature) { + case FEATURE_HIGH_TOUCH_POLLING_RATE: + IHighTouchPollingRate highTouchPollingRate = (IHighTouchPollingRate) obj; + return highTouchPollingRate.setEnabled(enable); + case FEATURE_HIGH_TOUCH_SENSITIVITY: + IGloveMode gloveMode = (IGloveMode) obj; + return gloveMode.setEnabled(enable); + case FEATURE_KEY_DISABLE: + IKeyDisabler keyDisabler = (IKeyDisabler) obj; + return keyDisabler.setEnabled(enable); + case FEATURE_KEY_SWAP: + IKeySwapper keySwapper = (IKeySwapper) obj; + return keySwapper.setEnabled(enable); + case FEATURE_TOUCH_HOVERING: + IStylusMode stylusMode = (IStylusMode) obj; + return stylusMode.setEnabled(enable); + case FEATURE_ADAPTIVE_BACKLIGHT: + IAdaptiveBacklight adaptiveBacklight = (IAdaptiveBacklight) obj; + return adaptiveBacklight.setEnabled(enable); + case FEATURE_ANTI_FLICKER: + IAntiFlicker antiFlicker = (IAntiFlicker) obj; + return antiFlicker.setEnabled(enable); + case FEATURE_AUTO_CONTRAST: + IAutoContrast autoContrast = (IAutoContrast) obj; + return autoContrast.setEnabled(enable); + case FEATURE_COLOR_ENHANCEMENT: + IColorEnhancement colorEnhancement = (IColorEnhancement) obj; + return colorEnhancement.setEnabled(enable); + case FEATURE_SUNLIGHT_ENHANCEMENT: + ISunlightEnhancement sunlightEnhancement = (ISunlightEnhancement) obj; + return sunlightEnhancement.setEnabled(enable); + case FEATURE_READING_ENHANCEMENT: + IReadingEnhancement readingEnhancement = (IReadingEnhancement) obj; + return readingEnhancement.setEnabled(enable); + } + } else if (checkService()) { + return sService.set(feature, enable); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * @return a list of available touchscreen gestures on the devices + */ + public TouchscreenGesture[] getTouchscreenGestures() { + try { + if (isSupportedHIDL(FEATURE_TOUCHSCREEN_GESTURES)) { + ITouchscreenGesture touchscreenGesture = (ITouchscreenGesture) + mHIDLMap.get(FEATURE_TOUCHSCREEN_GESTURES); + return HIDLHelper.fromHIDLGestures(touchscreenGesture.getSupportedGestures()); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * @return true if setting the activation status was successful + */ + public boolean setTouchscreenGestureEnabled( + TouchscreenGesture gesture, boolean state) { + try { + if (isSupportedHIDL(FEATURE_TOUCHSCREEN_GESTURES)) { + ITouchscreenGesture touchscreenGesture = (ITouchscreenGesture) + mHIDLMap.get(FEATURE_TOUCHSCREEN_GESTURES); + return touchscreenGesture.setGestureEnabled( + HIDLHelper.toHIDLGesture(gesture), state); + } + } catch (RemoteException e) { + } + return false; + } + + private int getArrayValue(int[] arr, int idx, int defaultValue) { + if (arr == null || arr.length <= idx) { + return defaultValue; + } + + return arr[idx]; + } + + /** + * {@hide} + */ + public static final int COLOR_CALIBRATION_RED_INDEX = 0; + /** + * {@hide} + */ + public static final int COLOR_CALIBRATION_GREEN_INDEX = 1; + /** + * {@hide} + */ + public static final int COLOR_CALIBRATION_BLUE_INDEX = 2; + /** + * {@hide} + */ + public static final int COLOR_CALIBRATION_MIN_INDEX = 3; + /** + * {@hide} + */ + public static final int COLOR_CALIBRATION_MAX_INDEX = 4; + + private int[] getDisplayColorCalibrationArray() { + try { + if (isSupportedHIDL(FEATURE_DISPLAY_COLOR_CALIBRATION)) { + IDisplayColorCalibration displayColorCalibration = (IDisplayColorCalibration) + mHIDLMap.get(FEATURE_DISPLAY_COLOR_CALIBRATION); + return ArrayUtils.convertToIntArray(displayColorCalibration.getCalibration()); + } else if (checkService()) { + return sService.getDisplayColorCalibration(); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * @return the current RGB calibration, where int[0] = R, int[1] = G, int[2] = B. + */ + public int[] getDisplayColorCalibration() { + int[] arr = getDisplayColorCalibrationArray(); + if (arr == null || arr.length < 3) { + return null; + } + return Arrays.copyOf(arr, 3); + } + + /** + * @return The minimum value for all colors + */ + public int getDisplayColorCalibrationMin() { + if (isSupportedHIDL(FEATURE_DISPLAY_COLOR_CALIBRATION)) { + IDisplayColorCalibration displayColorCalibration = (IDisplayColorCalibration) + mHIDLMap.get(FEATURE_DISPLAY_COLOR_CALIBRATION); + try { + return displayColorCalibration.getMinValue(); + } catch (RemoteException e) { + return 0; + } + } + + return getArrayValue(getDisplayColorCalibrationArray(), COLOR_CALIBRATION_MIN_INDEX, 0); + } + + /** + * @return The maximum value for all colors + */ + public int getDisplayColorCalibrationMax() { + if (isSupportedHIDL(FEATURE_DISPLAY_COLOR_CALIBRATION)) { + IDisplayColorCalibration displayColorCalibration = (IDisplayColorCalibration) + mHIDLMap.get(FEATURE_DISPLAY_COLOR_CALIBRATION); + try { + return displayColorCalibration.getMaxValue(); + } catch (RemoteException e) { + return 0; + } + } + + return getArrayValue(getDisplayColorCalibrationArray(), COLOR_CALIBRATION_MAX_INDEX, 0); + } + + /** + * Set the display color calibration to the given rgb triplet + * + * @param rgb RGB color calibration. Each value must be between + * {@link #getDisplayColorCalibrationMin()} and {@link #getDisplayColorCalibrationMax()}, + * inclusive. + * + * @return true on success, false otherwise. + */ + public boolean setDisplayColorCalibration(int[] rgb) { + try { + if (isSupportedHIDL(FEATURE_DISPLAY_COLOR_CALIBRATION)) { + IDisplayColorCalibration displayColorCalibration = (IDisplayColorCalibration) + mHIDLMap.get(FEATURE_DISPLAY_COLOR_CALIBRATION); + return displayColorCalibration.setCalibration( + new ArrayList(Arrays.asList(rgb[0], rgb[1], rgb[2]))); + } else if (checkService()) { + return sService.setDisplayColorCalibration(rgb); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * @return true if adaptive backlight should be enabled when sunlight enhancement + * is enabled. + */ + public boolean requireAdaptiveBacklightForSunlightEnhancement() { + if (isSupportedHIDL(FEATURE_SUNLIGHT_ENHANCEMENT)) { + return false; + } + + try { + if (checkService()) { + return sService.requireAdaptiveBacklightForSunlightEnhancement(); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * @return true if this implementation does it's own lux metering + */ + public boolean isSunlightEnhancementSelfManaged() { + if (isSupportedHIDL(FEATURE_SUNLIGHT_ENHANCEMENT)) { + return false; + } + + try { + if (checkService()) { + return sService.isSunlightEnhancementSelfManaged(); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * @return a list of available display modes on the devices + */ + public DisplayMode[] getDisplayModes() { + DisplayMode[] modes = null; + try { + if (isSupportedHIDL(FEATURE_DISPLAY_MODES)) { + IDisplayModes displayModes = (IDisplayModes) mHIDLMap.get(FEATURE_DISPLAY_MODES); + modes = HIDLHelper.fromHIDLModes(displayModes.getDisplayModes()); + } else if (checkService()) { + modes= sService.getDisplayModes(); + } + } catch (RemoteException e) { + } finally { + if (modes == null) { + return null; + } + final ArrayList remapped = new ArrayList(); + for (DisplayMode mode : modes) { + DisplayMode r = remapDisplayMode(mode); + if (r != null) { + remapped.add(r); + } + } + return remapped.toArray(new DisplayMode[0]); + } + } + + /** + * @return the currently active display mode + */ + public DisplayMode getCurrentDisplayMode() { + DisplayMode mode = null; + try { + if (isSupportedHIDL(FEATURE_DISPLAY_MODES)) { + IDisplayModes displayModes = (IDisplayModes) mHIDLMap.get(FEATURE_DISPLAY_MODES); + mode = HIDLHelper.fromHIDLMode(displayModes.getCurrentDisplayMode()); + } else if (checkService()) { + mode = sService.getCurrentDisplayMode(); + } + } catch (RemoteException e) { + } finally { + return mode != null ? remapDisplayMode(mode) : null; + } + } + + /** + * @return the default display mode to be set on boot + */ + public DisplayMode getDefaultDisplayMode() { + DisplayMode mode = null; + try { + if (isSupportedHIDL(FEATURE_DISPLAY_MODES)) { + IDisplayModes displayModes = (IDisplayModes) mHIDLMap.get(FEATURE_DISPLAY_MODES); + mode = HIDLHelper.fromHIDLMode(displayModes.getDefaultDisplayMode()); + } else if (checkService()) { + mode = sService.getDefaultDisplayMode(); + } + } catch (RemoteException e) { + } finally { + return mode != null ? remapDisplayMode(mode) : null; + } + } + + /** + * @return true if setting the mode was successful + */ + public boolean setDisplayMode(DisplayMode mode, boolean makeDefault) { + try { + if (isSupportedHIDL(FEATURE_DISPLAY_MODES)) { + IDisplayModes displayModes = (IDisplayModes) mHIDLMap.get(FEATURE_DISPLAY_MODES); + return displayModes.setDisplayMode(mode.id, makeDefault); + } else if (checkService()) { + return sService.setDisplayMode(mode, makeDefault); + } + } catch (RemoteException e) { + } + return false; + } + + private DisplayMode remapDisplayMode(DisplayMode in) { + if (in == null) { + return null; + } + if (mDisplayModeMappings.containsKey(in.name)) { + return new DisplayMode(in.id, mDisplayModeMappings.get(in.name)); + } + if (!mFilterDisplayModes) { + return in; + } + return null; + } + + /** + * @return the available range for color temperature adjustments + */ + public Range getColorBalanceRange() { + try { + if (isSupportedHIDL(FEATURE_COLOR_BALANCE)) { + IColorBalance colorBalance = (IColorBalance) mHIDLMap.get(FEATURE_COLOR_BALANCE); + return HIDLHelper.fromHIDLRange(colorBalance.getColorBalanceRange()); + } else if (checkService()) { + return new Range( + sService.getColorBalanceMin(), + sService.getColorBalanceMax()); + } + } catch (RemoteException e) { + } + return new Range(0, 0); + } + + /** + * @return the current color balance value + */ + public int getColorBalance() { + try { + if (isSupportedHIDL(FEATURE_COLOR_BALANCE)) { + IColorBalance colorBalance = (IColorBalance) mHIDLMap.get(FEATURE_COLOR_BALANCE); + return colorBalance.getColorBalance(); + } else if (checkService()) { + return sService.getColorBalance(); + } + } catch (RemoteException e) { + } + return 0; + } + + /** + * Sets the desired color balance. Must fall within the range obtained from + * getColorBalanceRange() + * + * @param value + * @return true if success + */ + public boolean setColorBalance(int value) { + try { + if (isSupportedHIDL(FEATURE_COLOR_BALANCE)) { + IColorBalance colorBalance = (IColorBalance) mHIDLMap.get(FEATURE_COLOR_BALANCE); + return colorBalance.setColorBalance(value); + } else if (checkService()) { + return sService.setColorBalance(value); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * Gets the current picture adjustment values + * + * @return HSIC object with current settings + */ + public HSIC getPictureAdjustment() { + try { + if (isSupportedHIDL(FEATURE_PICTURE_ADJUSTMENT)) { + IPictureAdjustment pictureAdjustment = (IPictureAdjustment) + mHIDLMap.get(FEATURE_PICTURE_ADJUSTMENT); + return HIDLHelper.fromHIDLHSIC(pictureAdjustment.getPictureAdjustment()); + } else if (checkService()) { + return sService.getPictureAdjustment(); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * Gets the default picture adjustment for the current mode + * + * @return HSIC object with default settings + */ + public HSIC getDefaultPictureAdjustment() { + try { + if (isSupportedHIDL(FEATURE_PICTURE_ADJUSTMENT)) { + IPictureAdjustment pictureAdjustment = (IPictureAdjustment) + mHIDLMap.get(FEATURE_PICTURE_ADJUSTMENT); + return HIDLHelper.fromHIDLHSIC(pictureAdjustment.getDefaultPictureAdjustment()); + } else if (checkService()) { + return sService.getDefaultPictureAdjustment(); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * Sets the desired hue/saturation/intensity/contrast + * + * @param hsic + * @return true if success + */ + public boolean setPictureAdjustment(final HSIC hsic) { + try { + if (isSupportedHIDL(FEATURE_PICTURE_ADJUSTMENT)) { + IPictureAdjustment pictureAdjustment = (IPictureAdjustment) + mHIDLMap.get(FEATURE_PICTURE_ADJUSTMENT); + return pictureAdjustment.setPictureAdjustment(HIDLHelper.toHIDLHSIC(hsic)); + } else if (checkService()) { + return sService.setPictureAdjustment(hsic); + } + } catch (RemoteException e) { + } + return false; + } + + /** + * Get a list of ranges valid for picture adjustment. + * + * @return range list + */ + public List> getPictureAdjustmentRanges() { + try { + if (isSupportedHIDL(FEATURE_PICTURE_ADJUSTMENT)) { + IPictureAdjustment pictureAdjustment = (IPictureAdjustment) + mHIDLMap.get(FEATURE_PICTURE_ADJUSTMENT); + return Arrays.asList( + HIDLHelper.fromHIDLRange(pictureAdjustment.getHueRange()), + HIDLHelper.fromHIDLRange(pictureAdjustment.getSaturationRange()), + HIDLHelper.fromHIDLRange(pictureAdjustment.getIntensityRange()), + HIDLHelper.fromHIDLRange(pictureAdjustment.getContrastRange()), + HIDLHelper.fromHIDLRange(pictureAdjustment.getSaturationThresholdRange())); + } else if (checkService()) { + float[] ranges = sService.getPictureAdjustmentRanges(); + if (ranges.length > 7) { + return Arrays.asList(new Range(ranges[0], ranges[1]), + new Range(ranges[2], ranges[3]), + new Range(ranges[4], ranges[5]), + new Range(ranges[6], ranges[7]), + (ranges.length > 9 ? + new Range(ranges[8], ranges[9]) : + new Range(0.0f, 0.0f))); + } + } + } catch (RemoteException e) { + } + return null; + } + + /** + * @return true if service is valid + */ + private boolean checkService() { + if (sService == null) { + Log.w(TAG, "not connected to LineageHardwareManagerService"); + return false; + } + return true; + } + +} diff --git a/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.aidl b/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.aidl new file mode 100644 index 0000000000000..5a9d951e78396 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.lineage.hardware; + +parcelable LiveDisplayConfig; diff --git a/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.java b/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.java new file mode 100644 index 0000000000000..56c17f18dcf83 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/LiveDisplayConfig.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.lineage.hardware; + +import static com.android.internal.lineage.hardware.LiveDisplayManager.FEATURE_COLOR_BALANCE; +import static com.android.internal.lineage.hardware.LiveDisplayManager.FEATURE_FIRST; +import static com.android.internal.lineage.hardware.LiveDisplayManager.FEATURE_LAST; +import static com.android.internal.lineage.hardware.LiveDisplayManager.MODE_FIRST; +import static com.android.internal.lineage.hardware.LiveDisplayManager.MODE_LAST; +import static com.android.internal.lineage.hardware.LiveDisplayManager.MODE_OFF; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Range; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import com.android.internal.lineage.util.Concierge; +import com.android.internal.lineage.util.Concierge.ParcelInfo; + +/** + * Holder class for LiveDisplay static configuration. + * + * This class holds various defaults and hardware capabilities + * which are involved with LiveDisplay. + */ +public class LiveDisplayConfig implements Parcelable { + + private final BitSet mCapabilities; + private final BitSet mAllModes = new BitSet(); + + private final int mDefaultDayTemperature; + private final int mDefaultNightTemperature; + private final int mDefaultMode; + + private final boolean mDefaultAutoContrast; + private final boolean mDefaultAutoOutdoorMode; + private final boolean mDefaultCABC; + private final boolean mDefaultColorEnhancement; + + private final Range mColorTemperatureRange; + private final Range mColorBalanceRange; + private final Range mHueRange; + private final Range mSaturationRange; + private final Range mIntensityRange; + private final Range mContrastRange; + private final Range mSaturationThresholdRange; + + public LiveDisplayConfig(BitSet capabilities, int defaultMode, + int defaultDayTemperature, int defaultNightTemperature, + boolean defaultAutoOutdoorMode, boolean defaultAutoContrast, + boolean defaultCABC, boolean defaultColorEnhancement, + Range colorTemperatureRange, + Range colorBalanceRange, + Range hueRange, + Range saturationRange, + Range intensityRange, + Range contrastRange, + Range saturationThresholdRange) { + super(); + mCapabilities = (BitSet) capabilities.clone(); + mAllModes.set(MODE_FIRST, MODE_LAST); + mDefaultMode = defaultMode; + mDefaultDayTemperature = defaultDayTemperature; + mDefaultNightTemperature = defaultNightTemperature; + mDefaultAutoContrast = defaultAutoContrast; + mDefaultAutoOutdoorMode = defaultAutoOutdoorMode; + mDefaultCABC = defaultCABC; + mDefaultColorEnhancement = defaultColorEnhancement; + mColorTemperatureRange = colorTemperatureRange; + mColorBalanceRange = colorBalanceRange; + mHueRange = hueRange; + mSaturationRange = saturationRange; + mIntensityRange = intensityRange; + mContrastRange = contrastRange; + mSaturationThresholdRange = saturationThresholdRange; + } + + private LiveDisplayConfig(Parcel parcel) { + // Read parcelable version via the Concierge + ParcelInfo parcelInfo = Concierge.receiveParcel(parcel); + int parcelableVersion = parcelInfo.getParcelVersion(); + + // temp vars + long capabilities = 0; + int defaultMode = 0; + int defaultDayTemperature = -1; + int defaultNightTemperature = -1; + boolean defaultAutoContrast = false; + boolean defaultAutoOutdoorMode = false; + boolean defaultCABC = false; + boolean defaultColorEnhancement = false; + int minColorTemperature = 0; + int maxColorTemperature = 0; + int minColorBalance = 0; + int maxColorBalance = 0; + float[] paRanges = new float[10]; + + capabilities = parcel.readLong(); + defaultMode = parcel.readInt(); + defaultDayTemperature = parcel.readInt(); + defaultNightTemperature = parcel.readInt(); + defaultAutoContrast = parcel.readInt() == 1; + defaultAutoOutdoorMode = parcel.readInt() == 1; + defaultCABC = parcel.readInt() == 1; + defaultColorEnhancement = parcel.readInt() == 1; + minColorTemperature = parcel.readInt(); + maxColorTemperature = parcel.readInt(); + minColorBalance = parcel.readInt(); + maxColorBalance = parcel.readInt(); + parcel.readFloatArray(paRanges); + + // set temps + mCapabilities = BitSet.valueOf(new long[] { capabilities }); + mAllModes.set(MODE_FIRST, MODE_LAST); + mDefaultMode = defaultMode; + mDefaultDayTemperature = defaultDayTemperature; + mDefaultNightTemperature = defaultNightTemperature; + mDefaultAutoContrast = defaultAutoContrast; + mDefaultAutoOutdoorMode = defaultAutoOutdoorMode; + mDefaultCABC = defaultCABC; + mDefaultColorEnhancement = defaultColorEnhancement; + mColorTemperatureRange = Range.create(minColorTemperature, maxColorTemperature); + mColorBalanceRange = Range.create(minColorBalance, maxColorBalance); + mHueRange = Range.create(paRanges[0], paRanges[1]); + mSaturationRange = Range.create(paRanges[2], paRanges[3]); + mIntensityRange = Range.create(paRanges[4], paRanges[5]); + mContrastRange = Range.create(paRanges[6], paRanges[7]); + mSaturationThresholdRange = Range.create(paRanges[8], paRanges[9]); + + // Complete parcel info for the concierge + parcelInfo.complete(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("capabilities=").append(mCapabilities.toString()); + sb.append(" defaultMode=").append(mDefaultMode); + sb.append(" defaultDayTemperature=").append(mDefaultDayTemperature); + sb.append(" defaultNightTemperature=").append(mDefaultNightTemperature); + sb.append(" defaultAutoOutdoorMode=").append(mDefaultAutoOutdoorMode); + sb.append(" defaultAutoContrast=").append(mDefaultAutoContrast); + sb.append(" defaultCABC=").append(mDefaultCABC); + sb.append(" defaultColorEnhancement=").append(mDefaultColorEnhancement); + sb.append(" colorTemperatureRange=").append(mColorTemperatureRange); + if (mCapabilities.get(LiveDisplayManager.FEATURE_COLOR_BALANCE)) { + sb.append(" colorBalanceRange=").append(mColorBalanceRange); + } + if (mCapabilities.get(LiveDisplayManager.FEATURE_PICTURE_ADJUSTMENT)) { + sb.append(" hueRange=").append(mHueRange); + sb.append(" saturationRange=").append(mSaturationRange); + sb.append(" intensityRange=").append(mIntensityRange); + sb.append(" contrastRange=").append(mContrastRange); + sb.append(" saturationThresholdRange=").append(mSaturationThresholdRange); + } + return sb.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + // Tell the concierge to prepare the parcel + ParcelInfo parcelInfo = Concierge.prepareParcel(out); + + // ==== FIG ===== + long[] caps = mCapabilities.toLongArray(); + out.writeLong(caps != null && caps.length > 0 ? caps[0] : 0L); + out.writeInt(mDefaultMode); + out.writeInt(mDefaultDayTemperature); + out.writeInt(mDefaultNightTemperature); + out.writeInt(mDefaultAutoContrast ? 1 : 0); + out.writeInt(mDefaultAutoOutdoorMode ? 1 : 0); + out.writeInt(mDefaultCABC ? 1 : 0); + out.writeInt(mDefaultColorEnhancement ? 1 : 0); + out.writeInt(mColorTemperatureRange.getLower()); + out.writeInt(mColorTemperatureRange.getUpper()); + out.writeInt(mColorBalanceRange.getLower()); + out.writeInt(mColorBalanceRange.getUpper()); + out.writeFloatArray(new float[] { + mHueRange.getLower(), mHueRange.getUpper(), + mSaturationRange.getLower(), mSaturationRange.getUpper(), + mIntensityRange.getLower(), mIntensityRange.getUpper(), + mContrastRange.getLower(), mContrastRange.getUpper(), + mSaturationThresholdRange.getLower(), mSaturationThresholdRange.getUpper() } ); + + // Complete the parcel info for the concierge + parcelInfo.complete(); + } + + /** + * Checks if a particular feature or mode is supported by the system. + * + * @param feature + * @return true if capable + */ + public boolean hasFeature(int feature) { + return ((feature >= MODE_FIRST && feature <= MODE_LAST) || + (feature >= FEATURE_FIRST && feature <= FEATURE_LAST)) && + (feature == MODE_OFF || mCapabilities.get(feature)); + } + + /** + * Checks if LiveDisplay is available for use on this device. + * + * @return true if any feature is enabled + */ + public boolean isAvailable() { + return !mCapabilities.isEmpty(); + } + + /** + * Checks if LiveDisplay has support for adaptive modes. + * + * @return true if adaptive modes are available + */ + public boolean hasModeSupport() { + return isAvailable() && mCapabilities.intersects(mAllModes); + } + + /** + * Gets the default color temperature to use in the daytime. This is typically + * set to 6500K, however this may not be entirely accurate. Use this value for + * resetting controls to the default. + * + * @return the default day temperature in K + */ + public int getDefaultDayTemperature() { + return mDefaultDayTemperature; + } + + /** + * Gets the default color temperature to use at night. This is typically set + * to 4500K, but this may not be entirely accurate. Use this value for resetting + * controls to defaults. + * + * @return the default night color temperature + */ + public int getDefaultNightTemperature() { + return mDefaultNightTemperature; + } + + /** + * Get the default adaptive mode. + * + * @return the default mode + */ + public int getDefaultMode() { + return mDefaultMode; + } + + /** + * Get the default value for auto contrast + * + * @return true if enabled + */ + public boolean getDefaultAutoContrast() { + return mDefaultAutoContrast; + } + + /** + * Get the default value for automatic outdoor mode + * + * @return true if enabled + */ + public boolean getDefaultAutoOutdoorMode() { + return mDefaultAutoOutdoorMode; + } + + /** + * Get the default value for CABC + * + * @return true if enabled + */ + public boolean getDefaultCABC() { + return mDefaultCABC; + } + + /** + * Get the default value for color enhancement + * + * @return true if enabled + */ + public boolean getDefaultColorEnhancement() { + return mDefaultColorEnhancement; + } + + /** + * Get the range of supported color temperatures + * + * @return range in Kelvin + */ + public Range getColorTemperatureRange() { + return mColorTemperatureRange; + } + + /** + * Get the range of supported color balance + * + * @return linear range which maps into the temperature range curve + */ + public Range getColorBalanceRange() { + return mColorBalanceRange; + } + + /** + * Get the supported range for hue adjustment + * + * @return float range + */ + public Range getHueRange() { return mHueRange; } + + /** + * Get the supported range for saturation adjustment + * + * @return float range + */ + public Range getSaturationRange() { return mSaturationRange; } + + /** + * Get the supported range for intensity adjustment + * + * @return float range + */ + public Range getIntensityRange() { return mIntensityRange; } + + /** + * Get the supported range for contrast adjustment + * + * @return float range + */ + public Range getContrastRange() { return mContrastRange; } + + /** + * Get the supported range for saturation threshold adjustment + * + * @return float range + */ + public Range getSaturationThresholdRange() { + return mSaturationThresholdRange; + } + + + /** + * Convenience method to get a list of all picture adjustment ranges + * with a single call. + * + * @return List of float ranges + */ + public List> getPictureAdjustmentRanges() { + return Arrays.asList(mHueRange, mSaturationRange, mIntensityRange, + mContrastRange, mSaturationThresholdRange); + } + + /** @hide */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public LiveDisplayConfig createFromParcel(Parcel in) { + return new LiveDisplayConfig(in); + } + + @Override + public LiveDisplayConfig[] newArray(int size) { + return new LiveDisplayConfig[size]; + } + }; +} diff --git a/core/java/com/android/internal/lineage/hardware/LiveDisplayManager.java b/core/java/com/android/internal/lineage/hardware/LiveDisplayManager.java new file mode 100644 index 0000000000000..a97c8a95fa8d5 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/LiveDisplayManager.java @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2018-2021 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.lineage.hardware; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.util.Range; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.android.internal.lineage.app.LineageContextConstants; + +/** + * LiveDisplay is an advanced set of features for improving + * display quality under various ambient conditions. + * + * The backend service is constructed with a set of LiveDisplayFeatures + * which provide capabilities such as outdoor mode, night mode, + * and calibration. It interacts with LineageHardwareService to relay + * changes down to the lower layers. + * + * Multiple adaptive modes are supported, and various hardware + * features such as CABC, ACO and color enhancement are also + * managed by LiveDisplay. + */ +public class LiveDisplayManager { + + /** + * Disable all LiveDisplay adaptive features + */ + public static final int MODE_OFF = 0; + + /** + * Change color temperature to night mode + */ + public static final int MODE_NIGHT = 1; + + /** + * Enable automatic detection of appropriate mode + */ + public static final int MODE_AUTO = 2; + + /** + * Increase brightness/contrast/saturation for sunlight + */ + public static final int MODE_OUTDOOR = 3; + + /** + * Change color temperature to day mode, and allow + * detection of outdoor conditions + */ + public static final int MODE_DAY = 4; + + /** @hide */ + public static final int MODE_FIRST = MODE_OFF; + /** @hide */ + public static final int MODE_LAST = MODE_DAY; + + /** + * Content adaptive backlight control, adjust images to + * increase brightness in order to reduce backlight level + */ + public static final int FEATURE_CABC = 10; + + /** + * Adjust images to increase contrast + */ + public static final int FEATURE_AUTO_CONTRAST = 11; + + /** + * Adjust image to improve saturation and color + */ + public static final int FEATURE_COLOR_ENHANCEMENT = 12; + + /** + * Capable of adjusting RGB levels + */ + public static final int FEATURE_COLOR_ADJUSTMENT = 13; + + /** + * System supports outdoor mode, but environmental sensing + * is done by an external application. + */ + public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14; + + /** + * System supports multiple display calibrations + * for different viewing intents. + */ + public static final int FEATURE_DISPLAY_MODES = 15; + + /** + * System supports direct range-based control of display + * color balance (temperature). This is preferred over + * simple RGB adjustment. + */ + public static final int FEATURE_COLOR_BALANCE = 16; + + /** + * System supports manual hue/saturation/intensity/contrast + * adjustment of display. + */ + public static final int FEATURE_PICTURE_ADJUSTMENT = 17; + + /** + * System supports grayscale matrix overlay + */ + public static final int FEATURE_READING_ENHANCEMENT = 18; + + /** + * System supports anti flicker mode + */ + public static final int FEATURE_ANTI_FLICKER = 19; + + public static final int ADJUSTMENT_HUE = 0; + public static final int ADJUSTMENT_SATURATION = 1; + public static final int ADJUSTMENT_INTENSITY = 2; + public static final int ADJUSTMENT_CONTRAST = 3; + + /** @hide */ + public static final int FEATURE_FIRST = FEATURE_CABC; + /** @hide */ + public static final int FEATURE_LAST = FEATURE_ANTI_FLICKER; + + private static final String TAG = "LiveDisplay"; + + private final Context mContext; + private LiveDisplayConfig mConfig; + + private static LiveDisplayManager sInstance; + private static ILiveDisplayService sService; + + /** + * @hide to prevent subclassing from outside of the framework + */ + private LiveDisplayManager(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + sService = getService(); + + if (!checkService()) { + Log.wtf(TAG, "Unable to get LiveDisplayService. The service either" + + " crashed, was not started, or the interface has been called to early in" + + " SystemServer init"); + } + } + + /** + * Get or create an instance of the {@link com.android.internal.lineage.hardware.LiveDisplayManager} + * @param context + * @return {@link LiveDisplayManager} + */ + public synchronized static LiveDisplayManager getInstance(Context context) { + if (sInstance == null) { + sInstance = new LiveDisplayManager(context); + } + return sInstance; + } + + /** @hide */ + public static ILiveDisplayService getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(LineageContextConstants.LINEAGE_LIVEDISPLAY_SERVICE); + if (b != null) { + sService = ILiveDisplayService.Stub.asInterface(b); + return sService; + } + return null; + } + + /** + * @return true if service is valid + */ + private boolean checkService() { + if (sService == null) { + Log.w(TAG, "not connected to LineageHardwareManagerService"); + return false; + } + return true; + } + + /** + * Gets the static configuration and settings. + * + * @return the configuration + */ + public LiveDisplayConfig getConfig() { + try { + if (mConfig == null) { + mConfig = checkService() ? sService.getConfig() : null; + } + return mConfig; + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns the current adaptive mode. + * + * @return id of the selected mode + */ + public int getMode() { + try { + return checkService() ? sService.getMode() : MODE_OFF; + } catch (RemoteException e) { + return MODE_OFF; + } + } + + /** + * Selects a new adaptive mode. + * + * @param mode + * @return true if the mode was selected + */ + public boolean setMode(int mode) { + try { + return checkService() && sService.setMode(mode); + } catch (RemoteException e) { + return false; + } + } + + /** + * Checks if the auto contrast optimization feature is enabled. + * + * @return true if enabled + */ + public boolean isAutoContrastEnabled() { + try { + return checkService() && sService.isAutoContrastEnabled(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sets the state of auto contrast optimization + * + * @param enabled + * @return true if state was changed + */ + public boolean setAutoContrastEnabled(boolean enabled) { + try { + return checkService() && sService.setAutoContrastEnabled(enabled); + } catch (RemoteException e) { + return false; + } + } + + /** + * Checks if the CABC feature is enabled + * + * @return true if enabled + */ + public boolean isCABCEnabled() { + try { + return checkService() && sService.isCABCEnabled(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sets the state of CABC + * + * @param enabled + * @return true if state was changed + */ + public boolean setCABCEnabled(boolean enabled) { + try { + return checkService() && sService.setCABCEnabled(enabled); + } catch (RemoteException e) { + return false; + } + } + + /** + * Checks if the color enhancement feature is enabled + * + * @return true if enabled + */ + public boolean isColorEnhancementEnabled() { + try { + return checkService() && sService.isColorEnhancementEnabled(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sets the state of color enhancement + * + * @param enabled + * @return true if state was changed + */ + public boolean setColorEnhancementEnabled(boolean enabled) { + try { + return checkService() && sService.setColorEnhancementEnabled(enabled); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the user-specified color temperature to use in the daytime. + * + * @return the day color temperature + */ + public int getDayColorTemperature() { + try { + return checkService() ? sService.getDayColorTemperature() : -1; + } catch (RemoteException e) { + return -1; + } + } + + /** + * Sets the color temperature to use in the daytime. + * + * @param temperature + * @return true if state was changed + */ + public boolean setDayColorTemperature(int temperature) { + try { + return checkService() && sService.setDayColorTemperature(temperature); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the user-specified color temperature to use at night. + * + * @return the night color temperature + */ + public int getNightColorTemperature() { + try { + return checkService() ? sService.getNightColorTemperature() : -1; + } catch (RemoteException e) { + return -1; + } + } + + /** + * Sets the color temperature to use at night. + * + * @param temperature + * @return true if state was changed + */ + public boolean setNightColorTemperature(int temperature) { + try { + return checkService() && sService.setNightColorTemperature(temperature); + } catch (RemoteException e) { + return false; + } + } + + /** + * Checks if outdoor mode should be enabled automatically when under extremely high + * ambient light. This is typically around 12000 lux. + * + * @return if outdoor conditions should be detected + */ + public boolean isAutomaticOutdoorModeEnabled() { + try { + return checkService() && sService.isAutomaticOutdoorModeEnabled(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Enables automatic detection of outdoor conditions. Outdoor mode is triggered + * when high ambient light is detected and it's not night. + * + * @param enabled + * @return true if state was changed + */ + public boolean setAutomaticOutdoorModeEnabled(boolean enabled) { + try { + return checkService() && sService.setAutomaticOutdoorModeEnabled(enabled); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the current RGB triplet which is applied as a color adjustment. + * The values are floats between 0 and 1. A setting of { 1.0, 1.0, 1.0 } + * means that no adjustment is made. + * + * @return array of { R, G, B } offsets + */ + public float[] getColorAdjustment() { + try { + if (checkService()) { + return sService.getColorAdjustment(); + } + } catch (RemoteException e) { + } + return new float[] { 1.0f, 1.0f, 1.0f }; + } + + /** + * Sets the color adjustment to use. This can be set by the user to calibrate + * their screen. This should be sent in the format { R, G, B } as floats from + * 0 to 1. A setting of { 1.0, 1.0, 1.0 } means that no adjustment is made. + * The hardware implementation may refuse certain values which make the display + * unreadable, such as { 0, 0, 0 }. This calibration will be combined with other + * internal adjustments, such as night mode, if necessary. + * + * @param array of { R, G, B } offsets + * @return true if state was changed + */ + public boolean setColorAdjustment(float[] adj) { + try { + return checkService() && sService.setColorAdjustment(adj); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the current picture adjustment settings (hue, saturation, intensity, contrast) + * + * @return HSIC object with current settings + */ + public HSIC getPictureAdjustment() { + try { + if (checkService()) { + return sService.getPictureAdjustment(); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * Sets a new picture adjustment + * + * @param hsic + * @return true if success + */ + public boolean setPictureAdjustment(final HSIC hsic) { + try { + return checkService() && sService.setPictureAdjustment(hsic); + } catch (RemoteException e) { + } + return false; + } + + /** + * Gets the default picture adjustment for the current display mode + * + * @return HSIC object with default values + */ + public HSIC getDefaultPictureAdjustment() { + try { + if (checkService()) { + return sService.getDefaultPictureAdjustment(); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * Determine whether night mode is enabled (be it automatic or manual) + */ + public boolean isNightModeEnabled() { + // This method might be called before config has been set up + // so a NPE would have been thrown, just report night mode is disabled instead + try { + return getMode() == MODE_NIGHT || sService.isNight(); + } catch (NullPointerException e) { + Log.w(TAG, "Can\'t check whether night mode is enabled because the service isn\'t ready"); + } catch (RemoteException ignored) { + } + return false; + } + + /** + * Checks if the anti flicker feature is enabled + * + * @return true if enabled + */ + public boolean isAntiFlickerEnabled() { + try { + return checkService() && sService.isAntiFlickerEnabled(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sets the state of anti flicker + * + * @param enabled + * @return true if state was changed + */ + public boolean setAntiFlickerEnabled(boolean enabled) { + try { + return checkService() && sService.setAntiFlickerEnabled(enabled); + } catch (RemoteException e) { + return false; + } + } +} diff --git a/core/java/com/android/internal/lineage/hardware/PictureAdjustment.java b/core/java/com/android/internal/lineage/hardware/PictureAdjustment.java new file mode 100644 index 0000000000000..c8ce51d22d07c --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/PictureAdjustment.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import android.util.Range; + +import com.android.internal.lineage.hardware.HSIC; + +/** + * Picture adjustment support + * + * Allows tuning of hue, saturation, intensity, and contrast levels + * of the display + */ +public class PictureAdjustment { + + /** + * Whether device supports picture adjustment + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return false; + } + + /** + * This method returns the current picture adjustment values based + * on the selected DisplayMode. + * + * @return the HSIC object or null if not supported + */ + public static HSIC getHSIC() { + return null; + } + + /** + * This method returns the default picture adjustment values. + * + * If DisplayModes are available, this may change depending on the + * selected mode. + * + * @return the HSIC object or null if not supported + */ + public static HSIC getDefaultHSIC() { + return null; + } + + /** + * This method allows to set the picture adjustment + * + * @param hsic + * @return boolean Must be false if feature is not supported or the operation + * failed; true in any other case. + */ + public static boolean setHSIC(final HSIC hsic) { + return false; + } + + /** + * Get the range available for hue adjustment + * @return range of floats + */ + public static Range getHueRange() { + return new Range(0.0f, 0.0f); + } + + /** + * Get the range available for saturation adjustment + * @return range of floats + */ + public static Range getSaturationRange() { + return new Range(0.0f, 0.0f); + } + + /** + * Get the range available for intensity adjustment + * @return range of floats + */ + public static Range getIntensityRange() { + return new Range(0.0f, 0.0f); + } + + /** + * Get the range available for contrast adjustment + * @return range of floats + */ + public static Range getContrastRange() { + return new Range(0.0f, 0.0f); + } + + /** + * Get the range available for saturation threshold adjustment + * + * This is the threshold where the display becomes fully saturated + * + * @return range of floats + */ + public static Range getSaturationThresholdRange() { + return new Range(0.0f, 0.0f); + } +} diff --git a/core/java/com/android/internal/lineage/hardware/ReadingEnhancement.java b/core/java/com/android/internal/lineage/hardware/ReadingEnhancement.java new file mode 100644 index 0000000000000..1582b68413125 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/ReadingEnhancement.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import com.android.internal.util.cherish.FileUtils; + +/** + * Reader mode + */ +public class ReadingEnhancement { + + private static final String TAG = "ReadingEnhancement"; + + private static final String FILE_READING = "/sys/class/graphics/fb0/reading_mode"; + + /** + * Whether device supports Reader Mode + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return FileUtils.isFileReadable(FILE_READING) && FileUtils.isFileWritable(FILE_READING); + } + + /** + * This method return the current activation status of Reader Mode + * + * @return boolean Must be false when Reader Mode is not supported or not activated, + * or the operation failed while reading the status; true in any other case. + */ + public static boolean isEnabled() { + return Integer.parseInt(FileUtils.readOneLine(FILE_READING)) > 0; + } + + /** + * This method allows to setup Reader Mode + * + * @param status The new Reader Mode status + * @return boolean Must be false if Reader Mode is not supported or the operation + * failed; true in any other case. + */ + public static boolean setEnabled(boolean status) { + return FileUtils.writeLine(FILE_READING, status ? "1" : "0"); + } + +} diff --git a/core/java/com/android/internal/lineage/hardware/SunlightEnhancement.java b/core/java/com/android/internal/lineage/hardware/SunlightEnhancement.java new file mode 100644 index 0000000000000..1cde746cfc758 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/SunlightEnhancement.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * Copyright (C) 2018 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import android.util.Log; + +import com.android.internal.util.cherish.FileUtils; + +/** + * Facemelt mode! + */ +public class SunlightEnhancement { + + private static final String TAG = "SunlightEnhancement"; + + private static final String FACEMELT_PATH = getFacemeltPath(); + private static final String FACEMELT_MODE = getFacemeltMode(); + + private static final String FILE_HBM = "/sys/class/graphics/fb0/hbm"; + private static final String FILE_SRE = "/sys/class/graphics/fb0/sre"; + + private static String getFacemeltPath() { + if (FileUtils.fileExists(FILE_HBM)) { + return FILE_HBM; + } else { + return FILE_SRE; + } + } + + private static String getFacemeltMode() { + if (FileUtils.fileExists(FILE_HBM)) { + return "1"; + } else { + return "2"; + } + } + + /** + * Whether device supports sunlight enhancement + * + * @return boolean Supported devices must return always true + */ + public static boolean isSupported() { + return FileUtils.isFileReadable(FACEMELT_PATH) && FileUtils.isFileWritable(FACEMELT_PATH); + } + + /** + * This method return the current activation status of sunlight enhancement + * + * @return boolean Must be false when sunlight enhancement is not supported or not activated, + * or the operation failed while reading the status; true in any other case. + */ + public static boolean isEnabled() { + return Integer.parseInt(FileUtils.readOneLine(FACEMELT_PATH)) > 0; + } + + /** + * This method allows to setup sunlight enhancement + * + * @param status The new sunlight enhancement status + * @return boolean Must be false if sunlight enhancement is not supported or the operation + * failed; true in any other case. + */ + public static boolean setEnabled(boolean status) { + return FileUtils.writeLine(FACEMELT_PATH, status ? FACEMELT_MODE : "0"); + } + + /** + * Whether adaptive backlight (CABL / CABC) is required to be enabled + * + * @return boolean False if adaptive backlight is not a dependency + */ + public static boolean isAdaptiveBacklightRequired() { + return false; + } + + /** + * Set this to true if the implementation is self-managed and does + * it's own ambient sensing. In this case, setEnabled is assumed + * to toggle the feature on or off, but not activate it. If set + * to false, LiveDisplay will call setEnabled when the ambient lux + * threshold is crossed. + * + * @return true if this enhancement is self-managed + */ + public static boolean isSelfManaged() { + return false; + } +} diff --git a/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.aidl b/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.aidl new file mode 100644 index 0000000000000..060686bcdc5dc --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +parcelable TouchscreenGesture; diff --git a/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.java b/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.java new file mode 100644 index 0000000000000..e7e6a3b682e33 --- /dev/null +++ b/core/java/com/android/internal/lineage/hardware/TouchscreenGesture.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.lineage.hardware; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Touchscreen gestures API + * + * A device may implement several touchscreen gestures for use while + * the display is turned off, such as drawing alphabets and shapes. + * These gestures can be interpreted by userspace to activate certain + * actions and launch certain apps, such as to skip music tracks, + * to turn on the flashlight, or to launch the camera app. + * + * This *should always* be supported by the hardware directly. + * A lot of recent touch controllers have a firmware option for this. + * + * This API provides support for enumerating the gestures + * supported by the touchscreen. + * + * A TouchscreenGesture is referenced by it's identifier and carries an + * associated name (up to the user to translate this value). + */ +public class TouchscreenGesture implements Parcelable { + + public final int id; + public final String name; + public final int keycode; + + public TouchscreenGesture(int id, String name, int keycode) { + this.id = id; + this.name = name; + this.keycode = keycode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(id); + parcel.writeString(name); + parcel.writeInt(keycode); + } + + /** @hide */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + public TouchscreenGesture createFromParcel(Parcel in) { + return new TouchscreenGesture(in.readInt(), in.readString(), in.readInt()); + } + + @Override + public TouchscreenGesture[] newArray(int size) { + return new TouchscreenGesture[size]; + } + }; +} diff --git a/core/java/com/android/internal/lineage/util/ColorUtils.java b/core/java/com/android/internal/lineage/util/ColorUtils.java new file mode 100644 index 0000000000000..4d3c9059c903e --- /dev/null +++ b/core/java/com/android/internal/lineage/util/ColorUtils.java @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2011-2016 The CyanogenMod 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.internal.lineage.util; + +import android.app.ActivityThread; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.util.MathUtils; + +import com.android.internal.lineage.util.palette.Palette; + +import java.util.Collections; +import java.util.Comparator; + +/** + * Helper class for colorspace conversions, and color-related + * algorithms which may be generally useful. + */ +public class ColorUtils { + + private static int[] SOLID_COLORS = new int[] { + Color.RED, 0xFFFFA500, Color.YELLOW, Color.GREEN, Color.CYAN, + Color.BLUE, Color.MAGENTA, Color.WHITE, Color.BLACK + }; + + private static float[] COEFFICIENTS = new float[9]; + + static { + String[] coefficients = ActivityThread.currentApplication().getApplicationContext() + .getResources().getStringArray( + com.android.internal.R.array.config_nightDisplayColorTemperatureCoefficients); + for (int i = 0; i < 9 && i < coefficients.length; i++) { + COEFFICIENTS[i] = Float.valueOf(coefficients[i]); + } + } + + /** + * Drop the alpha component from an RGBA packed int and return + * a non sign-extended RGB int. + * + * @param rgba + * @return rgb + */ + public static int dropAlpha(int rgba) { + return rgba & 0x00FFFFFF; + } + + /** + * Converts an RGB packed int into L*a*b space, which is well-suited for finding + * perceptual differences in color + * + * @param rgb A 32-bit value of packed RGB ints + * @return array of Lab values of size 3 + */ + public static float[] convertRGBtoLAB(int rgb) { + float[] lab = new float[3]; + float fx, fy, fz; + float eps = 216.f / 24389.f; + float k = 24389.f / 27.f; + + float Xr = 0.964221f; // reference white D50 + float Yr = 1.0f; + float Zr = 0.825211f; + + // RGB to XYZ + float r = Color.red(rgb) / 255.f; //R 0..1 + float g = Color.green(rgb) / 255.f; //G 0..1 + float b = Color.blue(rgb) / 255.f; //B 0..1 + + // assuming sRGB (D65) + if (r <= 0.04045) + r = r / 12; + else + r = (float) Math.pow((r + 0.055) / 1.055, 2.4); + + if (g <= 0.04045) + g = g / 12; + else + g = (float) Math.pow((g + 0.055) / 1.055, 2.4); + + if (b <= 0.04045) + b = b / 12; + else + b = (float) Math.pow((b + 0.055) / 1.055, 2.4); + + float X = 0.436052025f * r + 0.385081593f * g + 0.143087414f * b; + float Y = 0.222491598f * r + 0.71688606f * g + 0.060621486f * b; + float Z = 0.013929122f * r + 0.097097002f * g + 0.71418547f * b; + + // XYZ to Lab + float xr = X / Xr; + float yr = Y / Yr; + float zr = Z / Zr; + + if (xr > eps) + fx = (float) Math.pow(xr, 1 / 3.); + else + fx = (float) ((k * xr + 16.) / 116.); + + if (yr > eps) + fy = (float) Math.pow(yr, 1 / 3.); + else + fy = (float) ((k * yr + 16.) / 116.); + + if (zr > eps) + fz = (float) Math.pow(zr, 1 / 3.); + else + fz = (float) ((k * zr + 16.) / 116); + + float Ls = (116 * fy) - 16; + float as = 500 * (fx - fy); + float bs = 200 * (fy - fz); + + lab[0] = (2.55f * Ls + .5f); + lab[1] = (as + .5f); + lab[2] = (bs + .5f); + + return lab; + } + + /** + * Calculate the colour difference value between two colours in lab space. + * This code is from OpenIMAJ under BSD License + * + * @param L1 first colour's L component + * @param a1 first colour's a component + * @param b1 first colour's b component + * @param L2 second colour's L component + * @param a2 second colour's a component + * @param b2 second colour's b component + * @return the CIE 2000 colour difference + */ + public static double calculateDeltaE(double L1, double a1, double b1, + double L2, double a2, double b2) { + double Lmean = (L1 + L2) / 2.0; + double C1 = Math.sqrt(a1 * a1 + b1 * b1); + double C2 = Math.sqrt(a2 * a2 + b2 * b2); + double Cmean = (C1 + C2) / 2.0; + + double G = (1 - Math.sqrt(Math.pow(Cmean, 7) / (Math.pow(Cmean, 7) + Math.pow(25, 7)))) / 2; + double a1prime = a1 * (1 + G); + double a2prime = a2 * (1 + G); + + double C1prime = Math.sqrt(a1prime * a1prime + b1 * b1); + double C2prime = Math.sqrt(a2prime * a2prime + b2 * b2); + double Cmeanprime = (C1prime + C2prime) / 2; + + double h1prime = Math.atan2(b1, a1prime) + + 2 * Math.PI * (Math.atan2(b1, a1prime) < 0 ? 1 : 0); + double h2prime = Math.atan2(b2, a2prime) + + 2 * Math.PI * (Math.atan2(b2, a2prime) < 0 ? 1 : 0); + double Hmeanprime = ((Math.abs(h1prime - h2prime) > Math.PI) + ? (h1prime + h2prime + 2 * Math.PI) / 2 : (h1prime + h2prime) / 2); + + double T = 1.0 - 0.17 * Math.cos(Hmeanprime - Math.PI / 6.0) + + 0.24 * Math.cos(2 * Hmeanprime) + 0.32 * Math.cos(3 * Hmeanprime + Math.PI / 30) + - 0.2 * Math.cos(4 * Hmeanprime - 21 * Math.PI / 60); + + double deltahprime = ((Math.abs(h1prime - h2prime) <= Math.PI) ? h2prime - h1prime + : (h2prime <= h1prime) ? h2prime - h1prime + 2 * Math.PI + : h2prime - h1prime - 2 * Math.PI); + + double deltaLprime = L2 - L1; + double deltaCprime = C2prime - C1prime; + double deltaHprime = 2.0 * Math.sqrt(C1prime * C2prime) * Math.sin(deltahprime / 2.0); + double SL = 1.0 + ((0.015 * (Lmean - 50) * (Lmean - 50)) + / (Math.sqrt(20 + (Lmean - 50) * (Lmean - 50)))); + double SC = 1.0 + 0.045 * Cmeanprime; + double SH = 1.0 + 0.015 * Cmeanprime * T; + + double deltaTheta = (30 * Math.PI / 180) + * Math.exp(-((180 / Math.PI * Hmeanprime - 275) / 25) + * ((180 / Math.PI * Hmeanprime - 275) / 25)); + double RC = (2 + * Math.sqrt(Math.pow(Cmeanprime, 7) / (Math.pow(Cmeanprime, 7) + Math.pow(25, 7)))); + double RT = (-RC * Math.sin(2 * deltaTheta)); + + double KL = 1; + double KC = 1; + double KH = 1; + + double deltaE = Math.sqrt( + ((deltaLprime / (KL * SL)) * (deltaLprime / (KL * SL))) + + ((deltaCprime / (KC * SC)) * (deltaCprime / (KC * SC))) + + ((deltaHprime / (KH * SH)) * (deltaHprime / (KH * SH))) + + (RT * (deltaCprime / (KC * SC)) * (deltaHprime / (KH * SH)))); + + return deltaE; + } + + /** + * Finds the "perceptually nearest" color from a list of colors to + * the given RGB value. This is done by converting to + * L*a*b colorspace and using the CIE2000 deltaE algorithm. + * + * @param rgb The original color to start with + * @param colors An array of colors to test + * @return RGB packed int of nearest color in the list + */ + public static int findPerceptuallyNearestColor(int rgb, int[] colors) { + int nearestColor = 0; + double closest = Double.MAX_VALUE; + + float[] original = convertRGBtoLAB(rgb); + + for (int i = 0; i < colors.length; i++) { + float[] cl = convertRGBtoLAB(colors[i]); + double deltaE = calculateDeltaE(original[0], original[1], original[2], + cl[0], cl[1], cl[2]); + if (deltaE < closest) { + nearestColor = colors[i]; + closest = deltaE; + } + } + return nearestColor; + } + + /** + * Convenience method to find the nearest "solid" color (having RGB components + * of either 0 or 255) to the given color. This is useful for cases such as + * LED notification lights which may not be able to display the full range + * of colors due to hardware limitations. + * + * @param rgb + * @return the perceptually nearest color in RGB + */ + public static int findPerceptuallyNearestSolidColor(int rgb) { + return findPerceptuallyNearestColor(rgb, SOLID_COLORS); + } + + /** + * Given a Palette, pick out the dominant swatch based on population + * + * @param palette + * @return the dominant Swatch + */ + public static Palette.Swatch getDominantSwatch(Palette palette) { + if (palette == null || palette.getSwatches().size() == 0) { + return null; + } + // find most-represented swatch based on population + return Collections.max(palette.getSwatches(), new Comparator() { + @Override + public int compare(Palette.Swatch sw1, Palette.Swatch sw2) { + return Integer.compare(sw1.getPopulation(), sw2.getPopulation()); + } + }); + } + + /** + * Takes a drawable and uses Palette to generate a suitable "alert" + * color which can be used for an external notification mechanism + * such as an RGB LED. This will always pick a solid color having + * RGB components of 255 or 0. + * + * @param drawable The drawable to generate a color for + * @return a suitable solid color which corresponds to the image + */ + public static int generateAlertColorFromDrawable(Drawable drawable) { + int alertColor = Color.BLACK; + Bitmap bitmap = null; + + if (drawable == null) { + return alertColor; + } + + if (drawable instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) drawable).getBitmap(); + } else { + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + bitmap = Bitmap.createBitmap(Math.max(1, width), + Math.max(1, height), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + } + + if (bitmap != null) { + Palette p = Palette.from(bitmap).generate(); + if (p == null) { + return alertColor; + } + + // First try the dominant color + final Palette.Swatch dominantSwatch = getDominantSwatch(p); + int iconColor = alertColor; + if (dominantSwatch != null) { + iconColor = dominantSwatch.getRgb(); + alertColor = findPerceptuallyNearestSolidColor(iconColor); + } + + // Try the most saturated color if we got white or black (boring) + if (alertColor == Color.BLACK || alertColor == Color.WHITE) { + iconColor = p.getVibrantColor(Color.WHITE); + alertColor = findPerceptuallyNearestSolidColor(iconColor); + } + + if (!(drawable instanceof BitmapDrawable)) { + bitmap.recycle(); + } + } + + return alertColor; + } + + /** + * Convert a color temperature value (in Kelvin) to a RGB units as floats. + * This can be used in a transform matrix or hardware gamma control. + * + * @param degreesK + * @return array of floats representing rgb values 0->1 + */ + + public static float[] temperatureToRGB(int degreesK) { + float[] rgb = new float[3]; + + final float square = degreesK * degreesK; + for (int i = 0; i < rgb.length; i++) { + rgb[i] = square * COEFFICIENTS[i * 3] + + degreesK * COEFFICIENTS[i * 3 + 1] + COEFFICIENTS[i * 3 + 2]; + } + + return rgb; + } + + /** + * This table is a modified version of the original blackbody chart, found here: + * http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html + * + * Created by Ingo Thiel. + */ + private static final double[] sColorTable = new double[] { + 1.00000000, 0.18172716, 0.00000000, + 1.00000000, 0.25503671, 0.00000000, + 1.00000000, 0.30942099, 0.00000000, + 1.00000000, 0.35357379, 0.00000000, + 1.00000000, 0.39091524, 0.00000000, + 1.00000000, 0.42322816, 0.00000000, + 1.00000000, 0.45159884, 0.00000000, + 1.00000000, 0.47675916, 0.00000000, + 1.00000000, 0.49923747, 0.00000000, + 1.00000000, 0.51943421, 0.00000000, + 1.00000000, 0.54360078, 0.08679949, + 1.00000000, 0.56618736, 0.14065513, + 1.00000000, 0.58734976, 0.18362641, + 1.00000000, 0.60724493, 0.22137978, + 1.00000000, 0.62600248, 0.25591950, + 1.00000000, 0.64373109, 0.28819679, + 1.00000000, 0.66052319, 0.31873863, + 1.00000000, 0.67645822, 0.34786758, + 1.00000000, 0.69160518, 0.37579588, + 1.00000000, 0.70602449, 0.40267128, + 1.00000000, 0.71976951, 0.42860152, + 1.00000000, 0.73288760, 0.45366838, + 1.00000000, 0.74542112, 0.47793608, + 1.00000000, 0.75740814, 0.50145662, + 1.00000000, 0.76888303, 0.52427322, + 1.00000000, 0.77987699, 0.54642268, + 1.00000000, 0.79041843, 0.56793692, + 1.00000000, 0.80053332, 0.58884417, + 1.00000000, 0.81024551, 0.60916971, + 1.00000000, 0.81957693, 0.62893653, + 1.00000000, 0.82854786, 0.64816570, + 1.00000000, 0.83717703, 0.66687674, + 1.00000000, 0.84548188, 0.68508786, + 1.00000000, 0.85347859, 0.70281616, + 1.00000000, 0.86118227, 0.72007777, + 1.00000000, 0.86860704, 0.73688797, + 1.00000000, 0.87576611, 0.75326132, + 1.00000000, 0.88267187, 0.76921169, + 1.00000000, 0.88933596, 0.78475236, + 1.00000000, 0.89576933, 0.79989606, + 1.00000000, 0.90198230, 0.81465502, + 1.00000000, 0.90963069, 0.82838210, + 1.00000000, 0.91710889, 0.84190889, + 1.00000000, 0.92441842, 0.85523742, + 1.00000000, 0.93156127, 0.86836903, + 1.00000000, 0.93853986, 0.88130458, + 1.00000000, 0.94535695, 0.89404470, + 1.00000000, 0.95201559, 0.90658983, + 1.00000000, 0.95851906, 0.91894041, + 1.00000000, 0.96487079, 0.93109690, + 1.00000000, 0.97107439, 0.94305985, + 1.00000000, 0.97713351, 0.95482993, + 1.00000000, 0.98305189, 0.96640795, + 1.00000000, 0.98883326, 0.97779486, + 1.00000000, 0.99448139, 0.98899179, + 1.00000000, 1.00000000, 1.00000000, + 0.98947904, 0.99348723, 1.00000000, + 0.97940448, 0.98722715, 1.00000000, + 0.96975025, 0.98120637, 1.00000000, + 0.96049223, 0.97541240, 1.00000000, + 0.95160805, 0.96983355, 1.00000000, + 0.94303638, 0.96443333, 1.00000000, + 0.93480451, 0.95923080, 1.00000000, + 0.92689056, 0.95421394, 1.00000000, + 0.91927697, 0.94937330, 1.00000000, + 0.91194747, 0.94470005, 1.00000000, + 0.90488690, 0.94018594, 1.00000000, + 0.89808115, 0.93582323, 1.00000000, + 0.89151710, 0.93160469, 1.00000000, + 0.88518247, 0.92752354, 1.00000000, + 0.87906581, 0.92357340, 1.00000000, + 0.87315640, 0.91974827, 1.00000000, + 0.86744421, 0.91604254, 1.00000000, + 0.86191983, 0.91245088, 1.00000000, + 0.85657444, 0.90896831, 1.00000000, + 0.85139976, 0.90559011, 1.00000000, + 0.84638799, 0.90231183, 1.00000000, + 0.84153180, 0.89912926, 1.00000000, + 0.83682430, 0.89603843, 1.00000000, + 0.83225897, 0.89303558, 1.00000000, + 0.82782969, 0.89011714, 1.00000000, + 0.82353066, 0.88727974, 1.00000000, + 0.81935641, 0.88452017, 1.00000000, + 0.81530175, 0.88183541, 1.00000000, + 0.81136180, 0.87922257, 1.00000000, + 0.80753191, 0.87667891, 1.00000000, + 0.80380769, 0.87420182, 1.00000000, + 0.80018497, 0.87178882, 1.00000000, + 0.79665980, 0.86943756, 1.00000000, + 0.79322843, 0.86714579, 1.00000000, + 0.78988728, 0.86491137, 1.00000000, + 0.78663296, 0.86273225, 1.00000000, + 0.78346225, 0.86060650, 1.00000000, + 0.78037207, 0.85853224, 1.00000000, + 0.77735950, 0.85650771, 1.00000000, + 0.77442176, 0.85453121, 1.00000000, + 0.77155617, 0.85260112, 1.00000000, + 0.76876022, 0.85071588, 1.00000000, + 0.76603147, 0.84887402, 1.00000000, + 0.76336762, 0.84707411, 1.00000000, + 0.76076645, 0.84531479, 1.00000000, + 0.75822586, 0.84359476, 1.00000000, + 0.75574383, 0.84191277, 1.00000000, + 0.75331843, 0.84026762, 1.00000000, + 0.75094780, 0.83865816, 1.00000000, + 0.74863017, 0.83708329, 1.00000000, + 0.74636386, 0.83554194, 1.00000000, + 0.74414722, 0.83403311, 1.00000000, + 0.74197871, 0.83255582, 1.00000000, + 0.73985682, 0.83110912, 1.00000000, + 0.73778012, 0.82969211, 1.00000000, + 0.73574723, 0.82830393, 1.00000000, + 0.73375683, 0.82694373, 1.00000000, + 0.73180765, 0.82561071, 1.00000000, + 0.72989845, 0.82430410, 1.00000000, + 0.72802807, 0.82302316, 1.00000000, + 0.72619537, 0.82176715, 1.00000000, + 0.72439927, 0.82053539, 1.00000000, + 0.72263872, 0.81932722, 1.00000000, + 0.72091270, 0.81814197, 1.00000000, + 0.71922025, 0.81697905, 1.00000000, + 0.71756043, 0.81583783, 1.00000000, + 0.71593234, 0.81471775, 1.00000000, + 0.71433510, 0.81361825, 1.00000000, + 0.71276788, 0.81253878, 1.00000000, + 0.71122987, 0.81147883, 1.00000000, + 0.70972029, 0.81043789, 1.00000000, + 0.70823838, 0.80941546, 1.00000000, + 0.70678342, 0.80841109, 1.00000000, + 0.70535469, 0.80742432, 1.00000000, + 0.70395153, 0.80645469, 1.00000000, + 0.70257327, 0.80550180, 1.00000000, + 0.70121928, 0.80456522, 1.00000000, + 0.69988894, 0.80364455, 1.00000000, + 0.69858167, 0.80273941, 1.00000000, + 0.69729688, 0.80184943, 1.00000000, + 0.69603402, 0.80097423, 1.00000000, + 0.69479255, 0.80011347, 1.00000000, + 0.69357196, 0.79926681, 1.00000000, + 0.69237173, 0.79843391, 1.00000000, + 0.69119138, 0.79761446, 1.00000000, + 0.69003044, 0.79680814, 1.00000000, + 0.68888844, 0.79601466, 1.00000000, + 0.68776494, 0.79523371, 1.00000000, + 0.68665951, 0.79446502, 1.00000000, + 0.68557173, 0.79370830, 1.00000000, + 0.68450119, 0.79296330, 1.00000000, + 0.68344751, 0.79222975, 1.00000000, + 0.68241029, 0.79150740, 1.00000000, + 0.68138918, 0.79079600, 1.00000000, + 0.68038380, 0.79009531, 1.00000000, + 0.67939381, 0.78940511, 1.00000000, + 0.67841888, 0.78872517, 1.00000000, + 0.67745866, 0.78805526, 1.00000000, + 0.67651284, 0.78739518, 1.00000000, + 0.67558112, 0.78674472, 1.00000000, + 0.67466317, 0.78610368, 1.00000000, + 0.67375872, 0.78547186, 1.00000000, + 0.67286748, 0.78484907, 1.00000000, + 0.67198916, 0.78423512, 1.00000000, + 0.67112350, 0.78362984, 1.00000000, + 0.67027024, 0.78303305, 1.00000000, + 0.66942911, 0.78244457, 1.00000000, + 0.66859988, 0.78186425, 1.00000000, + 0.66778228, 0.78129191, 1.00000000, + 0.66697610, 0.78072740, 1.00000000, + 0.66618110, 0.78017057, 1.00000000, + 0.66539706, 0.77962127, 1.00000000, + 0.66462376, 0.77907934, 1.00000000, + 0.66386098, 0.77854465, 1.00000000, + 0.66310852, 0.77801705, 1.00000000, + 0.66236618, 0.77749642, 1.00000000, + 0.66163375, 0.77698261, 1.00000000, + 0.66091106, 0.77647551, 1.00000000, + 0.66019791, 0.77597498, 1.00000000, + 0.65949412, 0.77548090, 1.00000000, + 0.65879952, 0.77499315, 1.00000000, + 0.65811392, 0.77451161, 1.00000000, + 0.65743716, 0.77403618, 1.00000000, + 0.65676908, 0.77356673, 1.00000000, + 0.65610952, 0.77310316, 1.00000000, + 0.65545831, 0.77264537, 1.00000000, + 0.65481530, 0.77219324, 1.00000000, + 0.65418036, 0.77174669, 1.00000000, + 0.65355332, 0.77130560, 1.00000000, + 0.65293404, 0.77086988, 1.00000000, + 0.65232240, 0.77043944, 1.00000000, + 0.65171824, 0.77001419, 1.00000000, + 0.65112144, 0.76959404, 1.00000000, + 0.65053187, 0.76917889, 1.00000000, + 0.64994941, 0.76876866, 1.00000000, + 0.64937392, 0.76836326, 1.00000000 + }; + +} diff --git a/core/java/com/android/internal/lineage/util/Concierge.java b/core/java/com/android/internal/lineage/util/Concierge.java new file mode 100644 index 0000000000000..4fffe7d5107ae --- /dev/null +++ b/core/java/com/android/internal/lineage/util/Concierge.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.lineage.util; + +import android.os.Parcel; + +/** + * Simply, Concierge handles your parcels and makes sure they get marshalled and unmarshalled + * correctly when cross IPC boundaries even when there is a version mismatch between the client + * sdk level and the framework implementation. + * + *

On incoming parcel (to be unmarshalled): + * + *

+ *     ParcelInfo incomingParcelInfo = Concierge.receiveParcel(incomingParcel);
+ *     int parcelableVersion = incomingParcelInfo.getParcelVersion();
+ *
+ *     // Do unmarshalling steps here iterating over every plausible version
+ *
+ *     // Complete the process
+ *     incomingParcelInfo.complete();
+ * 
+ * + *

On outgoing parcel (to be marshalled): + * + *

+ *     ParcelInfo outgoingParcelInfo = Concierge.prepareParcel(incomingParcel);
+ *
+ *     // Do marshalling steps here iterating over every plausible version
+ *
+ *     // Complete the process
+ *     outgoingParcelInfo.complete();
+ * 
+ */ +public final class Concierge { + + /** Not instantiable */ + private Concierge() { + // Don't instantiate + } + + /** + * Since there might be a case where new versions of the lineage framework use applications running + * old versions of the protocol (and thus old versions of this class), we need a versioning + * system for the parcels sent between the core framework and its sdk users. + * + * This parcelable version should be the latest version API version listed in + * {@link LINEAGE_VERSION_CODES} + * @hide + */ + public static final int PARCELABLE_VERSION = 9; + + /** + * Tell the concierge to receive our parcel, so we can get information from it. + * + * MUST CALL {@link ParcelInfo#complete()} AFTER UNMARSHALLING. + * + * @param parcel Incoming parcel to be unmarshalled + * @return {@link ParcelInfo} containing parcel information, specifically the version. + */ + public static ParcelInfo receiveParcel(Parcel parcel) { + return new ParcelInfo(parcel); + } + + /** + * Prepare a parcel for the Concierge. + * + * MUST CALL {@link ParcelInfo#complete()} AFTER MARSHALLING. + * + * @param parcel Outgoing parcel to be marshalled + * @return {@link ParcelInfo} containing parcel information, specifically the version. + */ + public static ParcelInfo prepareParcel(Parcel parcel) { + return new ParcelInfo(parcel, PARCELABLE_VERSION); + } + + /** + * Parcel header info specific to the Parcel object that is passed in via + * {@link #prepareParcel(Parcel)} or {@link #receiveParcel(Parcel)}. The exposed method + * of {@link #getParcelVersion()} gets the api level of the parcel object. + */ + public final static class ParcelInfo { + private Parcel mParcel; + private int mParcelableVersion; + private int mParcelableSize; + private int mStartPosition; + private int mSizePosition; + private boolean mCreation = false; + + ParcelInfo(Parcel parcel) { + mCreation = false; + mParcel = parcel; + mParcelableVersion = parcel.readInt(); + mParcelableSize = parcel.readInt(); + mStartPosition = parcel.dataPosition(); + } + + ParcelInfo(Parcel parcel, int parcelableVersion) { + mCreation = true; + mParcel = parcel; + mParcelableVersion = parcelableVersion; + + // Write parcelable version, make sure to define explicit changes + // within {@link #PARCELABLE_VERSION); + mParcel.writeInt(mParcelableVersion); + + // Inject a placeholder that will store the parcel size from this point on + // (not including the size itself). + mSizePosition = parcel.dataPosition(); + mParcel.writeInt(0); + mStartPosition = parcel.dataPosition(); + } + + /** + * Get the parcel version from the {@link Parcel} received by the Concierge. + * @return {@link #PARCELABLE_VERSION} of the {@link Parcel} + */ + public int getParcelVersion() { + return mParcelableVersion; + } + + /** + * Complete the {@link ParcelInfo} for the Concierge. + */ + public void complete() { + if (mCreation) { + // Go back and write size + mParcelableSize = mParcel.dataPosition() - mStartPosition; + mParcel.setDataPosition(mSizePosition); + mParcel.writeInt(mParcelableSize); + mParcel.setDataPosition(mStartPosition + mParcelableSize); + } else { + mParcel.setDataPosition(mStartPosition + mParcelableSize); + } + } + } +} diff --git a/core/java/com/android/internal/lineage/util/MathUtils.java b/core/java/com/android/internal/lineage/util/MathUtils.java new file mode 100644 index 0000000000000..6cab1063efd69 --- /dev/null +++ b/core/java/com/android/internal/lineage/util/MathUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.lineage.util; + +public final class MathUtils { + + /** + * Given a range of values which change continuously in a non-linear way, + * we can map back and forth to a linear scale using some quadratic equations. + * + * The linear scale ranges from 0 -> 1. This method will calculate the + * coefficients needed to solve the conversion functions in the next two methods. + * + * lower = actual value when linear value = 0 + * mid = actual value when linear value = .5 + * upper actual value when linear value = 1 + * + * @param lower + * @param mid + * @param upper + * @return array of coefficients + */ + public static double[] powerCurve(double lower, double mid, double upper) { + final double[] curve = new double[3]; + curve[0] = ((lower * upper) - (mid * mid)) / (lower - (2 * mid) + upper); + curve[1] = Math.pow((mid - lower), 2) / (lower - (2 * mid) + upper); + curve[2] = 2 * Math.log((upper - mid) / (mid - lower)); + return curve; + } + + /** + * Map a value on a power curve to a linear value + * + * @param curve obtained from powerCurve() + * @param value to convert to linear scale + * @return linear value from 0 -> 1 + */ + public static double powerCurveToLinear(final double[] curve, double value) { + return Math.log((value - curve[0]) / curve[1]) / curve[2]; + } + + /** + * Map a value on a linear scale to a value on a power curve + * + * @param curve obtained from powerCurve() + * @param value from 0 -> 1 to map onto power curve + * @return actual value on the given curve + */ + public static double linearToPowerCurve(final double[] curve, double value) { + return curve[0] + curve[1] * Math.exp(curve[2] * value); + } + + +} diff --git a/core/java/com/android/internal/lineage/util/palette/ColorCutQuantizer.java b/core/java/com/android/internal/lineage/util/palette/ColorCutQuantizer.java new file mode 100644 index 0000000000000..308d455b49984 --- /dev/null +++ b/core/java/com/android/internal/lineage/util/palette/ColorCutQuantizer.java @@ -0,0 +1,517 @@ +/* + * Copyright 2014 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.internal.lineage.util.palette; + +import android.graphics.Color; +import android.util.TimingLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +import com.android.internal.lineage.util.palette.Palette.Swatch; + +/** + * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct + * colors rather than representation colors. + * + * The color space is represented as a 3-dimensional cube with each dimension being an RGB + * component. The cube is then repeatedly divided until we have reduced the color space to the + * requested number of colors. An average color is then generated from each cube. + * + * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes + * have roughly the same population, where this quantizer divides boxes based on their color volume. + * This means that the color space is divided into distinct colors, rather than representative + * colors. + * + * @hide + */ +final class ColorCutQuantizer { + + private static final String LOG_TAG = "ColorCutQuantizer"; + private static final boolean LOG_TIMINGS = false; + + private static final int COMPONENT_RED = -3; + private static final int COMPONENT_GREEN = -2; + private static final int COMPONENT_BLUE = -1; + + private static final int QUANTIZE_WORD_WIDTH = 5; + private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1; + + final int[] mColors; + final int[] mHistogram; + final List mQuantizedColors; + final TimingLogger mTimingLogger; + final Palette.Filter[] mFilters; + + private final float[] mTempHsl = new float[3]; + + /** + * Constructor. + * + * @param pixels histogram representing an image's pixel data + * @param maxColors The maximum number of colors that should be in the result palette. + * @param filters Set of filters to use in the quantization stage + */ + ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) { + mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; + mFilters = filters; + + final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; + for (int i = 0; i < pixels.length; i++) { + final int quantizedColor = quantizeFromRgb888(pixels[i]); + // Now update the pixel value to the quantized value + pixels[i] = quantizedColor; + // And update the histogram + hist[quantizedColor]++; + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Histogram created"); + } + + // Now let's count the number of distinct colors + int distinctColorCount = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0 && shouldIgnoreColor(color)) { + // If we should ignore the color, set the population to 0 + hist[color] = 0; + } + if (hist[color] > 0) { + // If the color has population, increase the distinct color count + distinctColorCount++; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Filtered colors and distinct colors counted"); + } + + // Now lets go through create an array consisting of only distinct colors + final int[] colors = mColors = new int[distinctColorCount]; + int distinctColorIndex = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0) { + colors[distinctColorIndex++] = color; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Distinct colors copied into array"); + } + + if (distinctColorCount <= maxColors) { + // The image has fewer colors than the maximum requested, so just return the colors + mQuantizedColors = new ArrayList<>(); + for (int color : colors) { + mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color])); + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Too few colors present. Copied to Swatches"); + mTimingLogger.dumpToLog(); + } + } else { + // We need use quantization to reduce the number of colors + mQuantizedColors = quantizePixels(maxColors); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Quantized colors computed"); + mTimingLogger.dumpToLog(); + } + } + } + + /** + * @return the list of quantized colors + */ + List getQuantizedColors() { + return mQuantizedColors; + } + + private List quantizePixels(int maxColors) { + // Create the priority queue which is sorted by volume descending. This means we always + // split the largest box in the queue + final PriorityQueue pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME); + + // To start, offer a box which contains all of the colors + pq.offer(new Vbox(0, mColors.length - 1)); + + // Now go through the boxes, splitting them until we have reached maxColors or there are no + // more boxes to split + splitBoxes(pq, maxColors); + + // Finally, return the average colors of the color boxes + return generateAverageColors(pq); + } + + /** + * Iterate through the {@link java.util.Queue}, popping + * {@link ColorCutQuantizer.Vbox} objects from the queue + * and splitting them. Once split, the new box and the remaining box are offered back to the + * queue. + * + * @param queue {@link java.util.PriorityQueue} to poll for boxes + * @param maxSize Maximum amount of boxes to split + */ + private void splitBoxes(final PriorityQueue queue, final int maxSize) { + while (queue.size() < maxSize) { + final Vbox vbox = queue.poll(); + + if (vbox != null && vbox.canSplit()) { + // First split the box, and offer the result + queue.offer(vbox.splitBox()); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Box split"); + } + // Then offer the box back + queue.offer(vbox); + } else { + if (LOG_TIMINGS) { + mTimingLogger.addSplit("All boxes split"); + } + // If we get here then there are no more boxes to split, so return + return; + } + } + } + + private List generateAverageColors(Collection vboxes) { + ArrayList colors = new ArrayList<>(vboxes.size()); + for (Vbox vbox : vboxes) { + Swatch swatch = vbox.getAverageColor(); + if (!shouldIgnoreColor(swatch)) { + // As we're averaging a color box, we can still get colors which we do not want, so + // we check again here + colors.add(swatch); + } + } + return colors; + } + + /** + * Represents a tightly fitting box around a color space. + */ + private class Vbox { + // lower and upper index are inclusive + private int mLowerIndex; + private int mUpperIndex; + // Population of colors within this box + private int mPopulation; + + private int mMinRed, mMaxRed; + private int mMinGreen, mMaxGreen; + private int mMinBlue, mMaxBlue; + + Vbox(int lowerIndex, int upperIndex) { + mLowerIndex = lowerIndex; + mUpperIndex = upperIndex; + fitBox(); + } + + final int getVolume() { + return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) * + (mMaxBlue - mMinBlue + 1); + } + + final boolean canSplit() { + return getColorCount() > 1; + } + + final int getColorCount() { + return 1 + mUpperIndex - mLowerIndex; + } + + /** + * Recomputes the boundaries of this box to tightly fit the colors within the box. + */ + final void fitBox() { + final int[] colors = mColors; + final int[] hist = mHistogram; + + // Reset the min and max to opposite values + int minRed, minGreen, minBlue; + minRed = minGreen = minBlue = Integer.MAX_VALUE; + int maxRed, maxGreen, maxBlue; + maxRed = maxGreen = maxBlue = Integer.MIN_VALUE; + int count = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + count += hist[color]; + + final int r = quantizedRed(color); + final int g = quantizedGreen(color); + final int b = quantizedBlue(color); + if (r > maxRed) { + maxRed = r; + } + if (r < minRed) { + minRed = r; + } + if (g > maxGreen) { + maxGreen = g; + } + if (g < minGreen) { + minGreen = g; + } + if (b > maxBlue) { + maxBlue = b; + } + if (b < minBlue) { + minBlue = b; + } + } + + mMinRed = minRed; + mMaxRed = maxRed; + mMinGreen = minGreen; + mMaxGreen = maxGreen; + mMinBlue = minBlue; + mMaxBlue = maxBlue; + mPopulation = count; + } + + /** + * Split this color box at the mid-point along it's longest dimension + * + * @return the new ColorBox + */ + final Vbox splitBox() { + if (!canSplit()) { + throw new IllegalStateException("Can not split a box with only 1 color"); + } + + // find median along the longest dimension + final int splitPoint = findSplitPoint(); + + Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex); + + // Now change this box's upperIndex and recompute the color boundaries + mUpperIndex = splitPoint; + fitBox(); + + return newBox; + } + + /** + * @return the dimension which this box is largest in + */ + final int getLongestColorDimension() { + final int redLength = mMaxRed - mMinRed; + final int greenLength = mMaxGreen - mMinGreen; + final int blueLength = mMaxBlue - mMinBlue; + + if (redLength >= greenLength && redLength >= blueLength) { + return COMPONENT_RED; + } else if (greenLength >= redLength && greenLength >= blueLength) { + return COMPONENT_GREEN; + } else { + return COMPONENT_BLUE; + } + } + + /** + * Finds the point within this box's lowerIndex and upperIndex index of where to split. + * + * This is calculated by finding the longest color dimension, and then sorting the + * sub-array based on that dimension value in each color. The colors are then iterated over + * until a color is found with at least the midpoint of the whole box's dimension midpoint. + * + * @return the index of the colors array to split from + */ + final int findSplitPoint() { + final int longestDimension = getLongestColorDimension(); + final int[] colors = mColors; + final int[] hist = mHistogram; + + // We need to sort the colors in this box based on the longest color dimension. + // As we can't use a Comparator to define the sort logic, we modify each color so that + // it's most significant is the desired dimension + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1 + Arrays.sort(colors, mLowerIndex, mUpperIndex + 1); + + // Now revert all of the colors so that they are packed as RGB again + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + final int midPoint = mPopulation / 2; + for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { + count += hist[colors[i]]; + if (count >= midPoint) { + return i; + } + } + + return mLowerIndex; + } + + /** + * @return the average color of this box. + */ + final Swatch getAverageColor() { + final int[] colors = mColors; + final int[] hist = mHistogram; + int redSum = 0; + int greenSum = 0; + int blueSum = 0; + int totalPopulation = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + final int colorPopulation = hist[color]; + + totalPopulation += colorPopulation; + redSum += colorPopulation * quantizedRed(color); + greenSum += colorPopulation * quantizedGreen(color); + blueSum += colorPopulation * quantizedBlue(color); + } + + final int redMean = Math.round(redSum / (float) totalPopulation); + final int greenMean = Math.round(greenSum / (float) totalPopulation); + final int blueMean = Math.round(blueSum / (float) totalPopulation); + + return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation); + } + } + + /** + * Modify the significant octet in a packed color int. Allows sorting based on the value of a + * single color component. This relies on all components being the same word size. + * + * @see Vbox#findSplitPoint() + */ + private static void modifySignificantOctet(final int[] a, final int dimension, + final int lower, final int upper) { + switch (dimension) { + case COMPONENT_RED: + // Already in RGB, no need to do anything + break; + case COMPONENT_GREEN: + // We need to do a RGB to GRB swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedRed(color) << QUANTIZE_WORD_WIDTH + | quantizedBlue(color); + } + break; + case COMPONENT_BLUE: + // We need to do a RGB to BGR swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedGreen(color) << QUANTIZE_WORD_WIDTH + | quantizedRed(color); + } + break; + } + } + + private boolean shouldIgnoreColor(int color565) { + final int rgb = approximateToRgb888(color565); + ColorUtils.colorToHSL(rgb, mTempHsl); + return shouldIgnoreColor(rgb, mTempHsl); + } + + private boolean shouldIgnoreColor(Swatch color) { + return shouldIgnoreColor(color.getRgb(), color.getHsl()); + } + + private boolean shouldIgnoreColor(int rgb, float[] hsl) { + if (mFilters != null && mFilters.length > 0) { + for (int i = 0, count = mFilters.length; i < count; i++) { + if (!mFilters[i].isAllowed(rgb, hsl)) { + return true; + } + } + } + return false; + } + + /** + * Comparator which sorts {@link Vbox} instances based on their volume, in descending order + */ + private static final Comparator VBOX_COMPARATOR_VOLUME = new Comparator() { + @Override + public int compare(Vbox lhs, Vbox rhs) { + return rhs.getVolume() - lhs.getVolume(); + } + }; + + /** + * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + private static int quantizeFromRgb888(int color) { + int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH); + int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH); + int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH); + return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b; + } + + /** + * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + private static int approximateToRgb888(int r, int g, int b) { + return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8)); + } + + private static int approximateToRgb888(int color) { + return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); + } + + /** + * @return red component of the quantized color + */ + private static int quantizedRed(int color) { + return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK; + } + + /** + * @return green component of a quantized color + */ + private static int quantizedGreen(int color) { + return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK; + } + + /** + * @return blue component of a quantized color + */ + private static int quantizedBlue(int color) { + return color & QUANTIZE_WORD_MASK; + } + + private static int modifyWordWidth(int value, int currentWidth, int targetWidth) { + final int newValue; + if (targetWidth > currentWidth) { + // If we're approximating up in word width, we'll shift up + newValue = value << (targetWidth - currentWidth); + } else { + // Else, we will just shift and keep the MSB + newValue = value >> (currentWidth - targetWidth); + } + return newValue & ((1 << targetWidth) - 1); + } + +} diff --git a/core/java/com/android/internal/lineage/util/palette/ColorUtils.java b/core/java/com/android/internal/lineage/util/palette/ColorUtils.java new file mode 100644 index 0000000000000..0b79e44c4d918 --- /dev/null +++ b/core/java/com/android/internal/lineage/util/palette/ColorUtils.java @@ -0,0 +1,299 @@ +/* + * Copyright 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.internal.lineage.util.palette; + +import android.graphics.Color; + +/** + * A set of color-related utility methods, building upon those available in {@code Color}. + * + * @hide + */ +public class ColorUtils { + + private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10; + private static final int MIN_ALPHA_SEARCH_PRECISION = 10; + + private ColorUtils() {} + + /** + * Composite two potentially translucent colors over each other and returns the result. + */ + public static int compositeColors(int foreground, int background) { + int bgAlpha = Color.alpha(background); + int fgAlpha = Color.alpha(foreground); + int a = compositeAlpha(fgAlpha, bgAlpha); + + int r = compositeComponent(Color.red(foreground), fgAlpha, + Color.red(background), bgAlpha, a); + int g = compositeComponent(Color.green(foreground), fgAlpha, + Color.green(background), bgAlpha, a); + int b = compositeComponent(Color.blue(foreground), fgAlpha, + Color.blue(background), bgAlpha, a); + + return Color.argb(a, r, g, b); + } + + private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) { + return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF); + } + + private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) { + if (a == 0) return 0; + return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF); + } + + /** + * Returns the luminance of a color. + * + * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + */ + public static double calculateLuminance(int color) { + double red = Color.red(color) / 255d; + red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4); + + double green = Color.green(color) / 255d; + green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4); + + double blue = Color.blue(color) / 255d; + blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4); + + return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue); + } + + /** + * Returns the contrast ratio between {@code foreground} and {@code background}. + * {@code background} must be opaque. + *

+ * Formula defined + * here. + */ + public static double calculateContrast(int foreground, int background) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent"); + } + if (Color.alpha(foreground) < 255) { + // If the foreground is translucent, composite the foreground over the background + foreground = compositeColors(foreground, background); + } + + final double luminance1 = calculateLuminance(foreground) + 0.05; + final double luminance2 = calculateLuminance(background) + 0.05; + + // Now return the lighter luminance divided by the darker luminance + return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2); + } + + /** + * Calculates the minimum alpha value which can be applied to {@code foreground} so that would + * have a contrast value of at least {@code minContrastRatio} when compared to + * {@code background}. + * + * @param foreground the foreground color. + * @param background the background color. Should be opaque. + * @param minContrastRatio the minimum contrast ratio. + * @return the alpha value in the range 0-255, or -1 if no value could be calculated. + */ + public static int calculateMinimumAlpha(int foreground, int background, + float minContrastRatio) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent"); + } + + // First lets check that a fully opaque foreground has sufficient contrast + int testForeground = setAlphaComponent(foreground, 255); + double testRatio = calculateContrast(testForeground, background); + if (testRatio < minContrastRatio) { + // Fully opaque foreground does not have sufficient contrast, return error + return -1; + } + + // Binary search to find a value with the minimum value which provides sufficient contrast + int numIterations = 0; + int minAlpha = 0; + int maxAlpha = 255; + + while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS && + (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) { + final int testAlpha = (minAlpha + maxAlpha) / 2; + + testForeground = setAlphaComponent(foreground, testAlpha); + testRatio = calculateContrast(testForeground, background); + + if (testRatio < minContrastRatio) { + minAlpha = testAlpha; + } else { + maxAlpha = testAlpha; + } + + numIterations++; + } + + // Conservatively return the max of the range of possible alphas, which is known to pass. + return maxAlpha; + } + + /** + * Convert RGB components to HSL (hue-saturation-lightness). + *

    + *
  • hsl[0] is Hue [0 .. 360)
  • + *
  • hsl[1] is Saturation [0...1]
  • + *
  • hsl[2] is Lightness [0...1]
  • + *
+ * + * @param r red component value [0..255] + * @param g green component value [0..255] + * @param b blue component value [0..255] + * @param hsl 3 element array which holds the resulting HSL components. + */ + public static void RGBToHSL(int r, int g, int b, float[] hsl) { + final float rf = r / 255f; + final float gf = g / 255f; + final float bf = b / 255f; + + final float max = Math.max(rf, Math.max(gf, bf)); + final float min = Math.min(rf, Math.min(gf, bf)); + final float deltaMaxMin = max - min; + + float h, s; + float l = (max + min) / 2f; + + if (max == min) { + // Monochromatic + h = s = 0f; + } else { + if (max == rf) { + h = ((gf - bf) / deltaMaxMin) % 6f; + } else if (max == gf) { + h = ((bf - rf) / deltaMaxMin) + 2f; + } else { + h = ((rf - gf) / deltaMaxMin) + 4f; + } + + s = deltaMaxMin / (1f - Math.abs(2f * l - 1f)); + } + + h = (h * 60f) % 360f; + if (h < 0) { + h += 360f; + } + + hsl[0] = constrain(h, 0f, 360f); + hsl[1] = constrain(s, 0f, 1f); + hsl[2] = constrain(l, 0f, 1f); + } + + /** + * Convert the ARGB color to its HSL (hue-saturation-lightness) components. + *
    + *
  • hsl[0] is Hue [0 .. 360)
  • + *
  • hsl[1] is Saturation [0...1]
  • + *
  • hsl[2] is Lightness [0...1]
  • + *
+ * + * @param color the ARGB color to convert. The alpha component is ignored. + * @param hsl 3 element array which holds the resulting HSL components. + */ + public static void colorToHSL(int color, float[] hsl) { + RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl); + } + + /** + * Convert HSL (hue-saturation-lightness) components to a RGB color. + *
    + *
  • hsl[0] is Hue [0 .. 360)
  • + *
  • hsl[1] is Saturation [0...1]
  • + *
  • hsl[2] is Lightness [0...1]
  • + *
+ * If hsv values are out of range, they are pinned. + * + * @param hsl 3 element array which holds the input HSL components. + * @return the resulting RGB color + */ + public static int HSLToColor(float[] hsl) { + final float h = hsl[0]; + final float s = hsl[1]; + final float l = hsl[2]; + + final float c = (1f - Math.abs(2 * l - 1f)) * s; + final float m = l - 0.5f * c; + final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f)); + + final int hueSegment = (int) h / 60; + + int r = 0, g = 0, b = 0; + + switch (hueSegment) { + case 0: + r = Math.round(255 * (c + m)); + g = Math.round(255 * (x + m)); + b = Math.round(255 * m); + break; + case 1: + r = Math.round(255 * (x + m)); + g = Math.round(255 * (c + m)); + b = Math.round(255 * m); + break; + case 2: + r = Math.round(255 * m); + g = Math.round(255 * (c + m)); + b = Math.round(255 * (x + m)); + break; + case 3: + r = Math.round(255 * m); + g = Math.round(255 * (x + m)); + b = Math.round(255 * (c + m)); + break; + case 4: + r = Math.round(255 * (x + m)); + g = Math.round(255 * m); + b = Math.round(255 * (c + m)); + break; + case 5: + case 6: + r = Math.round(255 * (c + m)); + g = Math.round(255 * m); + b = Math.round(255 * (x + m)); + break; + } + + r = constrain(r, 0, 255); + g = constrain(g, 0, 255); + b = constrain(b, 0, 255); + + return Color.rgb(r, g, b); + } + + /** + * Set the alpha component of {@code color} to be {@code alpha}. + */ + public static int setAlphaComponent(int color, int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("alpha must be between 0 and 255."); + } + return (color & 0x00ffffff) | (alpha << 24); + } + + private static float constrain(float amount, float low, float high) { + return amount < low ? low : (amount > high ? high : amount); + } + + private static int constrain(int amount, int low, int high) { + return amount < low ? low : (amount > high ? high : amount); + } + +} diff --git a/core/java/com/android/internal/lineage/util/palette/DefaultGenerator.java b/core/java/com/android/internal/lineage/util/palette/DefaultGenerator.java new file mode 100644 index 0000000000000..a3b663a573cc3 --- /dev/null +++ b/core/java/com/android/internal/lineage/util/palette/DefaultGenerator.java @@ -0,0 +1,244 @@ +/* + * Copyright 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.internal.lineage.util.palette; + +import java.util.List; + +import com.android.internal.lineage.util.palette.Palette.Swatch; + +/** + * @hide + */ +class DefaultGenerator extends Palette.Generator { + + private static final float TARGET_DARK_LUMA = 0.26f; + private static final float MAX_DARK_LUMA = 0.45f; + + private static final float MIN_LIGHT_LUMA = 0.55f; + private static final float TARGET_LIGHT_LUMA = 0.74f; + + private static final float MIN_NORMAL_LUMA = 0.3f; + private static final float TARGET_NORMAL_LUMA = 0.5f; + private static final float MAX_NORMAL_LUMA = 0.7f; + + private static final float TARGET_MUTED_SATURATION = 0.3f; + private static final float MAX_MUTED_SATURATION = 0.4f; + + private static final float TARGET_VIBRANT_SATURATION = 1f; + private static final float MIN_VIBRANT_SATURATION = 0.35f; + + private static final float WEIGHT_SATURATION = 3f; + private static final float WEIGHT_LUMA = 6f; + private static final float WEIGHT_POPULATION = 1f; + + private List mSwatches; + + private int mHighestPopulation; + + private Swatch mVibrantSwatch; + private Swatch mMutedSwatch; + private Swatch mDarkVibrantSwatch; + private Swatch mDarkMutedSwatch; + private Swatch mLightVibrantSwatch; + private Swatch mLightMutedSwatch; + + @Override + public void generate(final List swatches) { + mSwatches = swatches; + + mHighestPopulation = findMaxPopulation(); + + generateVariationColors(); + + // Now try and generate any missing colors + generateEmptySwatches(); + } + + @Override + public Swatch getVibrantSwatch() { + return mVibrantSwatch; + } + + @Override + public Swatch getLightVibrantSwatch() { + return mLightVibrantSwatch; + } + + @Override + public Swatch getDarkVibrantSwatch() { + return mDarkVibrantSwatch; + } + + @Override + public Swatch getMutedSwatch() { + return mMutedSwatch; + } + + @Override + public Swatch getLightMutedSwatch() { + return mLightMutedSwatch; + } + + @Override + public Swatch getDarkMutedSwatch() { + return mDarkMutedSwatch; + } + + private void generateVariationColors() { + mVibrantSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mLightVibrantSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mDarkVibrantSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mMutedSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + + mLightMutedSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + + mDarkMutedSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + } + + /** + * Try and generate any missing swatches from the swatches we did find. + */ + private void generateEmptySwatches() { + if (mVibrantSwatch == null) { + // If we do not have a vibrant color... + if (mDarkVibrantSwatch != null) { + // ...but we do have a dark vibrant, generate the value by modifying the luma + final float[] newHsl = copyHslValues(mDarkVibrantSwatch); + newHsl[2] = TARGET_NORMAL_LUMA; + mVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0); + } + } + + if (mDarkVibrantSwatch == null) { + // If we do not have a dark vibrant color... + if (mVibrantSwatch != null) { + // ...but we do have a vibrant, generate the value by modifying the luma + final float[] newHsl = copyHslValues(mVibrantSwatch); + newHsl[2] = TARGET_DARK_LUMA; + mDarkVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0); + } + } + } + + /** + * Find the {@link Palette.Swatch} with the highest population value and return the population. + */ + private int findMaxPopulation() { + int population = 0; + for (Swatch swatch : mSwatches) { + population = Math.max(population, swatch.getPopulation()); + } + return population; + } + + private Swatch findColorVariation(float targetLuma, float minLuma, float maxLuma, + float targetSaturation, float minSaturation, float maxSaturation) { + Swatch max = null; + float maxValue = 0f; + + for (Swatch swatch : mSwatches) { + final float sat = swatch.getHsl()[1]; + final float luma = swatch.getHsl()[2]; + + if (sat >= minSaturation && sat <= maxSaturation && + luma >= minLuma && luma <= maxLuma && + !isAlreadySelected(swatch)) { + float value = createComparisonValue(sat, targetSaturation, luma, targetLuma, + swatch.getPopulation(), mHighestPopulation); + if (max == null || value > maxValue) { + max = swatch; + maxValue = value; + } + } + } + + return max; + } + + /** + * @return true if we have already selected {@code swatch} + */ + private boolean isAlreadySelected(Swatch swatch) { + return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch || + mLightVibrantSwatch == swatch || mMutedSwatch == swatch || + mDarkMutedSwatch == swatch || mLightMutedSwatch == swatch; + } + + private static float createComparisonValue(float saturation, float targetSaturation, + float luma, float targetLuma, + int population, int maxPopulation) { + return createComparisonValue(saturation, targetSaturation, WEIGHT_SATURATION, + luma, targetLuma, WEIGHT_LUMA, + population, maxPopulation, WEIGHT_POPULATION); + } + + private static float createComparisonValue( + float saturation, float targetSaturation, float saturationWeight, + float luma, float targetLuma, float lumaWeight, + int population, int maxPopulation, float populationWeight) { + return weightedMean( + invertDiff(saturation, targetSaturation), saturationWeight, + invertDiff(luma, targetLuma), lumaWeight, + population / (float) maxPopulation, populationWeight + ); + } + + /** + * Copy a {@link Swatch}'s HSL values into a new float[]. + */ + private static float[] copyHslValues(Swatch color) { + final float[] newHsl = new float[3]; + System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); + return newHsl; + } + + /** + * Returns a value in the range 0-1. 1 is returned when {@code value} equals the + * {@code targetValue} and then decreases as the absolute difference between {@code value} and + * {@code targetValue} increases. + * + * @param value the item's value + * @param targetValue the value which we desire + */ + private static float invertDiff(float value, float targetValue) { + return 1f - Math.abs(value - targetValue); + } + + private static float weightedMean(float... values) { + float sum = 0f; + float sumWeight = 0f; + + for (int i = 0; i < values.length; i += 2) { + float value = values[i]; + float weight = values[i + 1]; + + sum += (value * weight); + sumWeight += weight; + } + + return sum / sumWeight; + } +} diff --git a/core/java/com/android/internal/lineage/util/palette/Palette.java b/core/java/com/android/internal/lineage/util/palette/Palette.java new file mode 100644 index 0000000000000..4d80ff7b5c864 --- /dev/null +++ b/core/java/com/android/internal/lineage/util/palette/Palette.java @@ -0,0 +1,740 @@ +/* + * Copyright 2014 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.internal.lineage.util.palette; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.AsyncTask; +import android.annotation.ColorInt; +import android.annotation.Nullable; +import android.util.TimingLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A helper class to extract prominent colors from an image. + *

+ * A number of colors with different profiles are extracted from the image: + *

    + *
  • Vibrant
  • + *
  • Vibrant Dark
  • + *
  • Vibrant Light
  • + *
  • Muted
  • + *
  • Muted Dark
  • + *
  • Muted Light
  • + *
+ * These can be retrieved from the appropriate getter method. + * + *

+ * Instances are created with a {@link Builder} which supports several options to tweak the + * generated Palette. See that class' documentation for more information. + *

+ * Generation should always be completed on a background thread, ideally the one in + * which you load your image on. {@link Builder} supports both synchronous and asynchronous + * generation: + * + *

+ * // Synchronous
+ * Palette p = Palette.from(bitmap).generate();
+ *
+ * // Asynchronous
+ * Palette.from(bitmap).generate(new PaletteAsyncListener() {
+ *     public void onGenerated(Palette p) {
+ *         // Use generated instance
+ *     }
+ * });
+ * 
+ * + * @hide + */ +public final class Palette { + + /** + * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or + * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)} + */ + public interface PaletteAsyncListener { + + /** + * Called when the {@link Palette} has been generated. + */ + void onGenerated(Palette palette); + } + + private static final int DEFAULT_RESIZE_BITMAP_MAX_DIMENSION = 192; + private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; + + private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; + private static final float MIN_CONTRAST_BODY_TEXT = 4.5f; + + private static final String LOG_TAG = "Palette"; + private static final boolean LOG_TIMINGS = false; + + /** + * Start generating a {@link Palette} with the returned {@link Builder} instance. + */ + public static Builder from(Bitmap bitmap) { + return new Builder(bitmap); + } + + /** + * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. + * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a + * list of swatches. Will return null if the {@code swatches} is null. + */ + public static Palette from(List swatches) { + return new Builder(swatches).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap) { + return from(bitmap).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap, int numColors) { + return from(bitmap).maximumColorCount(numColors).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask generateAsync( + Bitmap bitmap, PaletteAsyncListener listener) { + return from(bitmap).generate(listener); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask generateAsync( + final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) { + return from(bitmap).maximumColorCount(numColors).generate(listener); + } + + private final List mSwatches; + private final Generator mGenerator; + + private Palette(List swatches, Generator generator) { + mSwatches = swatches; + mGenerator = generator; + } + + /** + * Returns all of the swatches which make up the palette. + */ + public List getSwatches() { + return Collections.unmodifiableList(mSwatches); + } + + /** + * Returns the most vibrant swatch in the palette. Might be null. + */ + @Nullable + public Swatch getVibrantSwatch() { + return mGenerator.getVibrantSwatch(); + } + + /** + * Returns a light and vibrant swatch from the palette. Might be null. + */ + @Nullable + public Swatch getLightVibrantSwatch() { + return mGenerator.getLightVibrantSwatch(); + } + + /** + * Returns a dark and vibrant swatch from the palette. Might be null. + */ + @Nullable + public Swatch getDarkVibrantSwatch() { + return mGenerator.getDarkVibrantSwatch(); + } + + /** + * Returns a muted swatch from the palette. Might be null. + */ + @Nullable + public Swatch getMutedSwatch() { + return mGenerator.getMutedSwatch(); + } + + /** + * Returns a muted and light swatch from the palette. Might be null. + */ + @Nullable + public Swatch getLightMutedSwatch() { + return mGenerator.getLightMutedSwatch(); + } + + /** + * Returns a muted and dark swatch from the palette. Might be null. + */ + @Nullable + public Swatch getDarkMutedSwatch() { + return mGenerator.getDarkMutedSwatch(); + } + + /** + * Returns the most vibrant color in the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a light and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getLightVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getLightVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a dark and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getDarkVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getDarkVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted and light color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getLightMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getLightMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted and dark color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getDarkMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getDarkMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Scale the bitmap down so that it's largest dimension is {@code targetMaxDimension}. + * If {@code bitmap} is smaller than this, then it is returned. + */ + private static Bitmap scaleBitmapDown(Bitmap bitmap, final int targetMaxDimension) { + final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); + + if (maxDimension <= targetMaxDimension) { + // If the bitmap is small enough already, just return it + return bitmap; + } + + final float scaleRatio = targetMaxDimension / (float) maxDimension; + return Bitmap.createScaledBitmap(bitmap, + Math.round(bitmap.getWidth() * scaleRatio), + Math.round(bitmap.getHeight() * scaleRatio), + false); + } + + /** + * Represents a color swatch generated from an image's palette. The RGB color can be retrieved + * by calling {@link #getRgb()}. + */ + public static final class Swatch { + private final int mRed, mGreen, mBlue; + private final int mRgb; + private final int mPopulation; + + private boolean mGeneratedTextColors; + private int mTitleTextColor; + private int mBodyTextColor; + + private float[] mHsl; + + public Swatch(@ColorInt int color, int population) { + mRed = Color.red(color); + mGreen = Color.green(color); + mBlue = Color.blue(color); + mRgb = color; + mPopulation = population; + } + + Swatch(int red, int green, int blue, int population) { + mRed = red; + mGreen = green; + mBlue = blue; + mRgb = Color.rgb(red, green, blue); + mPopulation = population; + } + + /** + * @return this swatch's RGB color value + */ + @ColorInt + public int getRgb() { + return mRgb; + } + + /** + * Return this swatch's HSL values. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Lightness [0...1] + */ + public float[] getHsl() { + if (mHsl == null) { + mHsl = new float[3]; + ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl); + } + return mHsl; + } + + /** + * @return the number of pixels represented by this swatch + */ + public int getPopulation() { + return mPopulation; + } + + /** + * Returns an appropriate color to use for any 'title' text which is displayed over this + * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getTitleTextColor() { + ensureTextColorsGenerated(); + return mTitleTextColor; + } + + /** + * Returns an appropriate color to use for any 'body' text which is displayed over this + * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getBodyTextColor() { + ensureTextColorsGenerated(); + return mBodyTextColor; + } + + private void ensureTextColorsGenerated() { + if (!mGeneratedTextColors) { + // First check white, as most colors will be dark + final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT); + final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (lightBodyAlpha != -1 && lightTitleAlpha != -1) { + // If we found valid light values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha); + mGeneratedTextColors = true; + return; + } + + final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT); + final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (darkBodyAlpha != -1 && darkBodyAlpha != -1) { + // If we found valid dark values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + return; + } + + // If we reach here then we can not find title and body values which use the same + // lightness, we need to use mismatched values + mBodyTextColor = lightBodyAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = lightTitleAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + } + } + + @Override + public String toString() { + return new StringBuilder(getClass().getSimpleName()) + .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') + .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') + .append(" [Population: ").append(mPopulation).append(']') + .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor())) + .append(']') + .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor())) + .append(']').toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Swatch swatch = (Swatch) o; + return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; + } + + @Override + public int hashCode() { + return 31 * mRgb + mPopulation; + } + } + + /** + * Builder class for generating {@link Palette} instances. + */ + public static final class Builder { + private List mSwatches; + private Bitmap mBitmap; + private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS; + private int mResizeMaxDimension = DEFAULT_RESIZE_BITMAP_MAX_DIMENSION; + private final List mFilters = new ArrayList<>(); + + private Generator mGenerator; + + /** + * Construct a new {@link Builder} using a source {@link Bitmap} + */ + public Builder(Bitmap bitmap) { + this(); + if (bitmap == null || bitmap.isRecycled()) { + throw new IllegalArgumentException("Bitmap is not valid"); + } + mBitmap = bitmap; + } + + /** + * Construct a new {@link Builder} using a list of {@link Swatch} instances. + * Typically only used for testing. + */ + public Builder(List swatches) { + this(); + if (swatches == null || swatches.isEmpty()) { + throw new IllegalArgumentException("List of Swatches is not valid"); + } + mSwatches = swatches; + } + + private Builder() { + mFilters.add(DEFAULT_FILTER); + } + + /** + * Set the {@link Generator} to use when generating the {@link Palette}. If this is called + * with {@code null} then the default generator will be used. + */ + Builder generator(Generator generator) { + mGenerator = generator; + return this; + } + + /** + * Set the maximum number of colors to use in the quantization step when using a + * {@link android.graphics.Bitmap} as the source. + *

+ * Good values for depend on the source image type. For landscapes, good values are in + * the range 10-16. For images which are largely made up of people's faces then this + * value should be increased to ~24. + */ + public Builder maximumColorCount(int colors) { + mMaxColors = colors; + return this; + } + + /** + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. + * If the bitmap's largest dimension is greater than the value specified, then the bitmap + * will be resized so that it's largest dimension matches {@code maxDimension}. If the + * bitmap is smaller or equal, the original is used as-is. + *

+ * This value has a large effect on the processing time. The larger the resized image is, + * the greater time it will take to generate the palette. The smaller the image is, the + * more detail is lost in the resulting image and thus less precision for color selection. + */ + public Builder resizeBitmapSize(int maxDimension) { + mResizeMaxDimension = maxDimension; + return this; + } + + /** + * Clear all added filters. This includes any default filters added automatically by + * {@link Palette}. + */ + public Builder clearFilters() { + mFilters.clear(); + return this; + } + + /** + * Add a filter to be able to have fine grained controlled over the colors which are + * allowed in the resulting palette. + * + * @param filter filter to add. + */ + public Builder addFilter(Filter filter) { + if (filter != null) { + mFilters.add(filter); + } + return this; + } + + /** + * Generate and return the {@link Palette} synchronously. + */ + public Palette generate() { + final TimingLogger logger = LOG_TIMINGS + ? new TimingLogger(LOG_TAG, "Generation") + : null; + + List swatches; + + if (mBitmap != null) { + // We have a Bitmap so we need to quantization to reduce the number of colors + + if (mResizeMaxDimension <= 0) { + throw new IllegalArgumentException( + "Minimum dimension size for resizing should should be >= 1"); + } + + // First we'll scale down the bitmap so it's largest dimension is as specified + final Bitmap scaledBitmap = scaleBitmapDown(mBitmap, mResizeMaxDimension); + + if (logger != null) { + logger.addSplit("Processed Bitmap"); + } + + // Now generate a quantizer from the Bitmap + final int width = scaledBitmap.getWidth(); + final int height = scaledBitmap.getHeight(); + final int[] pixels = new int[width * height]; + scaledBitmap.getPixels(pixels, 0, width, 0, 0, width, height); + + final ColorCutQuantizer quantizer = new ColorCutQuantizer(pixels, mMaxColors, + mFilters.isEmpty() ? null : mFilters.toArray(new Filter[0])); + + // If created a new bitmap, recycle it + if (scaledBitmap != mBitmap) { + scaledBitmap.recycle(); + } + swatches = quantizer.getQuantizedColors(); + + if (logger != null) { + logger.addSplit("Color quantization completed"); + } + } else { + // Else we're using the provided swatches + swatches = mSwatches; + } + + // If we haven't been provided with a generator, use the default + if (mGenerator == null) { + mGenerator = new DefaultGenerator(); + } + + // Now call let the Generator do it's thing + mGenerator.generate(swatches); + + if (logger != null) { + logger.addSplit("Generator.generate() completed"); + } + + // Now create a Palette instance + Palette p = new Palette(swatches, mGenerator); + + if (logger != null) { + logger.addSplit("Created Palette"); + logger.dumpToLog(); + } + + return p; + } + + /** + * Generate the {@link Palette} asynchronously. The provided listener's + * {@link PaletteAsyncListener#onGenerated} method will be called with the palette when + * generated. + */ + public AsyncTask generate(final PaletteAsyncListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener can not be null"); + } + + AsyncTask task = new AsyncTask() { + @Override + protected Palette doInBackground(Bitmap... params) { + return generate(); + } + + @Override + protected void onPostExecute(Palette colorExtractor) { + listener.onGenerated(colorExtractor); + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); + return task; + } + } + + static abstract class Generator { + + /** + * This method will be called with the {@link Palette.Swatch} that represent an image. + * You should process this list so that you have appropriate values when the other methods in + * class are called. + *

+ * This method will probably be called on a background thread. + */ + public abstract void generate(List swatches); + + /** + * Return the most vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getVibrantSwatch() { + return null; + } + + /** + * Return a light and vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getLightVibrantSwatch() { + return null; + } + + /** + * Return a dark and vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getDarkVibrantSwatch() { + return null; + } + + /** + * Return a muted {@link Palette.Swatch} + */ + public Palette.Swatch getMutedSwatch() { + return null; + } + + /** + * Return a muted and light {@link Palette.Swatch} + */ + public Palette.Swatch getLightMutedSwatch() { + return null; + } + + /** + * Return a muted and dark {@link Palette.Swatch} + */ + public Palette.Swatch getDarkMutedSwatch() { + return null; + } + } + + /** + * A Filter provides a mechanism for exercising fine-grained control over which colors + * are valid within a resulting {@link Palette}. + */ + public interface Filter { + /** + * Hook to allow clients to be able filter colors from resulting palette. + * + * @param rgb the color in RGB888. + * @param hsl HSL representation of the color. + * + * @return true if the color is allowed, false if not. + * + * @see Builder#addFilter(Filter) + */ + boolean isAllowed(int rgb, float[] hsl); + } + + /** + * The default filter. + */ + private static final Filter DEFAULT_FILTER = new Filter() { + private static final float BLACK_MAX_LIGHTNESS = 0.05f; + private static final float WHITE_MIN_LIGHTNESS = 0.95f; + + @Override + public boolean isAllowed(int rgb, float[] hsl) { + return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl); + } + + /** + * @return true if the color represents a color which is close to black. + */ + private boolean isBlack(float[] hslColor) { + return hslColor[2] <= BLACK_MAX_LIGHTNESS; + } + + /** + * @return true if the color represents a color which is close to white. + */ + private boolean isWhite(float[] hslColor) { + return hslColor[2] >= WHITE_MIN_LIGHTNESS; + } + + /** + * @return true if the color lies close to the red side of the I line. + */ + private boolean isNearRedILine(float[] hslColor) { + return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f; + } + }; +} diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 681b46a01c8db..59e5dfbfff62d 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -68,6 +68,7 @@ public class SystemNotificationChannels { public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION"; public static String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY"; public static String ABUSIVE_BACKGROUND_APPS = "ABUSIVE_BACKGROUND_APPS"; + public static String SLEEP = "SLEEP"; public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); @@ -113,7 +114,7 @@ public static void createAll(Context context) { final NotificationChannel developerImportant = new NotificationChannel( DEVELOPER_IMPORTANT, context.getString(R.string.notification_channel_developer_important), - NotificationManager.IMPORTANCE_HIGH); + NotificationManager.IMPORTANCE_MIN); developer.setBlockable(true); channelsList.add(developerImportant); @@ -172,6 +173,7 @@ public static void createAll(Context context) { USB, context.getString(R.string.notification_channel_usb), NotificationManager.IMPORTANCE_MIN); + usb.setBlockable(true); channelsList.add(usb); NotificationChannel foregroundChannel = new NotificationChannel( @@ -206,6 +208,11 @@ public static void createAll(Context context) { NotificationManager.IMPORTANCE_LOW); channelsList.add(dndChanges); + NotificationChannel sleepModeChanges = new NotificationChannel(SLEEP, + context.getString(R.string.notification_channel_sleep), + NotificationManager.IMPORTANCE_LOW); + channelsList.add(sleepModeChanges); + final NotificationChannel newFeaturePrompt = new NotificationChannel( ACCESSIBILITY_MAGNIFICATION, context.getString(R.string.notification_channel_accessibility_magnification), diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 98d4c5976adc8..2f516d8bdb36c 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -7906,20 +7906,31 @@ public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes synchronized (mModemNetworkLock) { if (displayTransport == TRANSPORT_CELLULAR) { mModemIfaces = includeInStringArray(mModemIfaces, iface); - if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces); + if (DEBUG) { + Slog.d(TAG, "Note mobile iface " + iface + ": " + + Arrays.toString(mModemIfaces)); + } } else { mModemIfaces = excludeFromStringArray(mModemIfaces, iface); - if (DEBUG) Slog.d(TAG, "Note non-mobile iface " + iface + ": " + mModemIfaces); + if (DEBUG) { + Slog.d(TAG, "Note non-mobile iface " + iface + ": " + + Arrays.toString(mModemIfaces)); + } } } synchronized (mWifiNetworkLock) { if (displayTransport == TRANSPORT_WIFI) { mWifiIfaces = includeInStringArray(mWifiIfaces, iface); - if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces); + if (DEBUG) { + Slog.d(TAG, "Note wifi iface " + iface + ": " + Arrays.toString(mWifiIfaces)); + } } else { mWifiIfaces = excludeFromStringArray(mWifiIfaces, iface); - if (DEBUG) Slog.d(TAG, "Note non-wifi iface " + iface + ": " + mWifiIfaces); + if (DEBUG) { + Slog.d(TAG, "Note non-wifi iface " + iface + ": " + + Arrays.toString(mWifiIfaces)); + } } } } @@ -14387,7 +14398,7 @@ private void updateCpuMeasuredEnergyStatsLocked(@NonNull long[] clusterChargeUC, @NonNull CpuDeltaPowerAccumulator accumulator) { if (DEBUG_ENERGY) { Slog.d(TAG, - "Updating cpu cluster stats: " + clusterChargeUC.toString()); + "Updating cpu cluster stats: " + Arrays.toString(clusterChargeUC)); } if (mGlobalMeasuredEnergyStats == null) { return; diff --git a/core/java/com/android/internal/os/DeviceKeyHandler.java b/core/java/com/android/internal/os/DeviceKeyHandler.java new file mode 100644 index 0000000000000..8902337f3ebb3 --- /dev/null +++ b/core/java/com/android/internal/os/DeviceKeyHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The CyanogenMod 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.internal.os; + +import android.view.KeyEvent; + +public interface DeviceKeyHandler { + + /** + * Invoked when an unknown key was detected by the system, letting the device handle + * this special keys prior to pass the key to the active app. + * + * @param event The key event to be handled + * @return null if event is consumed, KeyEvent to be handled otherwise + */ + public KeyEvent handleKeyEvent(KeyEvent event); +} diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index c801be0ce3e77..7ea55dcbe67f4 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; import android.os.StrictMode; import android.util.IntArray; import android.util.Slog; @@ -497,7 +498,7 @@ private void processUidDelta(@Nullable Callback cb) { // Unit is 10ms. mDeltaTimes[i] = mCurTimes[i] - lastTimes[i]; if (mDeltaTimes[i] < 0) { - Slog.e(mTag, "Negative delta from freq time for uid: " + uid + if (DEBUG) Slog.e(mTag, "Negative delta from freq time for uid: " + uid + ", delta: " + mDeltaTimes[i]); return; } @@ -530,7 +531,11 @@ void readDeltaImpl(@Nullable Callback cb, boolean forceRead) { CharBuffer buf; while ((buf = iter.nextLine()) != null) { if (asLongs(buf, mBuffer) != mBuffer.length) { - Slog.wtf(mTag, "Invalid line: " + buf.toString()); + if (Build.IS_ENG) { + Slog.wtf(mTag, "Invalid line: " + buf.toString()); + } else { + Slog.w(mTag, "Invalid line: " + buf.toString()); + } continue; } processUidDelta(cb); @@ -673,7 +678,7 @@ private void processUidDelta(@Nullable Callback cb) { cb.onUidCpuTime(uid, delta); } } else if (delta < 0) { - Slog.e(mTag, "Negative delta from active time for uid: " + uid + if (DEBUG) Slog.e(mTag, "Negative delta from active time for uid: " + uid + ", delta: " + delta); } } diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java index 205c5fd735eac..dc416eaa940df 100644 --- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java +++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java @@ -18,8 +18,11 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BACK_GESTURE_EDGE_WIDTH; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; @@ -29,6 +32,8 @@ import android.util.DisplayMetrics; import android.util.TypedValue; +import com.android.internal.util.cherish.CherishUtils; + /** * @hide */ @@ -36,6 +41,7 @@ public class GestureNavigationSettingsObserver extends ContentObserver { private Context mContext; private Runnable mOnChangeRunnable; private Handler mMainHandler; + private IntentFilter mIntentFilter; public GestureNavigationSettingsObserver(Handler handler, Context context, Runnable onChangeRunnable) { @@ -43,6 +49,9 @@ public GestureNavigationSettingsObserver(Handler handler, Context context, mMainHandler = handler; mContext = context; mOnChangeRunnable = onChangeRunnable; + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + mIntentFilter.addDataScheme("package"); } private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = @@ -56,7 +65,79 @@ public void onPropertiesChanged(DeviceConfig.Properties properties) { } }; + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if(action.equals(Intent.ACTION_PACKAGE_REMOVED)) { + // Get packageName from Uri + String packageName = intent.getData().getSchemeSpecificPart(); + // If the package is still installed + if (CherishUtils.isPackageInstalled(context, packageName)) { + // it's an application update, we can skip the rest. + return; + } + // Get package names currently set as default + String leftPackageName = Settings.System.getStringForUser(context.getContentResolver(), + Settings.System.LEFT_LONG_BACK_SWIPE_APP_ACTION, + UserHandle.USER_CURRENT); + String rightPackageName = Settings.System.getStringForUser(context.getContentResolver(), + Settings.System.RIGHT_LONG_BACK_SWIPE_APP_ACTION, + UserHandle.USER_CURRENT); + String verticalLeftPackageName = Settings.System.getStringForUser(context.getContentResolver(), + Settings.System.LEFT_VERTICAL_BACK_SWIPE_APP_ACTION, + UserHandle.USER_CURRENT); + String verticalRightPackageName = Settings.System.getStringForUser(context.getContentResolver(), + Settings.System.RIGHT_VERTICAL_BACK_SWIPE_APP_ACTION, + UserHandle.USER_CURRENT); + // if the package name equals to some set value + if(packageName.equals(leftPackageName)) { + // The short application action has to be reset + resetApplicationAction(true, false); + } + if (packageName.equals(rightPackageName)) { + // The long application action has to be reset + resetApplicationAction(false, false); + } + if(packageName.equals(verticalLeftPackageName)) { + // The short application action has to be reset + resetApplicationAction(true, true); + } + if (packageName.equals(verticalRightPackageName)) { + // The long application action has to be reset + resetApplicationAction(false, true); + } + } + } + }; + + private void resetApplicationAction(boolean isLeftAction, boolean isVertical) { + if (isLeftAction) { + // Remove stored values + Settings.System.putIntForUser(mContext.getContentResolver(), + isVertical ? Settings.System.LEFT_VERTICAL_BACK_SWIPE_ACTION : Settings.System.LEFT_LONG_BACK_SWIPE_ACTION, + /* no action */ 0, + UserHandle.USER_CURRENT); + Settings.System.putStringForUser(mContext.getContentResolver(), + isVertical ? Settings.System.LEFT_VERTICAL_BACK_SWIPE_APP_FR_ACTION : Settings.System.LEFT_LONG_BACK_SWIPE_APP_FR_ACTION, + /* none */ "", + UserHandle.USER_CURRENT); + } else { + // Remove stored values + Settings.System.putIntForUser(mContext.getContentResolver(), + isVertical ? Settings.System.RIGHT_VERTICAL_BACK_SWIPE_ACTION : Settings.System.RIGHT_LONG_BACK_SWIPE_ACTION, + /* no action */ 0, + UserHandle.USER_CURRENT); + Settings.System.putStringForUser(mContext.getContentResolver(), + isVertical ? Settings.System.RIGHT_VERTICAL_BACK_SWIPE_APP_FR_ACTION : Settings.System.RIGHT_LONG_BACK_SWIPE_APP_FR_ACTION, + /* none */ "", + UserHandle.USER_CURRENT); + } + // the observer will trigger EdgeBackGestureHandler.updateCurrentUserResources and update settings there too + } + public void register() { + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); ContentResolver r = mContext.getContentResolver(); r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT), @@ -64,6 +145,9 @@ public void register() { r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT), false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.SHOW_BACK_ARROW_GESTURE), + false, this, UserHandle.USER_ALL); r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL); @@ -71,9 +155,28 @@ public void register() { DeviceConfig.NAMESPACE_SYSTEMUI, runnable -> mMainHandler.post(runnable), mOnPropertiesChangedListener); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.LONG_BACK_SWIPE_TIMEOUT), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.LEFT_LONG_BACK_SWIPE_ACTION), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.RIGHT_LONG_BACK_SWIPE_ACTION), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.BACK_SWIPE_EXTENDED), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.LEFT_VERTICAL_BACK_SWIPE_ACTION), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.RIGHT_VERTICAL_BACK_SWIPE_ACTION), + false, this, UserHandle.USER_ALL); } public void unregister() { + mContext.unregisterReceiver(mBroadcastReceiver); mContext.getContentResolver().unregisterContentObserver(this); DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); } @@ -94,6 +197,12 @@ public int getRightSensitivity(Resources userRes) { return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT); } + public boolean getBackArrowGesture() { + return Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.SHOW_BACK_ARROW_GESTURE, 1, + UserHandle.USER_CURRENT) == 1; + } + public boolean areNavigationButtonForcedVisible() { return Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0; @@ -114,4 +223,40 @@ private int getSensitivity(Resources userRes, String side) { mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT); return (int) (inset * scale); } + + public int getLongSwipeTimeOut() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.LONG_BACK_SWIPE_TIMEOUT, 2000, + UserHandle.USER_CURRENT); + } + + public int getLeftLongSwipeAction() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.LEFT_LONG_BACK_SWIPE_ACTION, 0, + UserHandle.USER_CURRENT); + } + + public int getRightLongSwipeAction() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.RIGHT_LONG_BACK_SWIPE_ACTION, 0, + UserHandle.USER_CURRENT); + } + + public boolean getIsExtendedSwipe() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.BACK_SWIPE_EXTENDED, 0, + UserHandle.USER_CURRENT) != 0; + } + + public int getLeftLSwipeAction() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.LEFT_VERTICAL_BACK_SWIPE_ACTION, 0, + UserHandle.USER_CURRENT); + } + + public int getRightLSwipeAction() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.RIGHT_VERTICAL_BACK_SWIPE_ACTION, 0, + UserHandle.USER_CURRENT); + } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 9c0fad902a52e..2f68b65434ea1 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; @@ -2541,6 +2542,16 @@ protected ViewGroup generateLayout(DecorView decor) { params.layoutInDisplayCutoutMode = mode; } + if (ActivityManager.isSystemReady()) { + try { + String packageName = context.getBasePackageName(); + if (ActivityManager.getService().shouldForceCutoutFullscreen(packageName)){ + params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + } catch (RemoteException e) { + } + } + if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index e93a7854f1cda..c837afed3af73 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -46,6 +46,7 @@ oneway interface IStatusBar void animateExpandSettingsPanel(String subPanel); void animateCollapsePanels(); void togglePanel(); + void toggleSettingsPanel(); void showWirelessChargingAnimation(int batteryLevel); @@ -148,7 +149,7 @@ oneway interface IStatusBar void showPinningEnterExitToast(boolean entering); void showPinningEscapeToast(); - void showShutdownUi(boolean isReboot, String reason); + void showShutdownUi(boolean isReboot, String reason, boolean advancedReboot); /** * Used to show the authentication dialog (Biometrics, Device Credential). @@ -324,4 +325,15 @@ oneway interface IStatusBar /** Unregisters a nearby media devices provider. */ void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider); + + void toggleCameraFlash(); + + /** + * Used to block or unblock usage of gestural navigation + */ + void setBlockedGesturalNavigation(boolean blocked); + + void screenPinningStateChanged(boolean enabled); + void leftInLandscapeChanged(boolean isLeft); + void killForegroundApp(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index ef8f2db5ff574..def11b0e08481 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -49,6 +49,7 @@ interface IStatusBarService @UnsupportedAppUsage void collapsePanels(); void togglePanel(); + void toggleSettingsPanel(); @UnsupportedAppUsage void disable(int what, IBinder token, String pkg); void disableForUser(int what, IBinder token, String pkg, int userId); @@ -102,6 +103,7 @@ interface IStatusBarService */ void shutdown(); void reboot(boolean safeMode); + void advancedReboot(String mode); /** just restarts android without rebooting device. Used for some feature flags. */ void restart(); @@ -226,4 +228,21 @@ interface IStatusBarService /** Unregisters a nearby media devices provider. */ void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider); + + /** Starts the default assistant app. */ + void startAssist(in Bundle args); + + void toggleCameraFlash(); + void toggleRecentApps(); + void toggleSplitScreen(); + void preloadRecentApps(); + void cancelPreloadRecentApps(); + void killForegroundApp(); + + /** + * Used to block or unblock usage of gestural navigation + */ + void setBlockedGesturalNavigation(boolean blocked); + + void screenPinningStateChanged(boolean enabled); } diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java index ced272225f487..b63a44ae14da4 100644 --- a/core/java/com/android/internal/util/ContrastColorUtil.java +++ b/core/java/com/android/internal/util/ContrastColorUtil.java @@ -703,8 +703,10 @@ public static double calculateLuminance(@ColorInt int color) { */ public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { if (Color.alpha(background) != 255) { - Log.wtf(TAG, "background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } if (Color.alpha(foreground) < 255) { // If the foreground is translucent, composite the foreground over the background diff --git a/core/java/com/android/internal/util/PastyException.java b/core/java/com/android/internal/util/PastyException.java new file mode 100644 index 0000000000000..9953562a09421 --- /dev/null +++ b/core/java/com/android/internal/util/PastyException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 Potato Open Sauce 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.internal.util; + +public class PastyException extends Exception { + + private static final long serialVersionUID = 666L; + + public PastyException(String message) { + super(message); + } +} diff --git a/core/java/com/android/internal/util/PastyUtils.java b/core/java/com/android/internal/util/PastyUtils.java new file mode 100644 index 0000000000000..65fe8b669b8c1 --- /dev/null +++ b/core/java/com/android/internal/util/PastyUtils.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 Potato Open Sauce 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.internal.util; + +import android.os.Handler; +import android.os.HandlerThread; +import android.util.JsonReader; + +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Helper functions for uploading to Pasty + */ +public final class PastyUtils { + private static final String TAG = "PastyUtils"; + private static final String BASE_URL = "https://paste.evolution-x.org"; + private static final String API_URL = String.format("%s/documents", BASE_URL); + private static Handler handler; + + private PastyUtils() { + } + + /** + * Uploads {@code content} to Pasty + * + * @param content the content to upload to Pasty + * @param callback the callback to call on success / failure + */ + public static void upload(String content, UploadResultCallback callback) { + getHandler().post(new Runnable() { + @Override + public void run() { + try { + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(API_URL).openConnection(); + try { + urlConnection.setRequestProperty("Content-Type", "text/plain"); + urlConnection.setRequestProperty("Accept-Charset", "UTF-8"); + urlConnection.setDoOutput(true); + + try (OutputStream output = urlConnection.getOutputStream()) { + output.write(content.getBytes("UTF-8")); + } + String key = ""; + try (JsonReader reader = new JsonReader( + new InputStreamReader(urlConnection.getInputStream(), "UTF-8"))) { + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (name.equals("key")) { + key = reader.nextString(); + break; + } else { + reader.skipValue(); + } + } + reader.endObject(); + } + if (!key.isEmpty()) { + callback.onSuccess(getUrl(key)); + } else { + String msg = "Failed to upload to Pasty: No key retrieved"; + callback.onFail(msg, new PastyException(msg)); + } + } finally { + urlConnection.disconnect(); + } + } catch (Exception e) { + callback.onFail("Failed to upload to Pasty", e); + } + } + }); + } + + /** + * Get the view URL from a key + */ + private static String getUrl(String key) { + return String.format("%s/%s", BASE_URL, key); + } + + private static Handler getHandler() { + if (handler == null) { + HandlerThread handlerThread = new HandlerThread("PastyThread"); + if (!handlerThread.isAlive()) + handlerThread.start(); + handler = new Handler(handlerThread.getLooper()); + } + return handler; + } + + public interface UploadResultCallback { + void onSuccess(String url); + + void onFail(String message, Exception e); + } +} diff --git a/core/java/com/android/internal/util/cherish/CherishUtils.java b/core/java/com/android/internal/util/cherish/CherishUtils.java new file mode 100644 index 0000000000000..aa05eb253f0b8 --- /dev/null +++ b/core/java/com/android/internal/util/cherish/CherishUtils.java @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2014 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.internal.util.cherish; + +import static android.provider.Settings.Global.ZEN_MODE_OFF; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; +import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; + +import android.Manifest; +import android.app.ActivityManager; +import android.app.ActivityThread; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.Intent; +import android.view.WindowManagerGlobal; +import android.content.res.Resources; +import android.os.PowerManager; +import android.app.AlertDialog; +import android.app.IActivityManager; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorManager; +import android.graphics.Point; +import android.graphics.Rect; +import android.location.LocationManager; +import android.media.AudioManager; +import android.os.Handler; +import android.os.Looper; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.input.InputManager; +import android.hardware.fingerprint.FingerprintManager; +import android.view.IWindowManager; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.telephony.SubscriptionManager; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.Surface; + +import android.widget.Toast; + +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.ArrayUtils; +import com.android.internal.R; + +import java.util.ArrayList; + +import java.util.List; +import java.util.Locale; + +public class CherishUtils { + private static final String TAG = "CherishUtils"; + + private static final boolean DEBUG = false; + + private static final int NO_CUTOUT = -1; + + private static OverlayManager mOverlayService; + + public static final String INTENT_SCREENSHOT = "action_take_screenshot"; + public static final String INTENT_REGION_SCREENSHOT = "action_take_region_screenshot"; + + public static boolean isChineseLanguage() { + return Resources.getSystem().getConfiguration().locale.getLanguage().startsWith( + Locale.CHINESE.getLanguage()); + } + + public static boolean isAppInstalled(Context context, String appUri) { + try { + PackageManager pm = context.getPackageManager(); + pm.getPackageInfo(appUri, PackageManager.GET_ACTIVITIES); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean isAvailableApp(String packageName, Context context) { + Context mContext = context; + final PackageManager pm = mContext.getPackageManager(); + try { + pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + int enabled = pm.getApplicationEnabledSetting(packageName); + return enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED && + enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; + } catch (NameNotFoundException e) { + return false; + } + } + + public static boolean isPackageInstalled(Context context, String pkg, boolean ignoreState) { + if (pkg != null) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(pkg, 0); + if (!pi.applicationInfo.enabled && !ignoreState) { + return false; + } + } catch (NameNotFoundException e) { + return false; + } + } + + return true; + } + + public static boolean isPackageInstalled(Context context, String pkg) { + return isPackageInstalled(context, pkg, true); + } + + public static boolean deviceSupportsFlashLight(Context context) { + CameraManager cameraManager = (CameraManager) context.getSystemService( + Context.CAMERA_SERVICE); + try { + String[] ids = cameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null + && flashAvailable + && lensFacing != null + && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + return true; + } + } + } catch (CameraAccessException e) { + // Ignore + } + return false; + } + + public static boolean isWifiOnly(Context context) { + return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + } + + // Check to see if Wifi is connected + public static boolean isWifiConnected(Context context) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = null; + if (cm != null) { + activeNetwork = cm.getActiveNetworkInfo(); + } + NetworkInfo wifi = null; + if (cm != null) { + wifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + } + return activeNetwork != null && activeNetwork.isConnectedOrConnecting() && wifi.isConnected(); + } + + // Check to see if Mobile data is connected + public static boolean isMobileConnected(Context context) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = null; + if (cm != null) { + activeNetwork = cm.getActiveNetworkInfo(); + } + NetworkInfo mobile = null; + if (cm != null) { + mobile = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + } + return activeNetwork != null && activeNetwork.isConnectedOrConnecting() && mobile.isConnected(); + } + + // Check to see if device supports the Fingerprint scanner + public static boolean hasFingerprintSupport(Context context) { + FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); + return context.getApplicationContext().checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && + (fingerprintManager != null && fingerprintManager.isHardwareDetected()); + } + + // Check to see if device not only supports the Fingerprint scanner but also if is enrolled + public static boolean hasFingerprintEnrolled(Context context) { + FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); + return context.getApplicationContext().checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && + (fingerprintManager != null && fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()); + } + + // Check to see if device has a camera + public static boolean hasCamera(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); + } + + // Check to see if device supports NFC + public static boolean hasNFC(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC); + } + + // Check to see if device supports Wifi + public static boolean hasWiFi(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); + } + + // Check to see if device supports Bluetooth + public static boolean hasBluetooth(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); + } + + // Check to see if device supports an alterative ambient display package + public static boolean hasAltAmbientDisplay(Context context) { + return context.getResources().getBoolean(com.android.internal.R.bool.config_alt_ambient_display); + } + + public static boolean deviceHasFlashlight(Context ctx) { + return ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); + } + + public static void toggleCameraFlash() { + FireActions.toggleCameraFlash(); + } + + public static void killForegroundApp() { + FireActions.killForegroundApp(); + } + + public static void sendKeycode(int keycode) { + long when = SystemClock.uptimeMillis(); + final KeyEvent evDown = new KeyEvent(when, when, KeyEvent.ACTION_DOWN, keycode, 0, + 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, + InputDevice.SOURCE_NAVIGATION_BAR); + final KeyEvent evUp = KeyEvent.changeAction(evDown, KeyEvent.ACTION_UP); + + final Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + InputManager.getInstance().injectInputEvent(evDown, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }); + handler.postDelayed(new Runnable() { + @Override + public void run() { + InputManager.getInstance().injectInputEvent(evUp, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }, 20); + } + +public static void sendSystemKeyToStatusBar(int keyCode) { + FireActions.sendSystemKeyToStatusBar(keyCode); + } + + // Launch Power Menu dialog + public static void showPowerMenu() { + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.showGlobalActions(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + + public static void takeScreenshot(boolean full) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + // Do nothing + } + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.sendCustomAction(new Intent(full? INTENT_SCREENSHOT : INTENT_REGION_SCREENSHOT)); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + /** + * Keep FireAction methods below this point. + * Place calls to methods above this point. + */ + private static final class FireActions { + private static IStatusBarService mStatusBarService = null; + private static IStatusBarService getStatusBarService() { + synchronized (FireActions.class) { + if (mStatusBarService == null) { + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService("statusbar")); + } + return mStatusBarService; + } + } + + public static void killForegroundApp() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.killForegroundApp(); + } catch (RemoteException e) { + // do nothing. + } + } + } + + public static void sendSystemKeyToStatusBar(int keyCode) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.handleSystemKey(keyCode); + } catch (RemoteException e) { + // do nothing. + } + } + } + + public static void toggleCameraFlash() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleCameraFlash(); + } catch (RemoteException e) { + // do nothing. + } + } + } + } + + /* returns whether the device has a centered display cutout or not. */ + public static boolean hasCenteredCutout(Context context) { + Display display = context.getDisplay(); + DisplayCutout cutout = display.getCutout(); + if (cutout != null) { + Point realSize = new Point(); + display.getRealSize(realSize); + + switch (display.getRotation()) { + case Surface.ROTATION_0: { + Rect rect = cutout.getBoundingRectTop(); + return !(rect.left <= 0 || rect.right >= realSize.x); + } + case Surface.ROTATION_90: { + Rect rect = cutout.getBoundingRectLeft(); + return !(rect.top <= 0 || rect.bottom >= realSize.y); + } + case Surface.ROTATION_180: { + Rect rect = cutout.getBoundingRectBottom(); + return !(rect.left <= 0 || rect.right >= realSize.x); + } + case Surface.ROTATION_270: { + Rect rect = cutout.getBoundingRectRight(); + return !(rect.top <= 0 || rect.bottom >= realSize.y); + } + } + } + return false; + } + + /** + * Returns whether the device is voice-capable (meaning, it is also a phone). + */ + public static boolean isVoiceCapable(Context context) { + TelephonyManager telephony = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + return telephony != null && telephony.isVoiceCapable(); + } + + public static class SleepModeController { + private final Resources mResources; + private final Context mUiContext; + + private Context mContext; + private AudioManager mAudioManager; + private NotificationManager mNotificationManager; + private WifiManager mWifiManager; + private SensorPrivacyManager mSensorPrivacyManager; + private BluetoothAdapter mBluetoothAdapter; + private int mSubscriptionId; + private Toast mToast; + + private boolean mSleepModeEnabled; + + private static boolean mWifiState; + private static boolean mCellularState; + private static boolean mBluetoothState; + private static int mLocationState; + private static int mRingerState; + private static int mZenState; + + private static final String TAG = "SleepModeController"; + private static final int SLEEP_NOTIFICATION_ID = 727; + public static final String SLEEP_MODE_TURN_OFF = "android.intent.action.SLEEP_MODE_TURN_OFF"; + + public SleepModeController(Context context) { + mContext = context; + mUiContext = ActivityThread.currentActivityThread().getSystemUiContext(); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService(Context.SENSOR_PRIVACY_SERVICE); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + mResources = mContext.getResources(); + + mSleepModeEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + + SettingsObserver observer = new SettingsObserver(new Handler(Looper.getMainLooper())); + observer.observe(); + observer.update(); + } + + private TelephonyManager getTelephonyManager() { + int subscriptionId = mSubscriptionId; + + // If mSubscriptionId is invalid, get default data sub. + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + subscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); + } + + // If data sub is also invalid, get any active sub. + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + int[] activeSubIds = SubscriptionManager.from(mContext).getActiveSubscriptionIdList(); + if (!ArrayUtils.isEmpty(activeSubIds)) { + subscriptionId = activeSubIds[0]; + } + } + + return mContext.getSystemService( + TelephonyManager.class).createForSubscriptionId(subscriptionId); + } + + private boolean isWifiEnabled() { + if (mWifiManager == null) { + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + } + try { + return mWifiManager.isWifiEnabled(); + } catch (Exception e) { + return false; + } + } + + private void setWifiEnabled(boolean enable) { + if (mWifiManager == null) { + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + } + try { + mWifiManager.setWifiEnabled(enable); + } catch (Exception e) { + } + } + + private int getLocationMode() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, UserHandle.USER_CURRENT); + } + + private void setLocationMode(int mode) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCATION_MODE, mode, UserHandle.USER_CURRENT); + } + + private boolean isBluetoothEnabled() { + if (mBluetoothAdapter == null) { + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } + try { + return mBluetoothAdapter.isEnabled(); + } catch (Exception e) { + return false; + } + } + + private void setBluetoothEnabled(boolean enable) { + if (mBluetoothAdapter == null) { + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } + try { + if (enable) mBluetoothAdapter.enable(); + else mBluetoothAdapter.disable(); + } catch (Exception e) { + } + } + + private boolean isSensorEnabled() { + if (mSensorPrivacyManager == null) { + mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService(Context.SENSOR_PRIVACY_SERVICE); + } + try { + return !mSensorPrivacyManager.isAllSensorPrivacyEnabled(); + } catch (Exception e) { + return false; + } + } + + private void setSensorEnabled(boolean enable) { + if (mSensorPrivacyManager == null) { + mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService(Context.SENSOR_PRIVACY_SERVICE); + } + try { + mSensorPrivacyManager.setAllSensorPrivacy(!enable); + } catch (Exception e) { + } + } + + private int getZenMode() { + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + try { + return mNotificationManager.getZenMode(); + } catch (Exception e) { + return -1; + } + } + + private void setZenMode(int mode) { + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + try { + mNotificationManager.setZenMode(mode, null, TAG); + } catch (Exception e) { + } + } + + private int getRingerModeInternal() { + if (mAudioManager == null) { + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + } + try { + return mAudioManager.getRingerModeInternal(); + } catch (Exception e) { + return -1; + } + } + + private void setRingerModeInternal(int mode) { + if (mAudioManager == null) { + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + } + try { + mAudioManager.setRingerModeInternal(mode); + } catch (Exception e) { + } + } + + private void enable() { + if (!ActivityManager.isSystemReady()) return; + + // Disable Wi-Fi + final boolean disableWifi = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_WIFI_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableWifi) { + mWifiState = isWifiEnabled(); + setWifiEnabled(false); + } + + // Disable Bluetooth + final boolean disableBluetooth = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_BLUETOOTH_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableBluetooth) { + mBluetoothState = isBluetoothEnabled(); + setBluetoothEnabled(false); + } + + // Disable Mobile Data + final boolean disableData = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_CELLULAR_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableData) { + mCellularState = getTelephonyManager().isDataEnabled(); + getTelephonyManager().setDataEnabled(false); + } + + // Disable Location + final boolean disableLocation = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_LOCATION_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableLocation) { + mLocationState = getLocationMode(); + setLocationMode(Settings.Secure.LOCATION_MODE_OFF); + } + + // Disable Sensors + final boolean disableSensors = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_SENSORS_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableSensors) { + setSensorEnabled(false); + } + + // Set Ringer mode (0: Off, 1: Vibrate, 2:DND: 3:Silent) + final int ringerMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_RINGER_MODE, 0, UserHandle.USER_CURRENT); + if (ringerMode != 0) { + mRingerState = getRingerModeInternal(); + mZenState = getZenMode(); + if (ringerMode == 1) { + setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE); + setZenMode(ZEN_MODE_OFF); + } else if (ringerMode == 2) { + setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL); + setZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS); + } else if (ringerMode == 3) { + setRingerModeInternal(AudioManager.RINGER_MODE_SILENT); + setZenMode(ZEN_MODE_OFF); + } + } + + showToast(mResources.getString(R.string.sleep_mode_enabled_toast), Toast.LENGTH_LONG); + addNotification(); + } + + private void disable() { + if (!ActivityManager.isSystemReady()) return; + + // Enable Wi-Fi + final boolean disableWifi = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_WIFI_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableWifi && mWifiState != isWifiEnabled()) { + setWifiEnabled(mWifiState); + } + + // Enable Bluetooth + final boolean disableBluetooth = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_BLUETOOTH_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableBluetooth && mBluetoothState != isBluetoothEnabled()) { + setBluetoothEnabled(mBluetoothState); + } + + // Enable Mobile Data + final boolean disableData = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_CELLULAR_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableData && mCellularState != getTelephonyManager().isDataEnabled()) { + getTelephonyManager().setDataEnabled(mCellularState); + } + + // Enable Location + final boolean disableLocation = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_LOCATION_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableLocation && mLocationState != getLocationMode()) { + setLocationMode(mLocationState); + } + + // Enable Sensors + final boolean disableSensors = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_SENSORS_TOGGLE, 1, UserHandle.USER_CURRENT) == 1; + if (disableSensors) { + setSensorEnabled(true); + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + if (!isSensorEnabled()) { + setSensorEnabled(true); + } + } + + // Set Ringer mode (0: Off, 1: Vibrate, 2:DND: 3:Silent) + final int ringerMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_RINGER_MODE, 0, UserHandle.USER_CURRENT); + if (ringerMode != 0 && (mRingerState != getRingerModeInternal() || + mZenState != getZenMode())) { + setRingerModeInternal(mRingerState); + setZenMode(mZenState); + } + + showToast(mResources.getString(R.string.sleep_mode_disabled_toast), Toast.LENGTH_LONG); + mNotificationManager.cancel(SLEEP_NOTIFICATION_ID); + } + + private void addNotification() { + Intent intent = new Intent(SLEEP_MODE_TURN_OFF); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + // Display a notification + Notification.Builder builder = new Notification.Builder(mContext, SystemNotificationChannels.SLEEP) + .setTicker(mResources.getString(R.string.sleep_mode_notification_title)) + .setContentTitle(mResources.getString(R.string.sleep_mode_notification_title)) + .setContentText(mResources.getString(R.string.sleep_mode_notification_content)) + .setSmallIcon(R.drawable.ic_sleep) + .setWhen(java.lang.System.currentTimeMillis()) + .setOngoing(true) + .setContentIntent(pendingIntent) + .setAutoCancel(false); + + Notification notification = builder.build(); + mNotificationManager.notify(SLEEP_NOTIFICATION_ID, notification); + } + + private void showToast(String msg, int duration) { + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + try { + if (mToast != null) mToast.cancel(); + mToast = Toast.makeText(mUiContext, msg, duration); + mToast.show(); + } catch (Exception e) { + } + } + }); + } + + private void setSleepMode(boolean enabled) { + if (mSleepModeEnabled == enabled) { + return; + } + + mSleepModeEnabled = enabled; + + if (mSleepModeEnabled) { + enable(); + } else { + disable(); + } + } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SLEEP_MODE_ENABLED), false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + update(); + } + + void update() { + final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.SLEEP_MODE_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + setSleepMode(enabled); + } + } + } + + public static int getCutoutType(Context context) { + final DisplayInfo info = new DisplayInfo(); + context.getDisplay().getDisplayInfo(info); + final DisplayCutout cutout = info.displayCutout; + if (cutout == null) { + if (DEBUG) Log.v(TAG, "noCutout"); + return NO_CUTOUT; + } + final Point displaySize = new Point(); + context.getDisplay().getRealSize(displaySize); + List cutOutBounds = cutout.getBoundingRects(); + if (cutOutBounds != null) { + for (Rect cutOutRect : cutOutBounds) { + if (DEBUG) Log.v(TAG, "cutout left= " + cutOutRect.left); + if (DEBUG) Log.v(TAG, "cutout right= " + cutOutRect.right); + if (cutOutRect.left == 0 && cutOutRect.right > 0) { //cutout is located on top left + if (DEBUG) Log.v(TAG, "cutout position= " + BOUNDS_POSITION_LEFT); + return BOUNDS_POSITION_LEFT; + } else if (cutOutRect.right == displaySize.x && (displaySize.x - cutOutRect.left) > 0) { //cutout is located on top right + if (DEBUG) Log.v(TAG, "cutout position= " + BOUNDS_POSITION_RIGHT); + return BOUNDS_POSITION_RIGHT; + } + } + } + return NO_CUTOUT; + } + + // Method to detect whether an overlay is enabled or not + public static boolean isThemeEnabled(String packageName) { + mOverlayService = new OverlayManager(); + try { + List infos = mOverlayService.getOverlayInfosForTarget("android", + UserHandle.myUserId()); + for (int i = 0, size = infos.size(); i < size; i++) { + if (infos.get(i).packageName.equals(packageName)) { + return infos.get(i).isEnabled(); + } + } + } catch (RemoteException e) { + e.printStackTrace(); + } + return false; + } + + public static class OverlayManager { + private final IOverlayManager mService; + + public OverlayManager() { + mService = IOverlayManager.Stub.asInterface( + ServiceManager.getService(Context.OVERLAY_SERVICE)); + } + + public void setEnabled(String pkg, boolean enabled, int userId) + throws RemoteException { + mService.setEnabled(pkg, enabled, userId); + } + + public List getOverlayInfosForTarget(String target, int userId) + throws RemoteException { + return mService.getOverlayInfosForTarget(target, userId); + } + } + + public static void restartSystemUi(Context context) { + new RestartSystemUiTask(context).execute(); + } + + public static void showSystemUiRestartDialog(Context context) { + new AlertDialog.Builder(context) + .setTitle(R.string.systemui_restart_title) + .setMessage(R.string.systemui_restart_message) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + restartSystemUi(context); + } + }) + .setNegativeButton(R.string.cancel, null) + .show(); + } + + private static class RestartSystemUiTask extends AsyncTask { + private Context mContext; + + public RestartSystemUiTask(Context context) { + super(); + mContext = context; + } + + @Override + protected Void doInBackground(Void... params) { + try { + ActivityManager am = + (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + IActivityManager ams = ActivityManager.getService(); + for (ActivityManager.RunningAppProcessInfo app: am.getRunningAppProcesses()) { + if ("com.android.systemui".equals(app.processName)) { + ams.killApplicationProcess(app.processName, app.uid); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } + + public static void showSettingsRestartDialog(Context context) { + new AlertDialog.Builder(context) + .setTitle(R.string.settings_restart_title) + .setMessage(R.string.settings_restart_message) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + restartSettings(context); + } + }) + .setNegativeButton(R.string.cancel, null) + .show(); + } + + public static void restartSettings(Context context) { + new restartSettingsTask(context).execute(); + } + + private static class restartSettingsTask extends AsyncTask { + private Context mContext; + + public restartSettingsTask(Context context) { + super(); + mContext = context; + } + + @Override + protected Void doInBackground(Void... params) { + try { + ActivityManager am = + (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + IActivityManager ams = ActivityManager.getService(); + for (ActivityManager.RunningAppProcessInfo app: am.getRunningAppProcesses()) { + if ("com.android.settings".equals(app.processName)) { + ams.killApplicationProcess(app.processName, app.uid); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } +} diff --git a/core/java/com/android/internal/util/cherish/FileUtils.java b/core/java/com/android/internal/util/cherish/FileUtils.java new file mode 100644 index 0000000000000..7a9e592c7d94e --- /dev/null +++ b/core/java/com/android/internal/util/cherish/FileUtils.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.internal.util.cherish; + +import android.annotation.Nullable; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public final class FileUtils { + private static final String TAG = "FileUtils"; + + private FileUtils() { + // This class is not supposed to be instantiated + } + + /** + * Reads the first line of text from the given file. + * Reference {@link BufferedReader#readLine()} for clarification on what a line is + * + * @return the read line contents, or null on failure + */ + @Nullable + public static String readOneLine(String fileName) { + String line = null; + BufferedReader reader = null; + + try { + reader = new BufferedReader(new FileReader(fileName), 512); + line = reader.readLine(); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for reading", e); + } catch (IOException e) { + Log.w(TAG, "Could not read from file " + fileName, e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return line; + } + + /** + * Writes the given value into the given file + * + * @return true on success, false on failure + */ + public static boolean writeLine(String fileName, String value) { + BufferedWriter writer = null; + + try { + writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(value); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for writing", e); + return false; + } catch (IOException e) { + Log.e(TAG, "Could not write to file " + fileName, e); + return false; + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return true; + } + + /** + * Checks whether the given file exists + * + * @return true if exists, false if not + */ + public static boolean fileExists(String fileName) { + final File file = new File(fileName); + return file.exists(); + } + + /** + * Checks whether the given file is readable + * + * @return true if readable, false if not + */ + public static boolean isFileReadable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canRead(); + } + + /** + * Checks whether the given file is writable + * + * @return true if writable, false if not + */ + public static boolean isFileWritable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canWrite(); + } + + /** + * Deletes an existing file + * + * @return true if the delete was successful, false if not + */ + public static boolean delete(String fileName) { + final File file = new File(fileName); + boolean ok = false; + try { + ok = file.delete(); + } catch (SecurityException e) { + Log.w(TAG, "SecurityException trying to delete " + fileName, e); + } + return ok; + } + + /** + * Renames an existing file + * + * @return true if the rename was successful, false if not + */ + public static boolean rename(String srcPath, String dstPath) { + final File srcFile = new File(srcPath); + final File dstFile = new File(dstPath); + boolean ok = false; + try { + ok = srcFile.renameTo(dstFile); + } catch (SecurityException e) { + Log.w(TAG, "SecurityException trying to rename " + srcPath + " to " + dstPath, e); + } catch (NullPointerException e) { + Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e); + } + return ok; + } +} diff --git a/core/java/com/android/internal/util/cherish/ImageHelper.java b/core/java/com/android/internal/util/cherish/ImageHelper.java new file mode 100644 index 0000000000000..59b5e098b226f --- /dev/null +++ b/core/java/com/android/internal/util/cherish/ImageHelper.java @@ -0,0 +1,369 @@ +/* +* Copyright (C) 2013 SlimRoms Project +* Copyright (C) 2015 TeamEos Project +* Copyright (C) 2015-2016 The DirtyUnicorns Project +* Copyright (C) 2019-2021 crDroid Android 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.internal.util.cherish; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.renderscript.Element; +import android.renderscript.Allocation; +import android.renderscript.ScriptIntrinsicBlur; +import android.renderscript.RenderScript; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; + +public class ImageHelper { + private static final int VECTOR_WIDTH = 512; + private static final int VECTOR_HEIGHT = 512; + + public static Drawable getColoredDrawable(Drawable d, int color) { + if (d == null) { + return null; + } + if (d instanceof VectorDrawable) { + d.setTint(color); + return d; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return new BitmapDrawable(grayscaleBitmap); + } + + public static Bitmap drawableToBitmap (Drawable drawable) { + if (drawable == null) { + return null; + } else if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + public static Bitmap getColoredBitmap(Drawable d, int color) { + if (d == null) { + return null; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return grayscaleBitmap; + } + + public static Bitmap toGrayscale(Bitmap bmpOriginal) { + int width, height; + height = bmpOriginal.getHeight(); + width = bmpOriginal.getWidth(); + try { + bmpOriginal = RGB565toARGB888(bmpOriginal); + } catch (Exception e) { + e.printStackTrace(); + } + + Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmpGrayscale); + Paint paint = new Paint(); + paint.setAntiAlias(true); + ColorMatrix cm = new ColorMatrix(); + final Rect rect = new Rect(0, 0, width, height); + cm.setSaturation(0); + + ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); + paint.setColorFilter(f); + c.drawBitmap(bmpOriginal, rect, rect, paint); + return bmpGrayscale; + } + + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static Drawable resize(Context context, Drawable image, int size) { + if (image == null || context == null) { + return null; + } + if (image instanceof VectorDrawable) { + return image; + } else { + int newSize = dpToPx(context, size); + Bitmap bitmap = ((BitmapDrawable) image).getBitmap(); + Bitmap scaledBitmap = Bitmap.createBitmap(newSize, newSize, Config.ARGB_8888); + + float ratioX = newSize / (float) bitmap.getWidth(); + float ratioY = newSize / (float) bitmap.getHeight(); + float middleX = newSize / 2.0f; + float middleY = newSize / 2.0f; + + final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + paint.setAntiAlias(true); + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, + middleY - bitmap.getHeight() / 2, paint); + return new BitmapDrawable(context.getResources(), scaledBitmap); + } + } + + public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + final int color = 0xff424242; + final Paint paint = new Paint(); + final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + final RectF rectF = new RectF(rect); + final float roundPx = 24; + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRoundRect(rectF, roundPx, roundPx, paint); + paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return output; + } + + public static Bitmap getCircleBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + Bitmap output = Bitmap.createBitmap(width, height, + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(shader); + + canvas.drawCircle(width/2, height/2, width/2, paint); + + return output; + } + + public static Drawable getVector(Resources res, int resId) { + return getVector(res, resId, 0, 0, false); + } + + public static Drawable getVector(Resources res, int resId, int width, int height) { + return getVector(res, resId, width, height, false); + } + + public static Drawable getVector(Resources res, int resId, boolean toBitmapDrawable) { + return getVector(res, resId, 0, 0, toBitmapDrawable); + } + + public static Drawable getVector(Resources res, int resId, int width, int height, + boolean toBitmapDrawable) { + if (width <= 0) { + width = VECTOR_WIDTH; + } + if (height <= 0) { + width = VECTOR_HEIGHT; + } + + VectorDrawable vectorDrawable = new VectorDrawable(); + vectorDrawable.setBounds(0, 0, width, height); + try { + XmlPullParser parser = res.getXml(resId); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + + if (type != XmlPullParser.START_TAG) { +// Log.e("ImageHelper VectorLoader", "No start tag found"); + } + + vectorDrawable.inflate(res, parser, attrs); + + if (!toBitmapDrawable) { + return vectorDrawable; + } + + return new BitmapDrawable(res, drawableToBitmap(vectorDrawable)); + } catch (Exception e) { +// Log.e("ImageHelper VectorLoader", "Error loading resource ID " + String.valueOf(resId) + " Try loading as a non vector"); + return null; + } + } + + /** + * @param context callers context + * @param uri Uri to handle + * @return A bitmap from the requested uri + * @throws IOException + * + * @Credit: StackOverflow + * http://stackoverflow.com/questions/35909008/pick-image + * -from-gallery-or-google-photos-failing + */ + public static Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException { + if (context == null || uri == null) { + return null; + } + ParcelFileDescriptor parcelFileDescriptor = + context.getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } + + /** + * @param storageDir Desired location in storage as a File + * @param fileName Name of bitmap file to store + * @param bitmap the bitmap to store + * @return the Uri of the bitmap + */ + public static Uri addBitmapToStorage(File storageDir, String fileName, Bitmap bitmap) { + if (storageDir == null || fileName == null || bitmap == null) { + return null; + } + File imageFile = new File(storageDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(imageFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + } catch (Exception e) { + return null; + } + return Uri.fromFile(imageFile); + } + + public static Bitmap getBlurredImage(Context context, Bitmap image) { + return getBlurredImage(context, image, 3.5f); + } + + public static Bitmap getBlurredImage(Context context, Bitmap image, float radius) { + try { + image = RGB565toARGB888(image); + } catch (Exception e) { + e.printStackTrace(); + } + + Bitmap bitmap = Bitmap.createBitmap( + image.getWidth(), image.getHeight(), + Bitmap.Config.ARGB_8888); + RenderScript renderScript = RenderScript.create(context); + Allocation blurInput = Allocation.createFromBitmap(renderScript, image); + Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap); + + ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, + Element.U8_4(renderScript)); + blur.setInput(blurInput); + blur.setRadius(radius); // radius must be 0 < r <= 25 + blur.forEach(blurOutput); + blurOutput.copyTo(bitmap); + renderScript.destroy(); + + return bitmap; + } + + public static Bitmap getGrayscaleBlurredImage(Context context, Bitmap image) { + return getGrayscaleBlurredImage(context, image, 3.5f); + } + + public static Bitmap getGrayscaleBlurredImage(Context context, Bitmap image, float radius) { + Bitmap finalImage = Bitmap.createBitmap( + image.getWidth(), image.getHeight(), + Bitmap.Config.ARGB_8888); + finalImage = toGrayscale(getBlurredImage(context, image, radius)); + return finalImage; + } + + private static Bitmap RGB565toARGB888(Bitmap img) throws Exception { + int numPixels = img.getWidth() * img.getHeight(); + int[] pixels = new int[numPixels]; + + //Get JPEG pixels. Each int is the color values for one pixel. + img.getPixels(pixels, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight()); + + //Create a Bitmap of the appropriate format. + Bitmap result = Bitmap.createBitmap(img.getWidth(), img.getHeight(), Bitmap.Config.ARGB_8888); + + //Set RGB pixels. + result.setPixels(pixels, 0, result.getWidth(), 0, 0, result.getWidth(), result.getHeight()); + return result; + } +} diff --git a/core/java/com/android/internal/util/cherish/PixelPropsUtils.java b/core/java/com/android/internal/util/cherish/PixelPropsUtils.java new file mode 100644 index 0000000000000..f5125c951d70e --- /dev/null +++ b/core/java/com/android/internal/util/cherish/PixelPropsUtils.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2020 The Pixel Experience Project + * 2021-2022 The CherishOS 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.internal.util.cherish; + +import android.app.Application; +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class PixelPropsUtils { + + private static final String TAG = PixelPropsUtils.class.getSimpleName(); + private static final String DEVICE = "ro.product.device"; + private static final boolean DEBUG = false; + + private static final Map propsToChange; + private static final Map propsToChangePixel5; + private static final Map propsToChangePixel7Pro; + private static final Map propsToChangePixelXL; + private static final Map propsToChangeROG1; + private static final Map propsToChangeXP5; + private static final Map propsToChangeOP8P; + private static final Map propsToChangeOP9P; + private static final Map propsToChangeMI11; + private static final Map> propsToKeep; + + private static final String[] packagesToChangePixel7Pro = { + "com.google.android.apps.wallpaper", + "com.google.android.apps.privacy.wildlife", + "com.google.android.apps.subscriptions.red", + "com.google.android.inputmethod.latin" + }; + + private static final String[] packagesToChangePixelXL = { + "com.samsung.accessory", + "com.samsung.accessory.fridaymgr", + "com.samsung.accessory.berrymgr", + "com.samsung.accessory.neobeanmgr", + "com.samsung.android.app.watchmanager", + "com.samsung.android.geargplugin", + "com.samsung.android.gearnplugin", + "com.samsung.android.modenplugin", + "com.samsung.android.neatplugin", + "com.samsung.android.waterplugin" + }; + + private static final String[] extraPackagesToChange = { + "com.android.chrome", + "com.android.vending", + "com.breel.wallpapers20", + "com.nhs.online.nhsonline" + }; + + private static final String[] packagesToKeep = { + "com.google.android.GoogleCamera", + "com.google.android.GoogleCamera.Cameight", + "com.google.android.GoogleCamera.Go", + "com.google.android.GoogleCamera.Urnyx", + "com.google.android.GoogleCameraAsp", + "com.google.android.GoogleCameraCVM", + "com.google.android.GoogleCameraEng", + "com.google.android.GoogleCameraEng2", + "com.google.android.GoogleCameraGood", + "com.google.android.MTCL83", + "com.google.android.UltraCVM", + "com.google.android.apps.cameralite", + "com.google.android.dialer", + "com.google.android.euicc", + "com.google.ar.core", + "com.google.android.youtube", + "com.google.android.apps.youtube.kids", + "com.google.android.apps.youtube.music", + "com.google.android.apps.recorder", + "com.google.android.apps.wearables.maestro.companion" + }; + + private static final String[] packagesToChangeROG1 = { + "com.dts.freefireth", + "com.dts.freefiremax", + "com.madfingergames.legends" + }; + + private static final String[] packagesToChangeXP5 = { + "com.activision.callofduty.shooter", + "com.tencent.tmgp.kr.codm", + "com.garena.game.codm", + "com.vng.codmvn" + }; + + private static final String[] packagesToChangeOP8P = { + "com.tencent.ig", + "com.pubg.imobile", + "com.pubg.krmobile", + "com.vng.pubgmobile", + "com.rekoo.pubgm", + "com.tencent.tmgp.pubgmhd", + "com.riotgames.league.wildrift", + "com.riotgames.league.wildrifttw", + "com.riotgames.league.wildriftvn", + "com.netease.lztgglobal" + }; + + private static final String[] packagesToChangeOP9P = { + "com.epicgames.fortnite", + "com.epicgames.portal" + }; + + private static final String[] packagesToChangeMI11 = { + "com.ea.gp.apexlegendsmobilefps", + "com.levelinfinite.hotta.gp", + "com.mobile.legends", + "com.tencent.tmgp.sgame" + }; + + // Codenames for currently supported Pixels by Google + private static final String[] pixelCodenames = { + "cheetah", + "panther", + "bluejay", + "oriole", + "raven", + "barbet", + "redfin", + "bramble", + "sunfish", + "coral", + "flame" + }; + + private static volatile boolean sIsGms = false; + private static volatile boolean sIsFinsky = false; + + static { + propsToKeep = new HashMap<>(); + propsToChange = new HashMap<>(); + propsToKeep.put("com.google.android.settings.intelligence", new ArrayList<>(Collections.singletonList("FINGERPRINT"))); + propsToChangePixel7Pro = new HashMap<>(); + propsToChangePixel7Pro.put("BRAND", "google"); + propsToChangePixel7Pro.put("MANUFACTURER", "Google"); + propsToChangePixel7Pro.put("DEVICE", "cheetah"); + propsToChangePixel7Pro.put("PRODUCT", "cheetah"); + propsToChangePixel7Pro.put("MODEL", "Pixel 7 Pro"); + propsToChangePixel7Pro.put("FINGERPRINT", "google/cheetah/cheetah:13/TD1A.221105.001/9104446:user/release-keys"); + propsToChangePixel5 = new HashMap<>(); + propsToChangePixel5.put("BRAND", "google"); + propsToChangePixel5.put("MANUFACTURER", "Google"); + propsToChangePixel5.put("DEVICE", "redfin"); + propsToChangePixel5.put("PRODUCT", "redfin"); + propsToChangePixel5.put("MODEL", "Pixel 5"); + propsToChangePixel5.put("FINGERPRINT", "google/redfin/redfin:13/TP1A.221105.002/9080065:user/release-keys"); + propsToChangePixelXL = new HashMap<>(); + propsToChangePixelXL.put("BRAND", "google"); + propsToChangePixelXL.put("MANUFACTURER", "Google"); + propsToChangePixelXL.put("DEVICE", "marlin"); + propsToChangePixelXL.put("PRODUCT", "marlin"); + propsToChangePixelXL.put("MODEL", "Pixel XL"); + propsToChangePixelXL.put("FINGERPRINT", "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys"); + propsToChangeROG1 = new HashMap<>(); + propsToChangeROG1.put("MODEL", "ASUS_Z01QD"); + propsToChangeROG1.put("MANUFACTURER", "asus"); + propsToChangeXP5 = new HashMap<>(); + propsToChangeXP5.put("MODEL", "SO-52A"); + propsToChangeOP8P = new HashMap<>(); + propsToChangeOP8P.put("MODEL", "IN2020"); + propsToChangeOP8P.put("MANUFACTURER", "OnePlus"); + propsToChangeOP9P = new HashMap<>(); + propsToChangeOP9P.put("BRAND", "OnePlus"); + propsToChangeOP9P.put("MANUFACTURER", "OnePlus"); + propsToChangeOP9P.put("DEVICE", "OnePlus9Pro"); + propsToChangeOP9P.put("PRODUCT", "OnePlus9Pro_EEA"); + propsToChangeOP9P.put("MODEL", "LE2123"); + propsToChangeMI11 = new HashMap<>(); + propsToChangeMI11.put("BRAND", "Xiaomi"); + propsToChangeMI11.put("MANUFACTURER", "Xiaomi"); + propsToChangeMI11.put("DEVICE", "star"); + propsToChangeMI11.put("PRODUCT", "star"); + propsToChangeMI11.put("MODEL", "M2102K1G"); + } + + public static void setProps(String packageName) { + if (packageName == null || packageName.isEmpty()) { + return; + } + if (Arrays.asList(packagesToKeep).contains(packageName)) { + return; + } + if (packageName.startsWith("com.google.") + || Arrays.asList(extraPackagesToChange).contains(packageName)) { + + boolean isPixelDevice = Arrays.asList(pixelCodenames).contains(SystemProperties.get(DEVICE)); + + if (packageName.equals("com.google.android.apps.photos")) { + if (SystemProperties.getBoolean("persist.sys.pixelprops.gphotos", true)) { + propsToChange.putAll(propsToChangePixelXL); + } else { + if (isPixelDevice) return; + propsToChange.putAll(propsToChangePixel5); + } + } else if (isPixelDevice) { + return; + } else if (packageName.equals("com.android.vending")) { + sIsFinsky = true; + return; + } else { + if (Arrays.asList(packagesToChangePixel7Pro).contains(packageName)) { + propsToChange.putAll(propsToChangePixel7Pro); + } else if (Arrays.asList(packagesToChangePixelXL).contains(packageName)) { + propsToChange.putAll(propsToChangePixelXL); + } else { + propsToChange.putAll(propsToChangePixel5); + } + } + + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChange.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + if (propsToKeep.containsKey(packageName) && propsToKeep.get(packageName).contains(key)) { + if (DEBUG) Log.d(TAG, "Not defining " + key + " prop for: " + packageName); + continue; + } + if (DEBUG) Log.d(TAG, "Defining " + key + " prop for: " + packageName); + setPropValue(key, value); + } + if (packageName.equals("com.google.android.gms")) { + final String processName = Application.getProcessName(); + if (processName.equals("com.google.android.gms.unstable")) { + sIsGms = true; + setPropValue("FINGERPRINT", "google/angler/angler:6.0/MDB08L/2343525:user/release-keys"); + setPropValue("MODEL", "angler"); + } + return; + } + // Set proper indexing fingerprint + if (packageName.equals("com.google.android.settings.intelligence")) { + setPropValue("FINGERPRINT", Build.VERSION.INCREMENTAL); + } + } else { + + if (!SystemProperties.getBoolean("persist.sys.pixelprops.games", false)) + return; + + if (Arrays.asList(packagesToChangeROG1).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeROG1.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeXP5).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeXP5.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeOP8P).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeOP8P.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeOP9P).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeOP9P.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeMI11).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeMI11.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } + } + } + + private static void setPropValue(String key, Object value) { + try { + if (DEBUG) Log.d(TAG, "Defining prop " + key + " to " + value.toString()); + Field field = Build.class.getDeclaredField(key); + field.setAccessible(true); + field.set(null, value); + field.setAccessible(false); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, "Failed to set prop " + key, e); + } + } + + private static boolean isCallerSafetyNet() { + return Arrays.stream(Thread.currentThread().getStackTrace()) + .anyMatch(elem -> elem.getClassName().contains("DroidGuard")); + } + + public static void onEngineGetCertificateChain() { + // Check stack for SafetyNet + if (sIsGms && isCallerSafetyNet()) { + throw new UnsupportedOperationException(); + } + + // Check stack for PlayIntegrity + if (sIsFinsky) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/java/com/android/internal/util/cherish/ThemeUtils.java b/core/java/com/android/internal/util/cherish/ThemeUtils.java new file mode 100644 index 0000000000000..56b1c23c2532d --- /dev/null +++ b/core/java/com/android/internal/util/cherish/ThemeUtils.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2014 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.internal.util.cherish; + +import static android.os.UserHandle.USER_SYSTEM; + +import android.util.PathParser; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ProviderInfo; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.content.res.Configuration; +import android.database.Cursor; +import android.graphics.Typeface; +import android.graphics.Path; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.PathShape; +import android.net.Uri; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class ThemeUtils { + + public static final String TAG = "ThemeUtils"; + + public static final String FONT_KEY = "android.theme.customization.font"; + public static final String ICON_SHAPE_KEY= "android.theme.customization.adaptive_icon_shape"; + + public static final Comparator OVERLAY_INFO_COMPARATOR = + Comparator.comparingInt(a -> a.priority); + + private Context mContext; + private IOverlayManager mOverlayManager; + private PackageManager pm; + private Resources overlayRes; + + public ThemeUtils(Context context) { + mContext = context; + mOverlayManager = IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)); + pm = context.getPackageManager(); + } + + public void setOverlayEnabled(String category, String packageName) { + final String currentPackageName = getOverlayInfos(category).stream() + .filter(info -> info.isEnabled()) + .map(info -> info.packageName) + .findFirst() + .orElse(null); + + try { + if ("android".equals(packageName)) { + mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); + } else { + mOverlayManager.setEnabledExclusiveInCategory(packageName, + USER_SYSTEM); + } + + writeSettings(category, packageName, "android".equals(packageName)); + + } catch (RemoteException e) { + } + } + + public void writeSettings(String category, String packageName, boolean disable) { + final String overlayPackageJson = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, USER_SYSTEM); + JSONObject object; + try { + if (overlayPackageJson == null) { + object = new JSONObject(); + } else { + object = new JSONObject(overlayPackageJson); + } + if (disable) { + if (object.has(category)) object.remove(category); + } else { + object.put(category, packageName); + } + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, + object.toString(), USER_SYSTEM); + } catch (JSONException e) { + Log.e(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); + } + } + + public List getOverlayPackagesForCategory(String category) { + return getOverlayPackagesForCategory(category, "android"); + } + + public List getOverlayPackagesForCategory(String category, String target) { + List overlays = new ArrayList<>(); + overlays.add("android"); + for (OverlayInfo info : getOverlayInfos(category, target)) { + if (category.equals(info.getCategory())) { + overlays.add(info.getPackageName()); + } + } + return overlays; + } + + public List getOverlayInfos(String category) { + return getOverlayInfos(category, "android"); + } + + public List getOverlayInfos(String category, String target) { + final List filteredInfos = new ArrayList<>(); + try { + List overlayInfos = mOverlayManager + .getOverlayInfosForTarget(target, USER_SYSTEM); + for (OverlayInfo overlayInfo : overlayInfos) { + if (category.equals(overlayInfo.category)) { + filteredInfos.add(overlayInfo); + } + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + filteredInfos.sort(OVERLAY_INFO_COMPARATOR); + return filteredInfos; + } + + public List getLabels(String category) { + List labels = new ArrayList<>(); + labels.add("Default"); + for (OverlayInfo info : getOverlayInfos(category)) { + if (category.equals(info.getCategory())) { + try { + labels.add(pm.getApplicationInfo(info.packageName, 0) + .loadLabel(pm).toString()); + } catch (PackageManager.NameNotFoundException e) { + labels.add(info.packageName); + } + } + } + return labels; + } + + public List getFonts() { + final List fontlist = new ArrayList<>(); + for (String overlayPackage : getOverlayPackagesForCategory(FONT_KEY)) { + try { + overlayRes = overlayPackage.equals("android") ? Resources.getSystem() + : pm.getResourcesForApplication(overlayPackage); + final String font = overlayRes.getString( + overlayRes.getIdentifier("config_bodyFontFamily", + "string", overlayPackage)); + fontlist.add(Typeface.create(font, Typeface.NORMAL)); + } catch (NameNotFoundException | NotFoundException e) { + // Do nothing + } + } + return fontlist; + } + + public List getShapeDrawables() { + final List shapelist = new ArrayList<>(); + for (String overlayPackage : getOverlayPackagesForCategory(ICON_SHAPE_KEY)) { + shapelist.add(createShapeDrawable(overlayPackage)); + } + return shapelist; + } + + public ShapeDrawable createShapeDrawable(String overlayPackage) { + try { + if (overlayPackage.equals("android")) { + overlayRes = Resources.getSystem(); + } else { + if (overlayPackage.equals("default")) overlayPackage = "android"; + overlayRes = pm.getResourcesForApplication(overlayPackage); + } + } catch (NameNotFoundException | NotFoundException e) { + // Do nothing + } + final String shape = overlayRes.getString( + overlayRes.getIdentifier("config_icon_mask", + "string", overlayPackage)); + Path path = TextUtils.isEmpty(shape) ? null : PathParser.createPathFromPathData(shape); + PathShape pathShape = new PathShape(path, 100f, 100f); + ShapeDrawable shapeDrawable = new ShapeDrawable(pathShape); + int mThumbSize = (int) (mContext.getResources().getDisplayMetrics().density * 72); + shapeDrawable.setIntrinsicHeight(mThumbSize); + shapeDrawable.setIntrinsicWidth(mThumbSize); + return shapeDrawable; + } + + public boolean isOverlayEnabled(String overlayPackage) { + try { + OverlayInfo info = mOverlayManager.getOverlayInfo(overlayPackage, USER_SYSTEM); + return info == null ? false : info.isEnabled(); + } catch (RemoteException e) { + e.printStackTrace(); + } + return false; + } + + public boolean isDefaultOverlay(String category) { + for (String overlayPackage : getOverlayPackagesForCategory(category)) { + try { + OverlayInfo info = mOverlayManager.getOverlayInfo(overlayPackage, USER_SYSTEM); + if (info != null && info.isEnabled()) { + return false; + } else { + continue; + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + return true; + } +} diff --git a/core/java/com/android/internal/util/cherish/ThemesUtils.java b/core/java/com/android/internal/util/cherish/ThemesUtils.java new file mode 100644 index 0000000000000..cb94f08c34086 --- /dev/null +++ b/core/java/com/android/internal/util/cherish/ThemesUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 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.internal.util.cherish; + +import static android.os.UserHandle.USER_SYSTEM; + +import android.app.UiModeManager; +import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Log; + +public class ThemesUtils { + + public static final String TAG = "ThemesUtils"; + + // Statusbar Signal icons + private static final String[] SIGNAL_BAR = { + "com.custom.systemui.signalbar_a", + "com.custom.systemui.signalbar_b", + "com.custom.systemui.signalbar_c", + "com.custom.systemui.signalbar_d", + "com.custom.systemui.signalbar_e", + "com.custom.systemui.signalbar_f", + "com.custom.systemui.signalbar_g", + "com.custom.systemui.signalbar_h", + "com.android.systemui.signalbar_j", + "com.custom.systemui.signalbar_aa", + "com.custom.systemui.signalbar_ab", + "com.custom.systemui.signalbar_ac", + "com.custom.systemui.signalbar_ad", + "com.custom.systemui.signalbar_ae", + "com.custom.systemui.signalbar_af", + "com.custom.systemui.signalbar_ag", + "com.custom.systemui.signalbar_ah", + "com.custom.systemui.signalbar_ai", + "com.custom.systemui.signalbar_aj", + "com.custom.systemui.signalbar_ak", + "com.custom.systemui.signalbar_al", + "com.custom.systemui.signalbar_am", + "com.custom.systemui.signalbar_an", + "com.custom.systemui.signalbar_ao", + "com.custom.systemui.signalbar_ap", + "com.custom.systemui.signalbar_aq", + "com.custom.systemui.signalbar_ar", + "com.custom.systemui.signalbar_as", + "com.custom.systemui.signalbar_at", + "com.custom.systemui.signalbar_au", + }; + + // Statusbar Wifi icons + private static final String[] WIFI_BAR = { + "com.custom.systemui.wifibar_a", + "com.custom.systemui.wifibar_b", + "com.custom.systemui.wifibar_c", + "com.custom.systemui.wifibar_d", + "com.custom.systemui.wifibar_e", + "com.custom.systemui.wifibar_f", + "com.custom.systemui.wifibar_g", + "com.custom.systemui.wifibar_h", + "com.android.systemui.wifibar_i", + "com.android.systemui.wifibar_j", + }; +} diff --git a/core/java/com/android/internal/util/cherish/cutout/CutoutFullscreenController.java b/core/java/com/android/internal/util/cherish/cutout/CutoutFullscreenController.java new file mode 100644 index 0000000000000..2f2c86e89d7e5 --- /dev/null +++ b/core/java/com/android/internal/util/cherish/cutout/CutoutFullscreenController.java @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2018 The LineageOS project + * Copyright (C) 2019 The PixelExperience 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.internal.util.cherish.cutout; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.text.TextUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import android.provider.Settings; + +public class CutoutFullscreenController { + private Set mApps = new HashSet<>(); + private Context mContext; + + private final boolean isAvailable; + + private static final String PKG_GOOGLE_DIALER = "com.google.android.dialer"; + + public CutoutFullscreenController(Context context) { + mContext = context; + final Resources resources = mContext.getResources(); + + final String displayCutout = resources.getString(com.android.internal.R.string.config_mainBuiltInDisplayCutout); + isAvailable = !TextUtils.isEmpty(displayCutout); + + if (!isAvailable) { + return; + } + + SettingsObserver observer = new SettingsObserver( + new Handler(Looper.getMainLooper())); + observer.observe(); + } + + public boolean isSupported() { + return isAvailable; + } + + public boolean shouldForceCutoutFullscreen(String packageName) { + return isSupported() && (mApps.contains(packageName) || packageName.equals(PKG_GOOGLE_DIALER)); + } + + public Set getApps() { + return mApps; + } + + public void addApp(String packageName) { + mApps.add(packageName); + Settings.System.putString(mContext.getContentResolver(), + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, String.join(",", mApps)); + } + + public void removeApp(String packageName) { + mApps.remove(packageName); + Settings.System.putString(mContext.getContentResolver(), + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, String.join(",", mApps)); + } + + public void setApps(Set apps) { + mApps = apps; + } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS), false, this, + UserHandle.USER_ALL); + + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + + String apps = Settings.System.getStringForUser(resolver, + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, + UserHandle.USER_CURRENT); + if (apps != null) { + setApps(new HashSet<>(Arrays.asList(apps.split(",")))); + } else { + setApps(new HashSet<>()); + } + } + } +} diff --git a/core/java/com/android/internal/util/cherish/udfps/UdfpsUtils.java b/core/java/com/android/internal/util/cherish/udfps/UdfpsUtils.java new file mode 100644 index 0000000000000..4bd9187a8bec6 --- /dev/null +++ b/core/java/com/android/internal/util/cherish/udfps/UdfpsUtils.java @@ -0,0 +1,29 @@ +/* +* Copyright (C) 2020 The Pixel Experience 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.internal.util.cherish.udfps; + +import android.content.Context; + +import com.android.internal.util.ArrayUtils; + +public class UdfpsUtils { + public static boolean hasUdfpsSupport(Context context) { + int[] udfpsProps = context.getResources().getIntArray( + com.android.internal.R.array.config_udfps_sensor_props); + + return !ArrayUtils.isEmpty(udfpsProps); + } +} diff --git a/core/java/com/android/internal/util/hwkeys/ActionConstants.java b/core/java/com/android/internal/util/hwkeys/ActionConstants.java new file mode 100644 index 0000000000000..1007b92b427e0 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/ActionConstants.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * ActionConstants.java: A helper class to assist Config.java with loading + * and assigning default feature configurations. Nested classes implement + * the static interface Defaults, which allows Settings and Config.java + * to handle configurations in a non-implementation specific way, allowing + * for more generalized code structures. + * + * Of strong importance is the ConfigMap pojo class. Current settings use + * a ActionPreference which sets a single action. Therefore, we must have a + * way to map individual actions to their associated buttons. ActionPreference + * key MUST match the tag associated with the target ConfigMap. + * + */ + +package com.android.internal.util.hwkeys; + +import java.util.HashMap; +import java.util.Map; + +import com.android.internal.util.hwkeys.ActionHandler.SystemAction; +import com.android.internal.util.hwkeys.Config.ActionConfig; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Bundle; +import android.provider.Settings; +import android.util.Log; +import android.util.TypedValue; + +public class ActionConstants { + public static interface Defaults { + public int getConfigType(); + public String getUri(); + public String getDefaultConfig(); + public int getMaxButtons(); + public Map getActionMap(); + public Bundle getConfigs(Context context); + } + + public static final String ACTION_DELIMITER = "|"; + public static final String EMPTY = "empty"; + public static final int HWKEYS = 1; + + private static final Hwkeys hwkeys = new Hwkeys(); + + public static Defaults getDefaults(int type) { + if (type == HWKEYS) { + return hwkeys; + } else { + return null; + } + } + + public static String dl(String s) { + return s + ACTION_DELIMITER; + } + + public static class Hwkeys implements Defaults { + public static final int HWKEY_MAX_BUTTONS = 7; + public static final String HWKEY_DEF_BUTTONS = "5"; + public static final String BACK_BUTTON_TAG = "hwkeys_button_back"; + public static final String HOME_BUTTON_TAG = "hwkeys_button_home"; + public static final String OVERVIEW_BUTTON_TAG = "hwkeys_button_overview"; + public static final String MENU_BUTTON_TAG = "hwkeys_button_menu"; + public static final String ASSIST_BUTTON_TAG = "hwkeys_button_assist"; + public static final String EXTRA1_BUTTON_TAG = "hwkeys_button_camera"; + public static final String EXTRA2_BUTTON_TAG = "hwkeys_button_extra"; + + public static final String BACK_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_back_single_tap"; + public static final String HOME_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_home_single_tap"; + public static final String OVERVIEW_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_overview_single_tap"; + public static final String MENU_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_menu_single_tap"; + public static final String ASSIST_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_assist_single_tap"; + + public static final String BACK_BUTTON_LONG_PRESS_TAG = "hwkeys_button_back_long_press"; + public static final String HOME_BUTTON_LONG_PRESS_TAG = "hwkeys_button_home_long_press"; + public static final String OVERVIEW_BUTTON_LONG_PRESS_TAG = "hwkeys_button_overview_long_press"; + public static final String MENU_BUTTON_LONG_PRESS_TAG = "hwkeys_button_menu_long_press"; + public static final String ASSIST_BUTTON_LONG_PRESS_TAG = "hwkeys_button_assist_long_press"; + + public static final String BACK_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_back_double_tap"; + public static final String HOME_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_home_double_tap"; + public static final String OVERVIEW_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_overview_double_tap"; + public static final String MENU_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_menu_double_tap"; + public static final String ASSIST_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_assist_double_tap"; + + private static final Map configMap = new HashMap(); + + static { + configMap.put(BACK_BUTTON_SINGLE_TAP_TAG, new ConfigMap(0, ActionConfig.PRIMARY)); + configMap.put(HOME_BUTTON_SINGLE_TAP_TAG, new ConfigMap(1, ActionConfig.PRIMARY)); + configMap.put(OVERVIEW_BUTTON_SINGLE_TAP_TAG, new ConfigMap(2, ActionConfig.PRIMARY)); + configMap.put(MENU_BUTTON_SINGLE_TAP_TAG, new ConfigMap(3, ActionConfig.PRIMARY)); + configMap.put(ASSIST_BUTTON_SINGLE_TAP_TAG, new ConfigMap(4, ActionConfig.PRIMARY)); + configMap.put(BACK_BUTTON_LONG_PRESS_TAG, new ConfigMap(0, ActionConfig.SECOND)); + configMap.put(HOME_BUTTON_LONG_PRESS_TAG, new ConfigMap(1, ActionConfig.SECOND)); + configMap.put(OVERVIEW_BUTTON_LONG_PRESS_TAG, new ConfigMap(2, ActionConfig.SECOND)); + configMap.put(MENU_BUTTON_LONG_PRESS_TAG, new ConfigMap(3, ActionConfig.SECOND)); + configMap.put(ASSIST_BUTTON_LONG_PRESS_TAG, new ConfigMap(4, ActionConfig.SECOND)); + configMap.put(BACK_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(0, ActionConfig.THIRD)); + configMap.put(HOME_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(1, ActionConfig.THIRD)); + configMap.put(OVERVIEW_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(2, ActionConfig.THIRD)); + configMap.put(MENU_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(3, ActionConfig.THIRD)); + configMap.put(ASSIST_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(4, ActionConfig.THIRD)); + } + + public static final String HWKEYS_CONFIG_DEFAULT = + dl(HWKEY_DEF_BUTTONS) + + dl(BACK_BUTTON_TAG) + + dl(SystemAction.Back.mAction) + dl(SystemAction.Back.mLabelRes) + dl(EMPTY) // single tap (PRIMARY) + + dl(SystemAction.KillApp.mAction) + dl(SystemAction.KillApp.mLabelRes) + dl(EMPTY) // long press (SECOND) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // double tap (THIRD) + + + dl(HOME_BUTTON_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(OVERVIEW_BUTTON_TAG) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(MENU_BUTTON_TAG) + + dl(SystemAction.Menu.mAction) + dl(SystemAction.Menu.mLabelRes) + dl(EMPTY) + + dl(SystemAction.LastApp.mAction) + dl(SystemAction.LastApp.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(ASSIST_BUTTON_TAG) + + dl(SystemAction.GoogleAssistant.mAction) + dl(SystemAction.GoogleAssistant.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; + + @Override + public int getConfigType() { + return HWKEYS; + } + + @Override + public String getUri() { + //return Settings.System.HWKEY_BUTTON_ACTIONS; + return "hwkey_config"; + } + + @Override + public String getDefaultConfig() { + return HWKEYS_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return HWKEY_MAX_BUTTONS; + } + + @Override + public Map getActionMap() { + return configMap; + } + + @Override + public Bundle getConfigs(Context context) { + // TODO Auto-generated method stub + return null; + } + } + + private static Bundle loadConfigsFromMap(Context ctx, Map configMap) { + Bundle b = new Bundle(); + for (Map.Entry entry : configMap.entrySet()) { + ConfigHolder holder = entry.getValue(); + Object obj = ActionUtils.getValue(ctx, holder.name, holder.type, holder.format, + holder.pkg); + ActionUtils.putValue(holder.name, obj, holder.type, b); + } + return b; + } + + private static class ConfigHolder { + public String pkg; + public String name; + public String format; + public String type; + + public ConfigHolder(String pkg, String name, String type) { + this(pkg, name, null, type); + } + + public ConfigHolder(String pkg, String name, String format, String type) { + this.pkg = pkg; + this.name = name; + this.format = format; + this.type = type; + } + } + + public static class ConfigMap { + public int button = -1; + public int action = -1; + + public ConfigMap() { + }; + + public ConfigMap(int button, int action) { + this.button = button; + this.action = action; + } + } + +} diff --git a/core/java/com/android/internal/util/hwkeys/ActionHandler.java b/core/java/com/android/internal/util/hwkeys/ActionHandler.java new file mode 100644 index 0000000000000..b2cafc2319849 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/ActionHandler.java @@ -0,0 +1,1107 @@ +/* + * Copyright (C) 2015 The TeamEos Project + * Copyright (C) 2016-2017 The DirtyUnicorns 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. + * + * Launches actions assigned to widgets. Creates bundles of state based + * on the type of action passed. + * + */ + +package com.android.internal.util.hwkeys; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.ActivityTaskManager; +import android.app.IActivityManager; +import android.app.SearchManager; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.bluetooth.BluetoothAdapter; +import android.content.ActivityNotFoundException; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.hardware.input.InputManager; +import android.media.AudioManager; +import android.media.ToneGenerator; +import android.media.session.MediaSessionLegacyHelper; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.os.Vibrator; +import android.provider.Settings; +import android.service.wallpaper.WallpaperService; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.WindowManagerGlobal; +//import android.view.WindowManagerPolicyControl; +import android.view.inputmethod.InputMethodManager; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.hwkeys.Config.ActionConfig; + +public class ActionHandler { + public static String TAG = ActionHandler.class.getSimpleName(); + + public static final String SYSTEM_PREFIX = "task"; + public static final String SYSTEMUI = "com.android.systemui"; + + // track and filter special actions + public static final String TASK_IME = "task_ime"; + public static final String TASK_MEDIA = "task_media"; + public static final String TASK_SOUNDMODE = "task_soundmode"; + + public static final String SYSTEMUI_TASK_NO_ACTION = "task_no_action"; + public static final String SYSTEMUI_TASK_SETTINGS_PANEL = "task_settings_panel"; + public static final String SYSTEMUI_TASK_NOTIFICATION_PANEL = "task_notification_panel"; + public static final String SYSTEMUI_TASK_SCREENSHOT = "task_screenshot"; + public static final String SYSTEMUI_TASK_REGION_SCREENSHOT = "task_region_screenshot"; + public static final String SYSTEMUI_TASK_SCREENRECORD = "task_screenrecord"; + // public static final String SYSTEMUI_TASK_AUDIORECORD = + // "task_audiorecord"; + public static final String SYSTEMUI_TASK_EXPANDED_DESKTOP = "task_expanded_desktop"; + public static final String SYSTEMUI_TASK_SCREENOFF = "task_screenoff"; + public static final String SYSTEMUI_TASK_KILL_PROCESS = "task_killcurrent"; + public static final String SYSTEMUI_TASK_GOOGLE_ASSISTANT = "task_google_assistant"; + public static final String SYSTEMUI_TASK_POWER_MENU = "task_powermenu"; + public static final String SYSTEMUI_TASK_TORCH = "task_torch"; + public static final String SYSTEMUI_TASK_CAMERA = "task_camera"; + public static final String SYSTEMUI_TASK_BT = "task_bt"; + public static final String SYSTEMUI_TASK_WIFI = "task_wifi"; + public static final String SYSTEMUI_TASK_WIFIAP = "task_wifiap"; + public static final String SYSTEMUI_TASK_RECENTS = "task_recents"; + public static final String SYSTEMUI_TASK_LAST_APP = "task_last_app"; + public static final String SYSTEMUI_TASK_APP_SEARCH = "task_app_search"; + public static final String SYSTEMUI_TASK_MENU = "task_menu"; + public static final String SYSTEMUI_TASK_BACK = "task_back"; + public static final String SYSTEMUI_TASK_HOME = "task_home"; + public static final String SYSTEMUI_TASK_IME_SWITCHER = "task_ime_switcher"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_LEFT = "task_ime_navigation_left"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_RIGHT = "task_ime_navigation_right"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_UP = "task_ime_navigation_up"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_DOWN = "task_ime_navigation_down"; + public static final String SYSTEMUI_TASK_MEDIA_PREVIOUS = "task_media_previous"; + public static final String SYSTEMUI_TASK_MEDIA_NEXT = "task_media_next"; + public static final String SYSTEMUI_TASK_MEDIA_PLAY_PAUSE = "task_media_play_pause"; + public static final String SYSTEMUI_TASK_SOUNDMODE_VIB = "task_soundmode_vib"; + public static final String SYSTEMUI_TASK_SOUNDMODE_SILENT = "task_soundmode_silent"; + public static final String SYSTEMUI_TASK_SOUNDMODE_VIB_SILENT = "task_soundmode_vib_silent"; + public static final String SYSTEMUI_TASK_WAKE_DEVICE = "task_wake_device"; + public static final String SYSTEMUI_TASK_STOP_SCREENPINNING = "task_stop_screenpinning"; + public static final String SYSTEMUI_TASK_CLEAR_NOTIFICATIONS = "task_clear_notifications"; + public static final String SYSTEMUI_TASK_VOLUME_PANEL = "task_volume_panel"; + public static final String SYSTEMUI_TASK_SPLIT_SCREEN = "task_split_screen"; + public static final String SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT = "task_one_handed_mode_left"; + public static final String SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT = "task_one_handed_mode_right"; + public static final String SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH = "task_assistant_sound_search"; + + public static final String INTENT_SHOW_POWER_MENU = "action_handler_show_power_menu"; + public static final String INTENT_TOGGLE_SCREENRECORD = "action_handler_toggle_screenrecord"; + public static final String INTENT_SCREENSHOT = "action_take_screenshot"; + public static final String INTENT_REGION_SCREENSHOT = "action_take_region_screenshot"; + + // remove actions from here as they come back on deck + static final Set sDisabledActions = new HashSet(); + static { + sDisabledActions.add(SYSTEMUI_TASK_SCREENRECORD); + sDisabledActions.add(SYSTEMUI_TASK_EXPANDED_DESKTOP); + sDisabledActions.add(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT); + sDisabledActions.add(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT); + // we need to make this more reliable when the user tap the partial screenshot button + // quickly and more times + sDisabledActions.add(SYSTEMUI_TASK_REGION_SCREENSHOT); + sDisabledActions.add(SYSTEMUI_TASK_STOP_SCREENPINNING); + sDisabledActions.add(SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH); + sDisabledActions.add(SYSTEMUI_TASK_POWER_MENU); + } + + static enum SystemAction { + NoAction(SYSTEMUI_TASK_NO_ACTION, SYSTEMUI, "label_action_no_action", "ic_sysbar_no_action"), + SettingsPanel(SYSTEMUI_TASK_SETTINGS_PANEL, SYSTEMUI, "label_action_settings_panel", "ic_sysbar_settings_panel"), + NotificationPanel(SYSTEMUI_TASK_NOTIFICATION_PANEL, SYSTEMUI, "label_action_notification_panel", "ic_sysbar_notification_panel"), + Screenshot(SYSTEMUI_TASK_SCREENSHOT, SYSTEMUI, "label_action_screenshot", "ic_sysbar_screenshot"), + RegionScreenshot(SYSTEMUI_TASK_REGION_SCREENSHOT, SYSTEMUI, "label_action_region_screenshot", "ic_sysbar_region_screenshot"), + Screenrecord(SYSTEMUI_TASK_SCREENRECORD, SYSTEMUI, "label_action_screenrecord", "ic_sysbar_record_screen"), + ExpandedDesktop(SYSTEMUI_TASK_EXPANDED_DESKTOP, SYSTEMUI, "label_action_expanded_desktop", "ic_sysbar_expanded_desktop"), + ScreenOff(SYSTEMUI_TASK_SCREENOFF, SYSTEMUI, "label_action_screen_off", "ic_sysbar_screen_off"), + KillApp(SYSTEMUI_TASK_KILL_PROCESS, SYSTEMUI, "label_action_force_close_app", "ic_sysbar_killtask"), + GoogleAssistant(SYSTEMUI_TASK_GOOGLE_ASSISTANT, SYSTEMUI, "label_action_google_assistant", "ic_sysbar_google_assistant"), + InAppSearch(SYSTEMUI_TASK_APP_SEARCH, SYSTEMUI, "label_action_in_app_search", "ic_sysbar_in_app_search"), + Flashlight(SYSTEMUI_TASK_TORCH, SYSTEMUI, "label_action_flashlight", "ic_sysbar_torch"), + Bluetooth(SYSTEMUI_TASK_BT, SYSTEMUI, "label_action_bluetooth", "ic_sysbar_bt"), + WiFi(SYSTEMUI_TASK_WIFI, SYSTEMUI, "label_action_wifi", "ic_sysbar_wifi"), + Hotspot(SYSTEMUI_TASK_WIFIAP, SYSTEMUI, "label_action_hotspot", "ic_sysbar_hotspot"), + LastApp(SYSTEMUI_TASK_LAST_APP, SYSTEMUI, "label_action_last_app", "ic_sysbar_lastapp"), + Overview(SYSTEMUI_TASK_RECENTS, SYSTEMUI, "label_action_overview", "ic_sysbar_recent_hw"), + PowerMenu(SYSTEMUI_TASK_POWER_MENU, SYSTEMUI, "label_action_power_menu", "ic_sysbar_power_menu"), + Menu(SYSTEMUI_TASK_MENU, SYSTEMUI, "label_action_menu", "ic_sysbar_menu_hw"), + Back(SYSTEMUI_TASK_BACK, SYSTEMUI, "label_action_back", "ic_sysbar_back_hw"), + Home(SYSTEMUI_TASK_HOME, SYSTEMUI, "label_action_home", "ic_sysbar_home_hw"), + Ime(SYSTEMUI_TASK_IME_SWITCHER, SYSTEMUI, "label_action_ime_switcher", "ic_ime_switcher_smartbar"), + StopScreenPinning(SYSTEMUI_TASK_STOP_SCREENPINNING, SYSTEMUI, "label_action_stop_screenpinning", "ic_smartbar_screen_pinning_off"), + ImeArrowDown(SYSTEMUI_TASK_IME_NAVIGATION_DOWN, SYSTEMUI, "label_action_ime_down", "ic_sysbar_ime_down"), + ImeArrowLeft(SYSTEMUI_TASK_IME_NAVIGATION_LEFT, SYSTEMUI, "label_action_ime_left", "ic_sysbar_ime_left"), + ImeArrowRight(SYSTEMUI_TASK_IME_NAVIGATION_RIGHT, SYSTEMUI, "label_action_ime_right", "ic_sysbar_ime_right"), + ImeArrowUp(SYSTEMUI_TASK_IME_NAVIGATION_UP, SYSTEMUI, "label_action_ime_up", "ic_sysbar_ime_up"), + ClearNotifications(SYSTEMUI_TASK_CLEAR_NOTIFICATIONS, SYSTEMUI, "label_action_clear_notifications", "ic_sysbar_clear_notifications"), + VolumePanel(SYSTEMUI_TASK_VOLUME_PANEL, SYSTEMUI, "label_action_volume_panel", "ic_sysbar_volume_panel"), + SplitScreen(SYSTEMUI_TASK_SPLIT_SCREEN, SYSTEMUI, "label_action_split_screen", "ic_sysbar_docked_hw"), + OneHandedModeLeft(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT, SYSTEMUI, "label_action_one_handed_mode_left", "ic_sysbar_one_handed_mode_left"), + OneHandedModeRight(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT, SYSTEMUI, "label_action_one_handed_mode_right", "ic_sysbar_one_handed_mode_right"), + MediaArrowLeft(SYSTEMUI_TASK_MEDIA_PREVIOUS, SYSTEMUI, "label_action_media_left", "ic_skip_previous"), + MediaArrowRight(SYSTEMUI_TASK_MEDIA_NEXT, SYSTEMUI, "label_action_media_right", "ic_skip_next"), + AssistantSoundSearch(SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH, SYSTEMUI, "label_action_assistant_sound_search", "ic_assistant_sound_search"); + + String mAction; + String mResPackage; + String mLabelRes; + String mIconRes; + String mDarkIconRes; + + private SystemAction(String action, String resPackage, String labelRes, String iconRes) { + mAction = action; + mResPackage = resPackage; + mLabelRes = labelRes; + mIconRes = iconRes; + mDarkIconRes = iconRes + "_dark"; + } + + private ActionConfig create(Context ctx) { + return new ActionConfig(ctx, mAction); + } + } + + /* + * Enumerated system actions with label and drawable support + */ + static SystemAction[] systemActions = new SystemAction[] { + SystemAction.NoAction, SystemAction.SettingsPanel, + SystemAction.NotificationPanel, SystemAction.Screenshot, + SystemAction.ScreenOff, SystemAction.KillApp, + SystemAction.Flashlight, SystemAction.Bluetooth, + SystemAction.WiFi, SystemAction.Hotspot, + SystemAction.LastApp, SystemAction.PowerMenu, + SystemAction.Overview,SystemAction.Menu, + SystemAction.Back, SystemAction.GoogleAssistant, + SystemAction.Home, SystemAction.ExpandedDesktop, + SystemAction.Screenrecord, SystemAction.Ime, + SystemAction.StopScreenPinning, SystemAction.ImeArrowDown, + SystemAction.ImeArrowLeft, SystemAction.ImeArrowRight, + SystemAction.ImeArrowUp, SystemAction.InAppSearch, + SystemAction.VolumePanel, SystemAction.ClearNotifications, + SystemAction.SplitScreen, SystemAction.RegionScreenshot, + SystemAction.OneHandedModeLeft, SystemAction.OneHandedModeRight, + SystemAction.MediaArrowLeft, SystemAction.MediaArrowRight, + SystemAction.AssistantSoundSearch + }; + + public static class ActionIconResources { + Drawable[] mDrawables; + Drawable[] mDarkDrawables; + Map mIndexMap; + + public ActionIconResources(Resources res) { + mDrawables = new Drawable[systemActions.length]; + mDarkDrawables = new Drawable[systemActions.length]; + mIndexMap = new HashMap(); + for (int i = 0; i < systemActions.length; i++) { + mIndexMap.put(systemActions[i].mAction, i); + mDrawables[i] = ActionUtils.getDrawable(res, systemActions[i].mIconRes, + systemActions[i].mResPackage); + mDarkDrawables[i] = ActionUtils.getDrawable(res, systemActions[i].mDarkIconRes, + systemActions[i].mResPackage); + } + } + + public void updateResources(Resources res) { + for (int i = 0; i < mDrawables.length; i++) { + mDrawables[i] = ActionUtils.getDrawable(res, systemActions[i].mIconRes, + systemActions[i].mResPackage); + mDarkDrawables[i] = ActionUtils.getDrawable(res, systemActions[i].mDarkIconRes, + systemActions[i].mResPackage); + } + } + + public Drawable getActionDrawable(String action) { + return mDrawables[mIndexMap.get(action)]; + } + + public Drawable getDarkActionDrawable(String action) { + return mDarkDrawables[mIndexMap.get(action)]; + } + } + + /* + * Default list to display in an action picker + * Filter by device capabilities and actions used internally + * but we don't really want as assignable + */ + public static ArrayList getSystemActions(Context context) { + ArrayList bundle = new ArrayList(); + for (int i = 0; i < systemActions.length; i++) { + ActionConfig c = systemActions[i].create(context); + String action = c.getAction(); + if (sDisabledActions.contains(action)) { + continue; + } + if (TextUtils.equals(action, SYSTEMUI_TASK_STOP_SCREENPINNING) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_DOWN) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_LEFT) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_RIGHT) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_UP) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_SWITCHER) + || TextUtils.equals(action, SYSTEMUI_TASK_MEDIA_PREVIOUS) + || TextUtils.equals(action, SYSTEMUI_TASK_MEDIA_NEXT)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_WIFIAP) + && !ActionUtils.deviceSupportsMobileData(context)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_BT) + && !ActionUtils.deviceSupportsBluetooth()) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_TORCH) + && !ActionUtils.deviceSupportsFlashLight(context)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_CAMERA) + && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_SCREENRECORD)) { + if (!ActionUtils.getBoolean(context, "config_enableScreenrecordChord", + ActionUtils.PACKAGE_ANDROID)) { + continue; + } + } + bundle.add(c); + } + Collections.sort(bundle); + return bundle; + } + + private static final class StatusBarHelper { + private static boolean isPreloaded = false; + private static IStatusBarService mService = null; + + private static IStatusBarService getStatusBarService() { + synchronized (StatusBarHelper.class) { + if (mService == null) { + try { + mService = IStatusBarService.Stub.asInterface( + ServiceManager.getService("statusbar")); + } catch (Exception e) { + } + } + return mService; + } + } + + private static void toggleFlashlight() { + IStatusBarService service = getStatusBarService(); + try { + service.toggleCameraFlash(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private static void toggleRecentsApps() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + sendCloseSystemWindows("recentapps"); + service.toggleRecentApps(); + } catch (RemoteException e) { + return; + } + isPreloaded = false; + } + } + + private static void cancelPreloadRecentApps() { + if (isPreloaded == false) + return; + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.cancelPreloadRecentApps(); + } catch (Exception e) { + return; + } + } + isPreloaded = false; + } + + private static void preloadRecentApps() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.preloadRecentApps(); + } catch (RemoteException e) { + isPreloaded = false; + return; + } + isPreloaded = true; + } + } + + private static void expandNotificationPanel() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandNotificationsPanel(); + } catch (RemoteException e) { + } + } + } + + private static void expandSettingsPanel() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandSettingsPanel(null); + } catch (RemoteException e) { + } + } + } + + private static void fireGoogleAssistant() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.startAssist(new Bundle()); + } catch (RemoteException e) { + } + } + } + + private static void splitScreen() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleSplitScreen(); + } catch (RemoteException e) { + } + } + } +/* + private static void fireIntentAfterKeyguard(Intent intent) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.showCustomIntentAfterKeyguard(intent); + } catch (RemoteException e) { + } + } + } +*/ + private static void clearAllNotifications() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.onClearAllNotifications(ActivityManager.getCurrentUser()); + } catch (RemoteException e) { + } + } + } + } + + public static void toggleRecentApps() { + StatusBarHelper.toggleRecentsApps(); + } + + public static void cancelPreloadRecentApps() { + StatusBarHelper.cancelPreloadRecentApps(); + } + + public static void preloadRecentApps() { + StatusBarHelper.preloadRecentApps(); + } +/* + public static void performTaskFromKeyguard(Context ctx, String action) { + // null: throw it out + if (action == null) { + return; + } + // not a system action, should be intent + if (!action.startsWith(SYSTEM_PREFIX)) { + Intent intent = ActionUtils.getIntent(action); + if (intent == null) { + return; + } + StatusBarHelper.fireIntentAfterKeyguard(intent); + } else { + performTask(ctx, action); + } + } +*/ + + public static void performTask(Context context, String action) { + // null: throw it out + if (action == null) { + return; + } + if (sDisabledActions.contains(action)) { + return; + } + // not a system action, should be intent + if (!action.startsWith(SYSTEM_PREFIX)) { + Intent intent = ActionUtils.getIntent(action); + if (intent == null) { + return; + } + launchActivity(context, intent); + return; + } else if (action.equals(SYSTEMUI_TASK_NO_ACTION)) { + return; + } else if (action.equals(SYSTEMUI_TASK_KILL_PROCESS)) { + killProcess(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENSHOT)) { + sendCommandToWindowManager(new Intent(INTENT_SCREENSHOT)); + return; + } else if (action.equals(SYSTEMUI_TASK_REGION_SCREENSHOT)) { + sendCommandToWindowManager(new Intent(INTENT_REGION_SCREENSHOT)); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENRECORD)) { + sendCommandToWindowManager(new Intent(INTENT_TOGGLE_SCREENRECORD)); + return; + // } else if (action.equals(SYSTEMUI_TASK_AUDIORECORD)) { + // takeAudiorecord(); + } else if (action.equals(SYSTEMUI_TASK_EXPANDED_DESKTOP)) { + // toggleExpandedDesktop(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENOFF)) { + screenOff(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WAKE_DEVICE)) { + PowerManager powerManager = + (PowerManager) context.getSystemService(Context.POWER_SERVICE); + if (!powerManager.isScreenOn()) { + powerManager.wakeUp(SystemClock.uptimeMillis()); + } + return; + } else if (action.equals(SYSTEMUI_TASK_GOOGLE_ASSISTANT)) { + StatusBarHelper.fireGoogleAssistant(); + return; + } else if (action.equals(SYSTEMUI_TASK_POWER_MENU)) { + sendCommandToWindowManager(new Intent(INTENT_SHOW_POWER_MENU)); + return; + } else if (action.equals(SYSTEMUI_TASK_TORCH)) { + StatusBarHelper.toggleFlashlight(); + return; + } else if (action.equals(SYSTEMUI_TASK_CAMERA)) { + launchCamera(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WIFI)) { + toggleWifi(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WIFIAP)) { + toggleWifiAP(context); + return; + } else if (action.equals(SYSTEMUI_TASK_BT)) { + toggleBluetooth(); + return; + } else if (action.equals(SYSTEMUI_TASK_RECENTS)) { + toggleRecentApps(); + return; + } else if (action.equals(SYSTEMUI_TASK_LAST_APP)) { + ActionUtils.switchToLastApp(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SETTINGS_PANEL)) { + StatusBarHelper.expandSettingsPanel(); + return; + } else if (action.equals(SYSTEMUI_TASK_NOTIFICATION_PANEL)) { + StatusBarHelper.expandNotificationPanel(); + return; + } else if (action.equals(SYSTEMUI_TASK_APP_SEARCH)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_SEARCH); + return; + } else if (action.equals(SYSTEMUI_TASK_MENU)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_MENU); + return; + } else if (action.equals(SYSTEMUI_TASK_BACK)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_BACK); + return; + } else if (action.equals(SYSTEMUI_TASK_HOME)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_HOME); + return; +/* } else if (action.equals(SYSTEMUI_TASK_IME_SWITCHER)) { + ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)) + .showInputMethodPicker(true /* showAuxiliarySubtypes);*/ +// return; +/* } else if (action.equals(SYSTEMUI_TASK_STOP_SCREENPINNING)) { + turnOffLockTask(); + return; +*/ + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_RIGHT)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_RIGHT); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_UP)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_UP); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_DOWN)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_DOWN); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_LEFT)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_LEFT); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_PREVIOUS)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PREVIOUS, context); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_NEXT)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_NEXT, context); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_PLAY_PAUSE)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, context); + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_VIB)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null) { + vib.vibrate(50); + } + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() != AudioManager.RINGER_MODE_SILENT) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_VIB_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null) { + vib.vibrate(50); + } + } else if (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_CLEAR_NOTIFICATIONS)) { + StatusBarHelper.clearAllNotifications(); + return; + } else if (action.equals(SYSTEMUI_TASK_VOLUME_PANEL)) { + volumePanel(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SPLIT_SCREEN)) { + StatusBarHelper.splitScreen(); + return; + } else if (action.equals(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT)) { +// toggleOneHandedMode(context, "left"); + return; + } else if (action.equals(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT)) { +// toggleOneHandedMode(context, "right"); + return; + } else if (action.equals(SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH)) { + startAssistantSoundSearch(context); + return; + } + } + + public static boolean isActionKeyEvent(String action) { + if (action.equals(SYSTEMUI_TASK_HOME) + || action.equals(SYSTEMUI_TASK_BACK) +// || action.equals(SYSTEMUI_TASK_SEARCH) + || action.equals(SYSTEMUI_TASK_MENU) +// || action.equals(ActionConstants.ACTION_MENU_BIG) + || action.equals(SYSTEMUI_TASK_NO_ACTION)) { + return true; + } + return false; + } + + private static void launchActivity(Context context, Intent intent) { + try { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } catch (Exception e) { + Log.i(TAG, "Unable to launch activity " + e); + } + } + + private static void sendCloseSystemWindows(String reason) { + if (ActivityManagerNative.isSystemReady()) { + try { + ActivityManagerNative.getDefault().closeSystemDialogs(reason); + } catch (RemoteException e) { + } + } + } + +/* + private static void toggleExpandedDesktop(Context context) { + ContentResolver cr = context.getContentResolver(); + String newVal = ""; + String currentVal = Settings.Global.getString(cr, Settings.Global.POLICY_CONTROL); + if (currentVal == null) { + currentVal = newVal; + } + if ("".equals(currentVal)) { + newVal = "immersive.full=*"; + } + Settings.Global.putString(cr, Settings.Global.POLICY_CONTROL, newVal); + if (newVal.equals("")) { + WindowManagerPolicyControl.reloadFromSetting(context); + } + } +*/ + + private static void dispatchMediaKeyWithWakeLock(int keycode, Context context) { + if (ActivityManagerNative.isSystemReady()) { + KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + event = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + } + } + + private static void triggerVirtualKeypress(Context context, final int keyCode) { + final InputManager im = InputManager.getInstance(); + final long now = SystemClock.uptimeMillis(); + int downflags = 0; + + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT + || keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + downflags = KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE; + } else { + downflags = KeyEvent.FLAG_FROM_SYSTEM; + } + + final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, + keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + downflags, InputDevice.SOURCE_KEYBOARD); + final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP); + final Handler handler = new Handler(Looper.getMainLooper()); + + final Runnable downRunnable = new Runnable() { + @Override + public void run() { + im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }; + + final Runnable upRunnable = new Runnable() { + @Override + public void run() { + im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }; + + handler.post(downRunnable); + handler.postDelayed(upRunnable, 10); + } + + private static void launchCamera(Context context) { + Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); + PackageManager pm = context.getPackageManager(); + final ResolveInfo mInfo = pm.resolveActivity(i, 0); + Intent intent = new Intent().setComponent(new ComponentName(mInfo.activityInfo.packageName, + mInfo.activityInfo.name)); + launchActivity(context, intent); + } + + private static void toggleWifi(Context context) { + WifiManager wfm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + wfm.setWifiEnabled(!wfm.isWifiEnabled()); + } + + private static void toggleBluetooth() { + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + boolean enabled = bluetoothAdapter.isEnabled(); + if (enabled) { + bluetoothAdapter.disable(); + } else { + bluetoothAdapter.enable(); + } + } + + private static void toggleWifiAP(Context context) { + final ContentResolver cr = context.getContentResolver(); + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final ConnectivityManager mConnectivityManager; + mConnectivityManager = (ConnectivityManager) context.getSystemService( + Context.CONNECTIVITY_SERVICE); + int state = wm.getWifiApState(); + boolean enabled = false; + switch (state) { + case WifiManager.WIFI_AP_STATE_ENABLING: + case WifiManager.WIFI_AP_STATE_ENABLED: + enabled = false; + break; + case WifiManager.WIFI_AP_STATE_DISABLING: + case WifiManager.WIFI_AP_STATE_DISABLED: + enabled = true; + break; + } + + // Turn on the Wifi AP + if (enabled) { + OnStartTetheringCallback callback = new OnStartTetheringCallback(); + mConnectivityManager.startTethering( + ConnectivityManager.TETHERING_WIFI, false, callback); + } else { + mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); + } + } + + static final class OnStartTetheringCallback extends + ConnectivityManager.OnStartTetheringCallback { + @Override + public void onTetheringStarted() {} + @Override + public void onTetheringFailed() { + // TODO: Show error. + } + } + + private static void sendCommandToWindowManager(Intent intent) { + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.sendCustomAction(intent); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private static boolean killProcess(Context context) { + if (context.checkCallingOrSelfPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) == PackageManager.PERMISSION_GRANTED + && !isLockTaskOn()) { + final int mUserId = ActivityManager.getCurrentUser(); + try { + return killForegroundAppInternal(context, mUserId); + } catch (RemoteException e) { + Log.e(TAG, "Could not kill foreground app"); + } + } + Log.d("ActionHandler", "Caller cannot kill processes, aborting"); + return false; + } + +/* + private static void killProcess(Context context) { + if (context.checkCallingOrSelfPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) == PackageManager.PERMISSION_GRANTED + && !isLockTaskOn()) { + try { + PackageManager packageManager = context.getPackageManager(); + final Intent intent = new Intent(Intent.ACTION_MAIN); + String defaultHomePackage = "com.android.launcher"; + intent.addCategory(Intent.CATEGORY_HOME); + final ResolveInfo res = packageManager.resolveActivity(intent, 0); + if (res.activityInfo != null + && !res.activityInfo.packageName.equals("android")) { + defaultHomePackage = res.activityInfo.packageName; + } + + // Use UsageStats to determine foreground app + UsageStatsManager usageStatsManager = (UsageStatsManager) + context.getSystemService(Context.USAGE_STATS_SERVICE); + long current = System.currentTimeMillis(); + long past = current - (1000 * 60 * 60); // uses snapshot of usage over past 60 minutes + + // Get the list, then sort it chronilogically so most recent usage is at start of list + List recentApps = usageStatsManager.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, past, current); + Collections.sort(recentApps, new Comparator() { + @Override + public int compare(UsageStats lhs, UsageStats rhs) { + long timeLHS = lhs.getLastTimeUsed(); + long timeRHS = rhs.getLastTimeUsed(); + if (timeLHS > timeRHS) { + return -1; + } else if (timeLHS < timeRHS) { + return 1; + } + return 0; + } + }); + + IActivityManager iam = ActivityManagerNative.getDefault(); + // this may not be needed due to !isLockTaskOn() in entry if + //if (am.getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE) return; + + // Look for most recent usagestat with lastevent == 1 and grab package name + // ...this seems to map to the UsageEvents.Event.MOVE_TO_FOREGROUND + String pkg = null; + for (int i = 0; i < recentApps.size(); i++) { + UsageStats mostRecent = recentApps.get(i); + if (mostRecent.mLastEvent == 1) { + pkg = mostRecent.mPackageName; + break; + } + } + + if (pkg != null && !pkg.equals("com.android.systemui") + && !pkg.equals(defaultHomePackage)) { + + // Restore home screen stack before killing the app + Intent home = new Intent(Intent.ACTION_MAIN, null); + home.addCategory(Intent.CATEGORY_HOME); + home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + context.startActivity(home); + + // Kill the app + iam.forceStopPackage(pkg, UserHandle.USER_CURRENT); + + // Remove killed app from Recents +/* final ActivityManager am = (ActivityManager) + context.getSystemService(Context.ACTIVITY_SERVICE); + final List recentTasks = + am.getRecentTasksForUser(ActivityManager.getMaxRecentTasksStatic(), + ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS + | ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS + | ActivityManager.RECENT_IGNORE_UNAVAILABLE + | ActivityManager.RECENT_INCLUDE_PROFILES, + UserHandle.CURRENT.getIdentifier()); + final int size = recentTasks.size(); + for (int i = 0; i < size; i++) { + ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i); + if (recentInfo.baseIntent.getComponent().getPackageName().equals(pkg)) { + int taskid = recentInfo.persistentId; + am.removeTask(taskid); + } + } +*/ +/* + String pkgName; + try { + pkgName = (String) packageManager.getApplicationLabel( + packageManager.getApplicationInfo(pkg, PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException e) { + // Just use pkg if issues getting appName + pkgName = pkg; + } + + Resources systemUIRes = ActionUtils.getResourcesForPackage(context, ActionUtils.PACKAGE_SYSTEMUI); + int ident = systemUIRes.getIdentifier("app_killed_message", ActionUtils.STRING, ActionUtils.PACKAGE_SYSTEMUI); + String toastMsg = systemUIRes.getString(ident, pkgName); + Context ctx = getPackageContext(context, ActionUtils.PACKAGE_SYSTEMUI); + Toast.makeText(ctx != null ? ctx : context, toastMsg, Toast.LENGTH_SHORT).show(); + return; + } else { + // make a "didnt kill anything" toast? + return; + } + } catch (RemoteException remoteException) { + Log.d("ActionHandler", "Caller cannot kill processes, aborting"); + } + } else { + Log.d("ActionHandler", "Caller cannot kill processes, aborting"); + } + } +*/ + + private static boolean killForegroundAppInternal(Context context, int userId) + throws RemoteException { + final String packageName = getForegroundTaskPackageName(context, userId); + String appName; + if (packageName == null) { + return false; + } + final IActivityManager am = ActivityManagerNative.getDefault(); + try { + ApplicationInfo app = context.getPackageManager().getApplicationInfo(packageName, 0); + appName = context.getPackageManager().getApplicationLabel(app).toString(); + } catch (Exception e) { + appName = ""; + } + am.forceStopPackage(packageName, UserHandle.USER_CURRENT); + Resources systemUIRes = ActionUtils.getResourcesForPackage(context, ActionUtils.PACKAGE_SYSTEMUI); + int ident = systemUIRes.getIdentifier("app_killed_message", ActionUtils.STRING, ActionUtils.PACKAGE_SYSTEMUI); + String toastMsg = systemUIRes.getString(ident,appName); + Context ctx = getPackageContext(context, ActionUtils.PACKAGE_SYSTEMUI); + Toast.makeText(ctx != null ? ctx : context, toastMsg, Toast.LENGTH_SHORT).show(); + return true; + } + + private static String getForegroundTaskPackageName(Context context, int userId) + throws RemoteException { + final String defaultHomePackage = resolveCurrentLauncherPackage(context, userId); + final ActivityTaskManager.RootTaskInfo focusedStack = ActivityTaskManager.getService() + .getFocusedRootTaskInfo(); + + if (focusedStack == null || focusedStack.topActivity == null) { + return null; + } + + final String packageName = focusedStack.topActivity.getPackageName(); + if (!packageName.equals(defaultHomePackage) + && !packageName.equals(SYSTEMUI)) { + return packageName; + } + + return null; + } + + private static String resolveCurrentLauncherPackage(Context context, int userId) { + final Intent launcherIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME); + final PackageManager pm = context.getPackageManager(); + final ResolveInfo launcherInfo = pm.resolveActivityAsUser(launcherIntent, 0, userId); + + if (launcherInfo.activityInfo != null && + !launcherInfo.activityInfo.packageName.equals("android")) { + return launcherInfo.activityInfo.packageName; + } + + return null; + } + + public static Context getPackageContext(Context context, String packageName) { + Context pkgContext = null; + if (context.getPackageName().equals(packageName)) { + pkgContext = context; + } else { + try { + pkgContext = context.createPackageContext(packageName, + Context.CONTEXT_IGNORE_SECURITY + | Context.CONTEXT_INCLUDE_CODE); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + } + return pkgContext; + } + + private static boolean isPackageLiveWalls(Context ctx, String pkg) { + if (ctx == null || pkg == null) { + return false; + } + List liveWallsList = ctx.getPackageManager().queryIntentServices( + new Intent(WallpaperService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + if (liveWallsList == null) { + return false; + } + for (ResolveInfo info : liveWallsList) { + if (info.serviceInfo != null) { + String packageName = info.serviceInfo.packageName; + if (TextUtils.equals(pkg, packageName)) { + return true; + } + } + } + return false; + } + + private static void screenOff(Context context) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + pm.goToSleep(SystemClock.uptimeMillis()); + } + +/* public static void turnOffLockTask() { + try { + ActivityManagerNative.getDefault().stopLockTaskMode(); + } catch (Exception e) { + } + } +*/ + + public static boolean isLockTaskOn() { + try { + return ActivityManagerNative.getDefault().isInLockTaskMode(); + } catch (Exception e) { + } + return false; + } + + public static void volumePanel(Context context) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + am.adjustVolume(AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); + } + +/* + private static void toggleOneHandedMode(Context context, String direction) { + String str = Settings.Global.getString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE); + + if (TextUtils.isEmpty(str)) + Settings.Global.putString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE, direction); + else + Settings.Global.putString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE, ""); + } + */ + + public static void startAssistantSoundSearch(Context context) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setAction("com.google.android.googlequicksearchbox.MUSIC_SEARCH"); + context.startActivity(intent); + } +} diff --git a/core/java/com/android/internal/util/hwkeys/ActionHolder.java b/core/java/com/android/internal/util/hwkeys/ActionHolder.java new file mode 100644 index 0000000000000..07221cefe6efd --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/ActionHolder.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * Widgets may implement this interface to interact with action configurations + * + */ + +package com.android.internal.util.hwkeys; + +import com.android.internal.util.hwkeys.ActionConstants.ConfigMap; +import com.android.internal.util.hwkeys.ActionConstants.Defaults; +import com.android.internal.util.hwkeys.Config.ActionConfig; +import com.android.internal.util.hwkeys.Config.ButtonConfig; + +public interface ActionHolder { + public String getTag(); + public void setTag(String tag); + public Defaults getDefaults(); + public void setDefaults(Defaults defaults); + public ConfigMap getConfigMap(); + public void setConfigMap(ConfigMap map); + public ButtonConfig getButtonConfig(); + public void setButtonConfig(ButtonConfig button); + public ActionConfig getActionConfig(); + public void setActionConfig(ActionConfig action); + public ButtonConfig getDefaultButtonConfig(); + public void setDefaultButtonConfig(ButtonConfig button); + public ActionConfig getDefaultActionConfig(); + public void setDefaultActionConfig(ActionConfig action); +} diff --git a/core/java/com/android/internal/util/hwkeys/ActionUtils.java b/core/java/com/android/internal/util/hwkeys/ActionUtils.java new file mode 100644 index 0000000000000..7658a230bc7be --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/ActionUtils.java @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2014 The TeamEos 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. + * + * Helper functions mostly for device configuration and some utilities + * including a fun ViewGroup crawler and dpi conversion + * + */ + +package com.android.internal.util.hwkeys; + +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.NotificationManager; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +//import android.content.res.ThemeConfig; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.media.AudioManager; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.Uri; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.Vibrator; +import android.provider.MediaStore; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.util.Log; +import android.view.Display; +import android.view.IWindowManager; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.widget.Toast; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.util.hwkeys.ActionConstants.Defaults; +import com.android.internal.util.hwkeys.Config.ActionConfig; +import com.android.internal.util.hwkeys.Config.ButtonConfig; + +import com.android.internal.statusbar.IStatusBarService; +import static android.content.Context.NOTIFICATION_SERVICE; +import static android.content.Context.VIBRATOR_SERVICE; + +public final class ActionUtils { + public static final String ANDROIDNS = "http://schemas.android.com/apk/res/android"; + public static final String PACKAGE_SYSTEMUI = "com.android.systemui"; + public static final String PACKAGE_ANDROID = "android"; + public static final String FORMAT_NONE = "none"; + public static final String FORMAT_FLOAT = "float"; + + public static final String ID = "id"; + public static final String DIMEN = "dimen"; + public static final String DIMEN_PIXEL = "dimen_pixel"; + public static final String FLOAT = "float"; + public static final String INT = "integer"; + public static final String DRAWABLE = "drawable"; + public static final String COLOR = "color"; + public static final String BOOL = "bool"; + public static final String STRING = "string"; + public static final String ANIM = "anim"; + public static final String INTENT_SCREENSHOT = "action_take_screenshot"; + public static final String INTENT_REGION_SCREENSHOT = "action_take_region_screenshot"; + + private static final String TAG = ActionUtils.class.getSimpleName(); + + private static IStatusBarService mStatusBarService = null; + + private static IStatusBarService getStatusBarService() { + synchronized (ActionUtils.class) { + if (mStatusBarService == null) { + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService("statusbar")); + } + return mStatusBarService; + } + } + + // 10 inch tablets + public static boolean isXLargeScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + // 7 inch "phablets" i.e. grouper + public static boolean isLargeScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + // normal phones + public static boolean isNormalScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_NORMAL; + } + + public static boolean isLandscape(Context context) { + return Configuration.ORIENTATION_LANDSCAPE + == context.getResources().getConfiguration().orientation; + } + + public static boolean navigationBarCanMove() { + return Resources.getSystem().getConfiguration().smallestScreenWidthDp < 600; + } + + public static boolean hasNavbarByDefault(Context context) { + boolean needsNav = context.getResources().getBoolean( + com.android.internal.R.bool.config_showNavigationBar); + String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + needsNav = false; + } else if ("0".equals(navBarOverride)) { + needsNav = true; + } + return needsNav; + } + + public static boolean isHWKeysSupported(Context context) { + return getInt(context, "config_deviceHardwareKeys", PACKAGE_ANDROID) != 64; + } + + public static boolean deviceSupportsLte(Context ctx) { + final TelephonyManager tm = (TelephonyManager) + ctx.getSystemService(Context.TELEPHONY_SERVICE); + return (tm.getLteOnCdmaMode(tm.getSubscriptionId()) == PhoneConstants.LTE_ON_CDMA_TRUE); +// || tm.getLteOnGsmMode() != 0; +// return tm.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE; + } + + public static boolean deviceSupportsDdsSupported(Context context) { + TelephonyManager tm = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + return tm.isMultiSimEnabled() + && tm.getMultiSimConfiguration() == TelephonyManager.MultiSimVariants.DSDA; + } + + public static boolean deviceSupportsMobileData(Context ctx) { + ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService( + Context.CONNECTIVITY_SERVICE); + Network[] networks = cm.getAllNetworks(); + for (int i = 0; i < networks.length; i++) { + NetworkCapabilities caps = cm.getNetworkCapabilities(networks[i]); + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return true; + } + } + return false; + } + + public static boolean deviceSupportsBluetooth() { + return BluetoothAdapter.getDefaultAdapter() != null; + } + + public static boolean deviceSupportsNfc(Context context) { + PackageManager packageManager = context.getPackageManager(); + return packageManager.hasSystemFeature(PackageManager.FEATURE_NFC); + } + + public static boolean deviceSupportsFlashLight(Context context) { + CameraManager cameraManager = (CameraManager) context.getSystemService( + Context.CAMERA_SERVICE); + try { + String[] ids = cameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null + && flashAvailable + && lensFacing != null + && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + return true; + } + } + } catch (CameraAccessException | AssertionError e) { + // Ignore + } + return false; + } + + public static boolean deviceSupportsCompass(Context context) { + SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + return sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null + && sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; + } + + public static boolean deviceSupportsDoze(Context context) { + String name = (String) getValue(context, "config_dozeComponent", + STRING, PACKAGE_ANDROID); + return !TextUtils.isEmpty(name); + } + + // Launch camera + public static void launchCamera(Context context) { + Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivity(intent); + } + + // Launch voice search + public static void launchVoiceSearch(Context context) { + Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + /** + * This method converts dp unit to equivalent pixels, depending on device + * density. + * + * @param dp A value in dp (density independent pixels) unit. Which we need + * to convert into pixels + * @param context Context to get resources and device specific display + * metrics + * @return A float value to represent px equivalent to dp depending on + * device density + */ + public static float convertDpToPixel(float dp, Context context) { + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + float px = dp * (metrics.densityDpi / 160f); + return px; + } + + public static int ConvertDpToPixelAsInt(float dp, Context context) { + float px = convertDpToPixel(dp, context); + if (px < 1) + px = 1; + return Math.round(px); + } + + public static int ConvertDpToPixelAsInt(int dp, Context context) { + float px = convertDpToPixel((float) dp, context); + if (px < 1) + px = 1; + return Math.round(px); + } + + /** + * This method converts device specific pixels to density independent + * pixels. + * + * @param px A value in px (pixels) unit. Which we need to convert into db + * @param context Context to get resources and device specific display + * metrics + * @return A float value to represent dp equivalent to px value + */ + public static float convertPixelsToDp(float px, Context context) { + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + float dp = px / (metrics.densityDpi / 160f); + return dp; + } + + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + + /* utility to iterate a viewgroup and return a list of child views */ + public static ArrayList getAllChildren(View v) { + + if (!(v instanceof ViewGroup)) { + ArrayList viewArrayList = new ArrayList(); + viewArrayList.add(v); + return viewArrayList; + } + + ArrayList result = new ArrayList(); + + ViewGroup vg = (ViewGroup) v; + for (int i = 0; i < vg.getChildCount(); i++) { + + View child = vg.getChildAt(i); + + ArrayList viewArrayList = new ArrayList(); + viewArrayList.add(v); + viewArrayList.addAll(getAllChildren(child)); + + result.addAll(viewArrayList); + } + return result; + } + + /* utility to iterate a viewgroup and return a list of child views of type */ + public static ArrayList getAllChildren(View root, Class returnType) { + if (!(root instanceof ViewGroup)) { + ArrayList viewArrayList = new ArrayList(); + try { + viewArrayList.add(returnType.cast(root)); + } catch (Exception e) { + // handle all exceptions the same and silently fail + } + return viewArrayList; + } + ArrayList result = new ArrayList(); + ViewGroup vg = (ViewGroup) root; + for (int i = 0; i < vg.getChildCount(); i++) { + View child = vg.getChildAt(i); + ArrayList viewArrayList = new ArrayList(); + try { + viewArrayList.add(returnType.cast(root)); + } catch (Exception e) { + // handle all exceptions the same and silently fail + } + viewArrayList.addAll(getAllChildren(child, returnType)); + result.addAll(viewArrayList); + } + return result; + } + + public static void resolveAndUpdateButtonActions(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return; + } + boolean configChanged = false; + final PackageManager pm = ctx.getPackageManager(); + ArrayList configs = Config.getConfig(ctx, defaults); + ArrayList buttonsToChange = new ArrayList(); + buttonsToChange.addAll(configs); + for (int h = 0; h < configs.size(); h++) { + ButtonConfig button = configs.get(h); + for (int i = 0; i < 3; i++) { + ActionConfig action = button.getActionConfig(i); + final String task = action.getAction(); + if (task.startsWith(ActionHandler.SYSTEM_PREFIX)) { + continue; + } + String resolvedName = getFriendlyNameForUri(ctx, task); + if (resolvedName == null || TextUtils.equals(resolvedName, task)) { + // if resolved name is null or the full raw intent string is + // returned, we were unable to resolve + configChanged = true; + ActionConfig newAction = new ActionConfig(ctx, + ActionHandler.SYSTEMUI_TASK_NO_ACTION, action.getIconUri()); + ButtonConfig newButton = buttonsToChange.get(h); + newButton.setActionConfig(newAction, i); + buttonsToChange.remove(h); + buttonsToChange.add(h, newButton); + } + } + } + if (configChanged) { + Config.setConfig(ctx, defaults, buttonsToChange); + } + } + + public static Intent getIntent(String uri) { + if (uri == null || uri.startsWith(ActionHandler.SYSTEM_PREFIX)) { + return null; + } + + Intent intent = null; + try { + intent = Intent.parseUri(uri, 0); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return intent; + } + + public static Object getValue(Context context, String resName, String resType, String pkg) { + return getValue(context, resName, resType, null, pkg); + } + + public static Object getValue(Context context, String resName, String resType, String format, + String pkg) { + Resources res = getResourcesForPackage(context, pkg); + String tmp; + if (resType.equals(DIMEN_PIXEL)) { + tmp = DIMEN; + } else { + tmp = resType; + } + int id = res.getIdentifier(resName, tmp, pkg); + if (format != null) { // standard res + TypedValue typedVal = new TypedValue(); + res.getValue(id, typedVal, true); + if (format.equals(FORMAT_FLOAT)) { + return Float.valueOf(typedVal.getFloat()); + } + } else { // typed values + if (resType.equals(ID)) { + return Integer.valueOf(id); + } else if (resType.equals(DIMEN)) { + return Float.valueOf(res.getDimension(id)); + } else if (resType.equals(DIMEN_PIXEL)) { + return Integer.valueOf(res.getDimensionPixelSize(id)); + } else if (resType.equals(FLOAT)) { + return Float.valueOf(res.getFloat(id)); + } else if (resType.equals(INT)) { + return Integer.valueOf(res.getInteger(id)); + } else if (resType.equals(COLOR)) { + int rawColor = res.getColor(id); + return Integer.valueOf(Color.argb(Color.alpha(rawColor), Color.red(rawColor), + Color.green(rawColor), Color.blue(rawColor))); + } else if (resType.equals(BOOL)) { + return Boolean.valueOf(res.getBoolean(id)); + } else if (resType.equals(STRING)) { + return String.valueOf(res.getString(id)); + } else if (resType.equals(DRAWABLE)) { + return getDrawable(context, resName, pkg); + } + } + return null; + } + + public static void putValue(String key, Object val, String type, Bundle b) { + if (type.equals(ID) || type.equals(DIMEN_PIXEL) || type.equals(INT) || type.equals(COLOR)) { + b.putInt(key, (Integer) val); + } else if (type.equals(FLOAT) || type.equals(DIMEN)) { + b.putFloat(key, (Float) val); + } else if (type.equals(BOOL)) { + b.putBoolean(key, (Boolean) val); + } else if (type.equals(STRING)) { + b.putString(key, (String) val); + } + } + + public static int getIdentifier(Context context, String resName, String resType, String pkg) { + try { + Resources res = context.getPackageManager() + .getResourcesForApplication(pkg); + int ident = res.getIdentifier(resName, resType, pkg); + return ident; + } catch (Exception e) { + return -1; + } + } + + public static String getString(Context context, String resName, String pkg) { + return (String) getValue(context, resName, STRING, null, pkg); + } + + public static boolean getBoolean(Context context, String resName, String pkg) { + return (Boolean) getValue(context, resName, BOOL, null, pkg); + } + + public static int getInt(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, INT, null, pkg); + } + + public static int getColor(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, COLOR, null, pkg); + } + + public static int getId(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, ID, null, pkg); + } + + public static float getDimen(Context context, String resName, String pkg) { + return (Float) getValue(context, resName, DIMEN, null, pkg); + } + + public static int getDimenPixelSize(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, DIMEN_PIXEL, null, pkg); + } + + public static Drawable getDrawable(Context context, String drawableName, String pkg) { + return getDrawable(getResourcesForPackage(context, pkg), drawableName, pkg); + } + + public static Drawable getDrawable(Context context, Uri uri) { + //set inputs here so we can clean up them in the finally + InputStream inputStream = null; + + try { + //get the inputstream + inputStream = context.getContentResolver().openInputStream(uri); + + //get available bitmapfactory options + BitmapFactory.Options options = new BitmapFactory.Options(); + //query the bitmap to decode the stream but don't allocate pixels in memory yet + options.inJustDecodeBounds = true; + //decode the bitmap with calculated bounds + Bitmap b1 = BitmapFactory.decodeStream(inputStream, null, options); + //get raw height and width of the bitmap + int rawHeight = options.outHeight; + int rawWidth = options.outWidth; + + //check if the bitmap is big and we need to scale the quality to take less memory + options.inSampleSize = calculateInSampleSize(options, rawHeight, rawWidth); + + //We need to close and load again the inputstream to avoid null + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + inputStream = context.getContentResolver().openInputStream(uri); + + //decode the stream again, with the calculated SampleSize option, + //and allocate the memory. Also add some metrics options to take a proper density + options.inJustDecodeBounds = false; + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + options.inScreenDensity = metrics.densityDpi; + options.inTargetDensity = metrics.densityDpi; + options.inDensity = DisplayMetrics.DENSITY_DEFAULT; + b1 = BitmapFactory.decodeStream(inputStream, null, options); + return new BitmapDrawable(context.getResources(), b1); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + //clean up the system resources + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + //Automate the quality scaling process + public static int calculateInSampleSize(BitmapFactory.Options options, int height, int width) { + //set default inSampleSize scale factor (no scaling) + int inSampleSize = 1; + + //if img size is in 257-512 range, sample scale factor will be 4x + if (height > 256 || width > 256) { + inSampleSize = 4; + return inSampleSize; + //if img size is in 129-256 range, sample scale factor will be 2x + } else if (height > 128 || width > 128) { + inSampleSize = 2; + return inSampleSize; + } + //if img size is in 0-128 range, no need to scale it + return inSampleSize; + } + + /** + * Screen images based on desired dimensions before fully decoding + * + *@param ctx Calling context + *@param uri Image uri + *@param maxWidth maximum allowed image width + *@param maxHeight maximum allowed image height + */ + public static boolean isBitmapAllowedSize(Context ctx, Uri uri, int maxWidth, int maxHeight) { + InputStream inputStream = null; + try { + inputStream = ctx.getContentResolver().openInputStream(uri); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(inputStream, null, options); + if (options.outWidth <= maxWidth && options.outHeight <= maxHeight) { + return true; + } + } catch (Exception e) { + return false; + } finally { + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + return false; + } + + public static Drawable getDrawableFromComponent(PackageManager pm, String activity) { + Drawable d = null; + try { + Intent intent = Intent.parseUri(activity, 0); + ActivityInfo info = intent.resolveActivityInfo(pm, + PackageManager.GET_ACTIVITIES); + if (info != null) { + d = info.loadIcon(pm); + } + } catch (Exception e) { + e.printStackTrace(); + } + return d; + } + + public static String getFriendlyActivityName(PackageManager pm, Intent intent, + boolean labelOnly) { + ActivityInfo ai = intent.resolveActivityInfo(pm, PackageManager.GET_ACTIVITIES); + String friendlyName = null; + if (ai != null) { + friendlyName = ai.loadLabel(pm).toString(); + if (friendlyName == null && !labelOnly) { + friendlyName = ai.name; + } + } + return friendlyName != null || labelOnly ? friendlyName : intent.toUri(0); + } + + public static String getFriendlyShortcutName(PackageManager pm, Intent intent) { + String activityName = getFriendlyActivityName(pm, intent, true); + String name = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + + if (activityName != null && name != null) { + return activityName + ": " + name; + } + return name != null ? name : intent.toUri(0); + } + + public static String getFriendlyNameForUri(Context ctx, String uri) { + if (uri == null) { + return null; + } + if (uri.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(uri)) { + return getString(ctx, ActionHandler.systemActions[i].mLabelRes, + ActionHandler.systemActions[i].mResPackage); + } + } + } else { + try { + Intent intent = Intent.parseUri(uri, 0); + if (Intent.ACTION_MAIN.equals(intent.getAction())) { + return getFriendlyActivityName(ctx.getPackageManager(), intent, false); + } + return getFriendlyShortcutName(ctx.getPackageManager(), intent); + } catch (URISyntaxException e) { + } + } + return uri; + } + + /** + * + * @param Target package resources + * @param drawableName + * @param Target package name + * @return the drawable if found, otherwise fall back to a green android guy + */ + public static Drawable getDrawable(Resources res, String drawableName, String pkg) { + try { + int resId = res.getIdentifier(drawableName, DRAWABLE, pkg); + Drawable icon = ImageHelper.getVector(res, resId, false); + if (icon == null) { + icon = res.getDrawable(resId); + } + return icon; + } catch (Exception e) { + return res.getDrawable( + com.android.internal.R.drawable.sym_def_app_icon); + } + } + + /** + * + * @param Target package resources + * @param drawableName + * @param Target package name + * @return the drawable if found, null otherwise. Useful for testing if a drawable is found + * in a theme overlay + */ + private static Drawable getMaybeNullDrawable(Resources res, String drawableName, String pkg) { + try { + int resId = res.getIdentifier(drawableName, DRAWABLE, pkg); + Drawable icon = ImageHelper.getVector(res, resId, false); + if (icon == null) { + icon = res.getDrawable(resId); + } + return icon; + } catch (Exception e) { + return null; + } + } + + public static Resources getResourcesForPackage(Context ctx, String pkg) { + try { + Resources res = ctx.getPackageManager() + .getResourcesForApplication(pkg); + return res; + } catch (Exception e) { + return ctx.getResources(); + } + } + + /** + * + * @param Context of the calling package + * @param the action we want a drawable for + * @return if a system action drawable is requested, we try to get the drawable + * from any current navigation overlay. if no overlay is found, get it + * from SystemUI. Return a component drawable if not a system action + */ + public static Drawable getDrawableForAction(Context context, String action) { + Drawable d = null; + + // this null check is probably no-op but let's be safe anyways + if (action == null || context == null) { + return d; + } + if (action.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(action)) { + // should always be SystemUI + String packageName = ActionHandler.systemActions[i].mResPackage; + Resources res = getResourcesForPackage(context, packageName); + String iconName = ActionHandler.systemActions[i].mIconRes; + d = getNavbarThemedDrawable(context, res, iconName); + if (d == null) { + d = getDrawable(res, iconName, packageName); + } + } + } + } else { + d = getDrawableFromComponent(context.getPackageManager(), action); + } + return d; + } + + /** + * + * @param calling package context, usually Settings for the custom action list adapter + * @param target package resources, usually SystemUI + * @param drawableName + * @return a navigation bar overlay themed action drawable if available, otherwise + * return drawable from SystemUI resources + */ + public static Drawable getNavbarThemedDrawable(Context context, Resources defRes, + String drawableName) { + if (context == null || defRes == null || drawableName == null) + return null; + + // TODO: turn on cmte support when it comes back + return getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); +/* + ThemeConfig themeConfig = context.getResources().getConfiguration().themeConfig; + + Drawable d = null; + if (themeConfig != null) { + try { + final String navbarThemePkgName = themeConfig.getOverlayForNavBar(); + final String sysuiThemePkgName = themeConfig.getOverlayForStatusBar(); + // Check if the same theme is applied for systemui, if so we can skip this + if (navbarThemePkgName != null && !navbarThemePkgName.equals(sysuiThemePkgName)) { + // Navbar theme and SystemUI (statusbar) theme packages are different + // But we can't assume navbar package has our drawable, so try navbar theme + // package first. If we fail, try the systemui (statusbar) package + // if we still fail, fall back to default package resource + Resources res = context.getPackageManager().getThemedResourcesForApplication( + PACKAGE_SYSTEMUI, navbarThemePkgName); + d = getMaybeNullDrawable(res, drawableName, PACKAGE_SYSTEMUI); + if (d == null) { + // drawable not found in overlay, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } else { + // no navbar overlay present, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } catch (PackageManager.NameNotFoundException e) { + // error thrown (unlikely), get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } + if (d == null) { + // theme config likely null, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + return d; + */ + } + + // Screen off + public static void switchScreenOff(Context ctx) { + PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE); + if (pm!= null && pm.isScreenOn()) { + pm.goToSleep(SystemClock.uptimeMillis()); + } + } + + // Screen on + public static void switchScreenOn(Context context) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + if (pm == null) return; + pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK"); + } + + // Volume panel + public static void toggleVolumePanel(Context context) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + am.adjustVolume(AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); + } + + // Toggle flashlight + public static void toggleCameraFlash() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleCameraFlash(); + } catch (RemoteException e) { + // do nothing. + } + } + } + + // Clear notifications + public static void clearAllNotifications() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.onClearAllNotifications(ActivityManager.getCurrentUser()); + } catch (RemoteException e) { + // do nothing. + } + } + } + + // Toggle notifications panel + public static void toggleNotifications() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.togglePanel(); + } catch (RemoteException e) { + // do nothing. + } + } + } + + // Toggle qs panel + public static void toggleQsPanel() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleSettingsPanel(); + } catch (RemoteException e) { + // do nothing. + } + } + } + + // Cycle ringer modes + public static void toggleRingerModes (Context context) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + Vibrator mVibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE); + + switch (am.getRingerMode()) { + case AudioManager.RINGER_MODE_NORMAL: + if (mVibrator.hasVibrator()) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + } + break; + case AudioManager.RINGER_MODE_VIBRATE: + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + notificationManager.setInterruptionFilter( + NotificationManager.INTERRUPTION_FILTER_PRIORITY); + break; + case AudioManager.RINGER_MODE_SILENT: + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + break; + } + } + + // Switch to last app + public static void switchToLastApp(Context context) { + final ActivityManager am = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.RunningTaskInfo lastTask = getLastTask(context, am); + + if (lastTask != null) { + am.moveTaskToFront(lastTask.id, ActivityManager.MOVE_TASK_NO_USER_ACTION, + getAnimation(context).toBundle()); + } + } + + private static ActivityOptions getAnimation(Context context) { + return ActivityOptions.makeCustomAnimation(context, + com.android.internal.R.anim.custom_app_in, + com.android.internal.R.anim.custom_app_out); + } + + private static ActivityManager.RunningTaskInfo getLastTask(Context context, + final ActivityManager am) { + final List packageNames = getCurrentLauncherPackages(context); + final List tasks = am.getRunningTasks(5); + for (int i = 1; i < tasks.size(); i++) { + String packageName = tasks.get(i).topActivity.getPackageName(); + if (!packageName.equals(context.getPackageName()) + && !packageName.equals(PACKAGE_SYSTEMUI) + && !packageNames.contains(packageName)) { + return tasks.get(i); + } + } + return null; + } + + private static List getCurrentLauncherPackages(Context context) { + final PackageManager pm = context.getPackageManager(); + final List homeActivities = new ArrayList<>(); + pm.getHomeActivities(homeActivities); + final List packageNames = new ArrayList<>(); + for (ResolveInfo info : homeActivities) { + final String name = info.activityInfo.packageName; + if (!name.equals("com.android.settings")) { + packageNames.add(name); + } + } + return packageNames; + } + + // Trigger Hush Mute mode + public static void triggerHushMute(Context context) { + // We can't call AudioService#silenceRingerModeInternal from here, so this is a partial copy of it + int silenceRingerSetting = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_OFF, + UserHandle.USER_CURRENT); + + int ringerMode; + int toastText; + if (silenceRingerSetting == Settings.Secure.VOLUME_HUSH_VIBRATE) { + ringerMode = AudioManager.RINGER_MODE_VIBRATE; + toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate; + } else { + // VOLUME_HUSH_MUTE and VOLUME_HUSH_OFF + ringerMode = AudioManager.RINGER_MODE_SILENT; + toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent; + } + AudioManager audioMan = (AudioManager) + context.getSystemService(Context.AUDIO_SERVICE); + audioMan.setRingerModeInternal(ringerMode); + Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show(); + } +} diff --git a/core/java/com/android/internal/util/hwkeys/Config.java b/core/java/com/android/internal/util/hwkeys/Config.java new file mode 100644 index 0000000000000..f2799359178c4 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/Config.java @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * Config.java: A helper class for loading/setting feature button configurations + * to SettingsProvider. We start with defining a "action" in the nested + * ActionConfig class. A action holds a raw action string, a label for that action, + * and helper functions for loading an associated drawable for that action. + * + * The nested static class ButtonConfig is a convenience class that holds three + * ActionConfig objects. Those ActionConfig objects are labeled as "PRIMARY", + * "SECOND", and "THIRD", which will typically refer to a single tap, long press, + * and double tap, respectively. However, this is not always the case, thus the + * more generalized naming convention. + * + * ActionConfig and ButtonConfig implement the private Stringable interface to allow + * for easy loading and setting + * + */ +package com.android.internal.util.hwkeys; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.android.internal.util.hwkeys.ActionHandler; +import com.android.internal.util.hwkeys.ActionConstants.ConfigMap; +import com.android.internal.util.hwkeys.ActionConstants.Defaults; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; + +public class Config { + private interface Stringable { + public String toDelimitedString(); + public void fromList(Context ctx, List items); + } + + /** + * For use with action/button based features that don't require a defaut configuration + */ + public static ArrayList getConfig(Context ctx, String uri, + boolean fromSecureSettings) { + if (ctx == null || uri == null) { + return null; + } + String config; + if (fromSecureSettings) { + config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } else { + config = Settings.System.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } + if (TextUtils.isEmpty(config)) { + return null; + } + ArrayList items = new ArrayList(); + items.addAll(Arrays.asList(config.split("\\|"))); // split string into array elements + int numConfigs = Integer.parseInt(items.get(0)); // first element is always the number of + // ButtonConfigs to parse + items.remove(0); // remove button count for a clean list of buttons + + ArrayList buttonList = new ArrayList(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; // (0, 10), (10, 20)... + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); // initialize button from list + // elements + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static ArrayList getConfig(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return null; + } + String config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), defaults.getUri(), + UserHandle.USER_CURRENT); + if (TextUtils.isEmpty(config)) { + config = defaults.getDefaultConfig(); + } + + ArrayList items = new ArrayList(); + items.addAll(Arrays.asList(config.split("\\|"))); // split string into array elements + int numConfigs = Integer.parseInt(items.get(0)); // first element is always the number of ButtonConfigs to parse + items.remove(0); // remove button count for a clean list of buttons + + ArrayList buttonList = new ArrayList(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; // (0, 10), (10, 20)... + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); // initialize button from list elements + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static ArrayList getDefaultConfig(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return null; + } + String config = defaults.getDefaultConfig(); + ArrayList items = new ArrayList(); + items.addAll(Arrays.asList(config.split("\\|"))); + int numConfigs = Integer.parseInt(items.get(0)); + items.remove(0); + ArrayList buttonList = new ArrayList(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static void setConfig(Context ctx, Defaults defaults, ArrayList config) { + if (ctx == null || defaults == null || config == null) { + return; + } + int numConfigs = config.size(); + if (numConfigs <= 0) { + return; + } + StringBuilder b = new StringBuilder(); + b.append(String.valueOf(numConfigs)); + b.append(ActionConstants.ACTION_DELIMITER); // size of list is always first element + for (ButtonConfig button : config) { + b.append(button.toDelimitedString()); // this is just beautiful ;D + } + String s = b.toString(); + if (s.endsWith(ActionConstants.ACTION_DELIMITER)) { + s = removeLastChar(s); // trim final delimiter if need be + } + Settings.Secure.putStringForUser(ctx.getContentResolver(), defaults.getUri(), s, + UserHandle.USER_CURRENT); + } + + public static ButtonConfig getButtonConfigFromTag(ArrayList configs, String tag) { + if (configs == null || tag == null) { + return null; + } + ButtonConfig config = null; + for (ButtonConfig b : configs) { + if (TextUtils.equals(b.getTag(), tag)) { + config = b; + break; + } + } + return config; + } + + public static ArrayList replaceButtonAtPosition(ArrayList configs, + ButtonConfig button, ConfigMap map) { + if (configs == null || button == null || map == null) { + return null; + } + configs.remove(map.button); + configs.add(map.button, button); + return configs; + } + + public static String removeLastChar(String s) { + if (s == null || s.length() == 0) { + return s; + } + return s.substring(0, s.length() - 1); + } + + public static class ButtonConfig implements Stringable, Parcelable { + public static final int NUM_ELEMENTS = 10; + protected ActionConfig[] configs = new ActionConfig[3]; + private String tag = ActionConstants.EMPTY; + + // internal use only + private ButtonConfig() { + } + + public ButtonConfig(Context ctx) { + configs[ActionConfig.PRIMARY] = new ActionConfig(ctx); + configs[ActionConfig.SECOND] = new ActionConfig(ctx); + configs[ActionConfig.THIRD] = new ActionConfig(ctx); + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public boolean hasCustomIcon() { + return configs[ActionConfig.PRIMARY].hasCustomIcon(); + } + + public void clearCustomIconIconUri() { + configs[ActionConfig.PRIMARY].clearCustomIconIconUri(); + } + + public void setCustomIconUri(String type, String packageName, String iconName) { + configs[ActionConfig.PRIMARY].setCustomIconUri(type, packageName, iconName); + } + + public void setCustomImageUri(Uri uri) { + configs[ActionConfig.PRIMARY].setCustomImageUri(uri); + } + + public Drawable getDefaultIcon(Context ctx) { + return configs[ActionConfig.PRIMARY].getDefaultIcon(ctx); + } + + public Drawable getCurrentIcon(Context ctx) { + return configs[ActionConfig.PRIMARY].getCurrentIcon(ctx); + } + + public boolean isSystemAction() { + return configs[ActionConfig.PRIMARY].isSystemAction(); + } + + public String getSystemActionIconName() { + return configs[ActionConfig.PRIMARY].getSystemActionIconName(); + } + + public ActionConfig getActionConfig(int which) { + if (which < ActionConfig.PRIMARY || which > ActionConfig.THIRD) { + return null; + } + return configs[which]; + } + + public void setActionConfig(ActionConfig config, int which) { + if (which < ActionConfig.PRIMARY || which > ActionConfig.THIRD || config == null) { + return; + } + configs[which] = config; + } + + public static void setButton(Context ctx, ButtonConfig button, String uri, boolean isSecure) { + StringBuilder b = new StringBuilder(); + b.append(button.toDelimitedString()); // this is just beautiful ;D + String s = b.toString(); + if (s.endsWith(ActionConstants.ACTION_DELIMITER)) { + s = removeLastChar(s); // trim final delimiter if need be + } + + if (isSecure) { + Settings.Secure.putStringForUser(ctx.getContentResolver(), uri, s, + UserHandle.USER_CURRENT); + } else { + Settings.System.putStringForUser(ctx.getContentResolver(), uri, s, + UserHandle.USER_CURRENT); + } + } + + public static ButtonConfig getButton(Context ctx, String uri, boolean isSecure) { + if (ctx == null || TextUtils.isEmpty(uri)) { + return null; + } + String config; + if (isSecure) { + config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } else { + config = Settings.System.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } + if (TextUtils.isEmpty(config)) { + return new ButtonConfig(ctx); + } + ArrayList items = new ArrayList(); + items.addAll(Arrays.asList(config.split("\\|"))); + ButtonConfig buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(0, NUM_ELEMENTS)); // initialize button from + // list elements + return buttonConfig; + } + + @Override + public String toDelimitedString() { + return tag + ActionConstants.ACTION_DELIMITER + + configs[ActionConfig.PRIMARY].toDelimitedString() + + configs[ActionConfig.SECOND].toDelimitedString() + + configs[ActionConfig.THIRD].toDelimitedString(); + } + + @Override + public void fromList(Context ctx, List items) { + ArrayList buttons = new ArrayList(); + buttons.addAll(items); + tag = buttons.get(0); + + ActionConfig config = new ActionConfig(); + config.fromList(ctx, buttons.subList(1, 4)); + configs[ActionConfig.PRIMARY] = config; + + config = new ActionConfig(); + config.fromList(ctx, buttons.subList(4, 7)); + configs[ActionConfig.SECOND] = config; + + config = new ActionConfig(); + config.fromList(ctx, buttons.subList(7, 10)); + configs[ActionConfig.THIRD] = config; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(tag); + dest.writeParcelable(configs[ActionConfig.PRIMARY], flags); + dest.writeParcelable(configs[ActionConfig.SECOND], flags); + dest.writeParcelable(configs[ActionConfig.THIRD], flags); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public ButtonConfig createFromParcel(Parcel in) { + return new ButtonConfig(in); + } + + public ButtonConfig[] newArray(int size) { + return new ButtonConfig[size]; + } + }; + + private ButtonConfig(Parcel in) { + tag = in.readString(); + configs[ActionConfig.PRIMARY] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + configs[ActionConfig.SECOND] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + configs[ActionConfig.THIRD] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + } + } + + public static class ActionConfig implements Stringable, Comparable, Parcelable { + public static final int PRIMARY = 0; + public static final int SECOND = 1; + public static final int THIRD = 2; + + private String action = ActionHandler.SYSTEMUI_TASK_NO_ACTION; + private String label = ActionConstants.EMPTY; + private String iconUri = ActionConstants.EMPTY; + + // internal use only + private ActionConfig() { + } + + public static ActionConfig create(Context ctx) { + return new ActionConfig(ctx); + } + + public static ActionConfig create(Context ctx, String action) { + return new ActionConfig(ctx, action); + } + + public static ActionConfig create(Context ctx, String action, String iconUri) { + return new ActionConfig(ctx, action, iconUri); + } + + public ActionConfig(Context ctx) { + label = ActionUtils.getFriendlyNameForUri(ctx, action); + } + + public ActionConfig(Context ctx, String action) { + this.action = action; + label = ActionUtils.getFriendlyNameForUri(ctx, action); + } + + public ActionConfig(Context ctx, String action, String iconUri) { + this(ctx, action); + this.iconUri = iconUri; + } + + public String getAction() { + return action; + } + + public String getLabel() { + return label; + } + + public String getIconUri() { + if (!hasCustomIcon()) { + return action; + } else { + return iconUri; + } + } + + public boolean hasCustomIcon() { + return !TextUtils.equals(ActionConstants.EMPTY, iconUri); + } + + public void clearCustomIconIconUri() { + iconUri = ActionConstants.EMPTY; + } + + public boolean isSystemAction() { + return action.startsWith(ActionHandler.SYSTEM_PREFIX); + } + + public String getSystemActionIconName() { + if (action.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(action)) { + return ActionHandler.systemActions[i].mIconRes; + } + } + } + return null; + } + + public void setCustomImageUri(Uri uri) { + iconUri = "image$" + uri.toString(); + } + + public void setCustomIconUri(String type, String packageName, String iconName) { + StringBuilder b = new StringBuilder() + .append(type) + .append("$") + .append(packageName) + .append("$") + .append(iconName); + iconUri = b.toString(); + } + + public Drawable getDefaultIcon(Context ctx) { + return ActionUtils.getDrawableForAction(ctx, action); + } + + /** + * Returns custom icon (if exists) + * @param ctx app's context + * @return drawable when custom icon exists, null otherwise + */ + public Drawable getCurrentCustomIcon(Context ctx) { + if (hasCustomIcon()) { + List items = Arrays.asList(iconUri.split("\\$")); + String type = items.get(0); + if (type.equals("iconpack") && items.size() == 3) { + String packageName = items.get(1); + String iconName = items.get(2); + return ActionUtils.getDrawable(ctx, iconName, packageName); + } else if (type.equals("image") && items.size() == 2) { + String uri = items.get(1); + return ActionUtils.getDrawable(ctx, Uri.parse(uri)); + } + } + return null; + } + + public Drawable getCurrentIcon(Context ctx) { + + Drawable drawable = getCurrentCustomIcon(ctx); + + //If icon doesn't exist (or is not set) fallback to action one + if (drawable == null) { + drawable = ActionUtils.getDrawableForAction(ctx, action); + } + + return drawable; + + } + + public boolean hasNoAction() { + return TextUtils.equals(action, ActionHandler.SYSTEMUI_TASK_NO_ACTION) + || TextUtils.equals(action, ActionConstants.EMPTY); + } + + public boolean isActionRecents() { + return TextUtils.equals(action, ActionHandler.SYSTEMUI_TASK_RECENTS); + } + + @Override + public int compareTo(ActionConfig another) { + int result = label.toString().compareToIgnoreCase(another.label.toString()); + return result; + } + + @Override + public String toDelimitedString() { + return action + ActionConstants.ACTION_DELIMITER + + label + ActionConstants.ACTION_DELIMITER + + iconUri + ActionConstants.ACTION_DELIMITER; + } + + @Override + public void fromList(Context ctx, List items) { + ArrayList actionStrings = new ArrayList(); + actionStrings.addAll(items); + action = items.get(0); + label = ActionUtils.getFriendlyNameForUri(ctx, action); + iconUri = items.get(2); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(action); + dest.writeString(label); + dest.writeString(iconUri); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public ActionConfig createFromParcel(Parcel in) { + return new ActionConfig(in); + } + + public ActionConfig[] newArray(int size) { + return new ActionConfig[size]; + } + }; + + private ActionConfig(Parcel in) { + action = in.readString(); + label = in.readString(); + iconUri = in.readString(); + } + } +} diff --git a/core/java/com/android/internal/util/hwkeys/ImageHelper.java b/core/java/com/android/internal/util/hwkeys/ImageHelper.java new file mode 100644 index 0000000000000..0c802f44d09b0 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/ImageHelper.java @@ -0,0 +1,299 @@ +/* +* Copyright (C) 2013 SlimRoms Project +* Copyright (C) 2015 TeamEos Project +* Copyright (C) 2015-2016 The DirtyUnicorns 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.internal.util.hwkeys; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; + +public class ImageHelper { + private static final int VECTOR_WIDTH = 512; + private static final int VECTOR_HEIGHT = 512; + + public static Drawable getColoredDrawable(Drawable d, int color) { + if (d == null) { + return null; + } + if (d instanceof VectorDrawable) { + d.setTint(color); + return d; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return new BitmapDrawable(grayscaleBitmap); + } + + public static Bitmap drawableToBitmap (Drawable drawable) { + if (drawable == null) { + return null; + } else if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + public static Bitmap getColoredBitmap(Drawable d, int color) { + if (d == null) { + return null; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return grayscaleBitmap; + } + + private static Bitmap toGrayscale(Bitmap bmpOriginal) { + int width, height; + height = bmpOriginal.getHeight(); + width = bmpOriginal.getWidth(); + + Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmpGrayscale); + Paint paint = new Paint(); + paint.setAntiAlias(true); + ColorMatrix cm = new ColorMatrix(); + final Rect rect = new Rect(0, 0, width, height); + cm.setSaturation(0); + + ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); + paint.setColorFilter(f); + c.drawBitmap(bmpOriginal, rect, rect, paint); + return bmpGrayscale; + } + + public static Drawable resize(Context context, Drawable image, int size) { + if (image == null || context == null) { + return null; + } + if (image instanceof VectorDrawable) { + return image; + } else { + int newSize = ActionUtils.dpToPx(context, size); + Bitmap bitmap = ((BitmapDrawable) image).getBitmap(); + Bitmap scaledBitmap = Bitmap.createBitmap(newSize, newSize, Config.ARGB_8888); + + float ratioX = newSize / (float) bitmap.getWidth(); + float ratioY = newSize / (float) bitmap.getHeight(); + float middleX = newSize / 2.0f; + float middleY = newSize / 2.0f; + + final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + paint.setAntiAlias(true); + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, + middleY - bitmap.getHeight() / 2, paint); + return new BitmapDrawable(context.getResources(), scaledBitmap); + } + } + + public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + final int color = 0xff424242; + final Paint paint = new Paint(); + final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + final RectF rectF = new RectF(rect); + final float roundPx = 24; + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRoundRect(rectF, roundPx, roundPx, paint); + paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return output; + } + + public static Bitmap getCircleBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + Bitmap output = Bitmap.createBitmap(width, height, + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(shader); + + canvas.drawCircle(width/2, height/2, width/2, paint); + + return output; + } + + public static Drawable getVector(Resources res, int resId) { + return getVector(res, resId, 0, 0, false); + } + + public static Drawable getVector(Resources res, int resId, int width, int height) { + return getVector(res, resId, width, height, false); + } + + public static Drawable getVector(Resources res, int resId, boolean toBitmapDrawable) { + return getVector(res, resId, 0, 0, toBitmapDrawable); + } + + public static Drawable getVector(Resources res, int resId, int width, int height, + boolean toBitmapDrawable) { + if (width <= 0) { + width = VECTOR_WIDTH; + } + if (height <= 0) { + width = VECTOR_HEIGHT; + } + + VectorDrawable vectorDrawable = new VectorDrawable(); + vectorDrawable.setBounds(0, 0, width, height); + try { + XmlPullParser parser = res.getXml(resId); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + + if (type != XmlPullParser.START_TAG) { +// Log.e("ImageHelper VectorLoader", "No start tag found"); + } + + vectorDrawable.inflate(res, parser, attrs); + + if (!toBitmapDrawable) { + return vectorDrawable; + } + + return new BitmapDrawable(res, drawableToBitmap(vectorDrawable)); + } catch (Exception e) { +// Log.e("ImageHelper VectorLoader", "Error loading resource ID " + String.valueOf(resId) + " Try loading as a non vector"); + return null; + } + } + + /** + * @param context callers context + * @param uri Uri to handle + * @return A bitmap from the requested uri + * @throws IOException + * + * @Credit: StackOverflow + * http://stackoverflow.com/questions/35909008/pick-image + * -from-gallery-or-google-photos-failing + */ + public static Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException { + if (context == null || uri == null) { + return null; + } + ParcelFileDescriptor parcelFileDescriptor = + context.getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } + + /** + * @param storageDir Desired location in storage as a File + * @param fileName Name of bitmap file to store + * @param bitmap the bitmap to store + * @return the Uri of the bitmap + */ + public static Uri addBitmapToStorage(File storageDir, String fileName, Bitmap bitmap) { + if (storageDir == null || fileName == null || bitmap == null) { + return null; + } + File imageFile = new File(storageDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(imageFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + } catch (Exception e) { + return null; + } + return Uri.fromFile(imageFile); + } +} diff --git a/core/java/com/android/internal/util/hwkeys/PackageMonitor.java b/core/java/com/android/internal/util/hwkeys/PackageMonitor.java new file mode 100644 index 0000000000000..8b1c688b7c910 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/PackageMonitor.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015-2016 The TeamEos Project + * + * Author: Randall Rushing + * + * 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. + * + * A simplified package monitor class with easy-to-use callbacks when a + * state changes. We register the receiver on background thread but post events + * to the UI thread + */ + +package com.android.internal.util.hwkeys; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; + +import java.util.ArrayList; + +public class PackageMonitor extends com.android.internal.content.PackageMonitor { + private static final int MSG_PACKAGE_ADDED = 1; + private static final int MSG_PACKAGE_REMOVED = 2; + private static final int MSG_PACKAGE_CHANGED = 3; + + public static enum PackageState { + PACKAGE_REMOVED, + PACKAGE_ADDED, + PACKAGE_CHANGED + } + + public interface PackageChangedListener { + public void onPackageChanged(String pkg, PackageState state); + } + + private Handler mHandler; + + private ArrayList mListeners = new ArrayList(); + + public void register(Context context, Handler foreground) { + register(context, null, null, true); + + mHandler = new Handler(foreground.getLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PACKAGE_ADDED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, PackageState.PACKAGE_ADDED); + } + break; + case MSG_PACKAGE_REMOVED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, + PackageState.PACKAGE_REMOVED); + } + break; + case MSG_PACKAGE_CHANGED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, + PackageState.PACKAGE_CHANGED); + } + break; + } + } + }; + } + + public void addListener(PackageChangedListener listener) { + if (listener != null) { + mListeners.add(listener); + } + } + + public void removeListener(PackageChangedListener listener) { + if (listener != null) { + mListeners.remove(listener); + } + } + + /** + * Called when a package is really added (and not replaced). + */ + public void onPackageAdded(String packageName, int uid) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_ADDED, packageName); + mHandler.sendMessage(msg); + } + + /** + * Called when a package is really removed (and not replaced). + */ + public void onPackageRemoved(String packageName, int uid) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_REMOVED, packageName); + mHandler.sendMessage(msg); + } + + /** + * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED Intent.ACTION_PACKAGE_CHANGED} + * being received, informing you of changes to the enabled/disabled state of components in a + * package and/or of the overall package. + * + * @param packageName The name of the package that is changing. + * @param uid The user ID the package runs under. + * @param components Any components in the package that are changing. If the overall package is + * changing, this will contain an entry of the package name itself. + * @return Return true to indicate you care about this change, which will result in + * {@link #onSomePackagesChanged()} being called later. If you return false, no further + * callbacks will happen about this change. The default implementation returns true if + * this is a change to the entire package. + */ + public boolean onPackageChanged(String packageName, int uid, String[] components) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_CHANGED, packageName); + mHandler.sendMessage(msg); + return super.onPackageChanged(packageName, uid, components); + } +} diff --git a/core/java/com/android/internal/util/hwkeys/SystemReceiver.java b/core/java/com/android/internal/util/hwkeys/SystemReceiver.java new file mode 100644 index 0000000000000..f817563324764 --- /dev/null +++ b/core/java/com/android/internal/util/hwkeys/SystemReceiver.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015-2016 The TeamEos Project + * + * Author: Randall Rushing + * + * 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. + * + * A BroadcastReceiver that filters out broadcasts from non-system + * related processes. Subclasses can allow additional broadcasting + * packages by overriding onExemptBroadcast(Context context, String packageName) + */ + +package com.android.internal.util.hwkeys; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public abstract class SystemReceiver extends BroadcastReceiver { + + protected abstract void onSecureReceive(Context context, Intent intent); + + protected boolean onExemptBroadcast(Context context, String packageName) { + return false; + } + + @Override + public final void onReceive(Context context, Intent intent) { + if (context == null || intent == null) { + return; + } + if (isBroadcastFromSystem(context)) { + onSecureReceive(context, intent); + } + } + + private boolean isBroadcastFromSystem(Context context) { + String packageName = context.getPackageName(); + if (packageName == null + && android.os.Process.SYSTEM_UID == context.getApplicationInfo().uid) { + packageName = "android"; + } + if (packageName == null) { + return false; + } + if (packageName.equals("com.android.systemui") + || packageName.equals("com.android.keyguard") + || packageName.equals("com.android.settings") + || packageName.equals("android") + || context.getApplicationInfo().uid == android.os.Process.SYSTEM_UID) { + return true; + } + if (onExemptBroadcast(context, packageName)) { + return true; + } + return false; + } +} diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index b866723954b5b..b11ea2961c179 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; import android.util.Size; @@ -108,6 +109,12 @@ public static Drawable resolveImage(@Nullable Icon icon, Context context, int ma } break; case Icon.TYPE_RESOURCE: + if (!(TextUtils.isEmpty(icon.getResPackage()) + || context.getPackageName().equals(icon.getResPackage()))) { + // We can't properly resolve icons from other packages here, so fall back. + return icon.loadDrawable(context); + } + Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight); if (result != null) { return tintDrawable(icon, result); diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index 1074004b4c33e..db83c09e962bc 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -26,7 +26,6 @@ import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.os.storage.StorageManager; import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -368,7 +367,7 @@ public void close() { @Override public int hashCode() { // Effective Java — Always override hashCode when you override equals - return (17 + mType) * 31 + mCredential.hashCode(); + return Objects.hash(mType, Arrays.hashCode(mCredential)); } @Override diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index 07ee9b5d2ff16..88b47c09a69b5 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -42,14 +42,11 @@ @RemoteViews.RemoteView public class NotificationExpandButton extends FrameLayout { - private View mPillView; private TextView mNumberView; private ImageView mIconView; private boolean mExpanded; private int mNumber; - private int mDefaultPillColor; private int mDefaultTextColor; - private int mHighlightPillColor; private int mHighlightTextColor; public NotificationExpandButton(Context context) { @@ -73,7 +70,6 @@ public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, i @Override protected void onFinishInflate() { super.onFinishInflate(); - mPillView = findViewById(R.id.expand_button_pill); mNumberView = findViewById(R.id.expand_button_number); mIconView = findViewById(R.id.expand_button_icon); } @@ -155,17 +151,11 @@ private void updateNumber() { private void updateColors() { if (shouldShowNumber()) { - if (mHighlightPillColor != 0) { - mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor)); - } mIconView.setColorFilter(mHighlightTextColor); if (mHighlightTextColor != 0) { mNumberView.setTextColor(mHighlightTextColor); } } else { - if (mDefaultPillColor != 0) { - mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor)); - } mIconView.setColorFilter(mDefaultTextColor); if (mDefaultTextColor != 0) { mNumberView.setTextColor(mDefaultTextColor); @@ -191,8 +181,7 @@ public void setDefaultTextColor(int color) { */ @RemotableViewMethod public void setDefaultPillColor(@ColorInt int color) { - mDefaultPillColor = color; - updateColors(); + // no op, but whole android gonna error out otherwise } /** @@ -209,8 +198,7 @@ public void setHighlightTextColor(int color) { */ @RemotableViewMethod public void setHighlightPillColor(@ColorInt int color) { - mHighlightPillColor = color; - updateColors(); + // no-op } /** diff --git a/core/jni/Android.bp b/core/jni/Android.bp index a1be88440c97a..e96a921627d46 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { name: "libandroid_runtime", host_supported: true, cflags: [ + "-O3", "-Wno-unused-parameter", "-Wno-non-virtual-dtor", "-Wno-maybe-uninitialized", @@ -36,7 +37,7 @@ cc_library_shared { "-Wunreachable-code", ], - cppflags: ["-Wno-conversion-null"], + cppflags: ["-Wno-conversion-null", "-O3"], srcs: [ "android_animation_PropertyValuesHolder.cpp", @@ -405,9 +406,8 @@ cc_library_shared { }, }, - // Workaround Clang LTO crash. lto: { - never: true, + thin: true, }, } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 365a18d174c9e..3325f62bba014 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -62,6 +62,18 @@ struct fields_t { jmethodID rect_constructor; jmethodID face_constructor; jmethodID point_constructor; + jfieldID face_sm_degree; + jfieldID face_sm_score; + jfieldID face_blink_detected; + jfieldID face_gaze_angle; + jfieldID face_updown_dir; + jfieldID face_leftright_dir; + jfieldID face_roll_dir; + jfieldID face_leye_blink; + jfieldID face_reye_blink; + jfieldID face_left_right_gaze; + jfieldID face_top_bottom_gaze; + jfieldID face_recognised; }; static fields_t fields; @@ -100,6 +112,7 @@ class JNICameraContext: public CameraListener jclass mFaceClass; // strong reference to Face class jclass mRectClass; // strong reference to Rect class jclass mPointClass; // strong reference to Point class + bool mIsExtendedFace; Mutex mLock; /* @@ -151,8 +164,16 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; - jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); - mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + jclass extendedfaceClazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != extendedfaceClazz) { + mFaceClass = (jclass) env->NewGlobalRef(extendedfaceClazz); + mIsExtendedFace = true; + } else { + env->ExceptionClear(); + jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); + mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + mIsExtendedFace = false; + } jclass rectClazz = env->FindClass("android/graphics/Rect"); mRectClass = (jclass) env->NewGlobalRef(rectClazz); @@ -404,7 +425,6 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(rect, fields.rect_top, metadata->faces[i].rect[1]); env->SetIntField(rect, fields.rect_right, metadata->faces[i].rect[2]); env->SetIntField(rect, fields.rect_bottom, metadata->faces[i].rect[3]); - env->SetObjectField(face, fields.face_rect, rect); env->SetIntField(face, fields.face_score, metadata->faces[i].score); @@ -433,6 +453,21 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(mouth, fields.point_y, metadata->faces[i].mouth[1]); env->SetObjectField(face, fields.face_mouth, mouth); env->DeleteLocalRef(mouth); + + if (mIsExtendedFace) { + env->SetIntField(face, fields.face_sm_degree, metadata->faces[i].smile_degree); + env->SetIntField(face, fields.face_sm_score, metadata->faces[i].smile_score); + env->SetIntField(face, fields.face_blink_detected, metadata->faces[i].blink_detected); + env->SetIntField(face, fields.face_recognised, metadata->faces[i].face_recognised); + env->SetIntField(face, fields.face_gaze_angle, metadata->faces[i].gaze_angle); + env->SetIntField(face, fields.face_updown_dir, metadata->faces[i].updown_dir); + env->SetIntField(face, fields.face_leftright_dir, metadata->faces[i].leftright_dir); + env->SetIntField(face, fields.face_roll_dir, metadata->faces[i].roll_dir); + env->SetIntField(face, fields.face_leye_blink, metadata->faces[i].leye_blink); + env->SetIntField(face, fields.face_reye_blink, metadata->faces[i].reye_blink); + env->SetIntField(face, fields.face_left_right_gaze, metadata->faces[i].left_right_gaze); + env->SetIntField(face, fields.face_top_bottom_gaze, metadata->faces[i].top_bottom_gaze); + } } env->DeleteLocalRef(face); @@ -470,6 +505,56 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM } } +static void android_hardware_Camera_setLongshot(JNIEnv *env, jobject thiz, jboolean enable) +{ + ALOGV("setLongshot"); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if ( enable ) { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_ON, 0, 0); + } else { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_OFF, 0, 0); + } + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "enabling longshot mode failed"); + } +} + +static void android_hardware_Camera_sendHistogramData(JNIEnv *env, jobject thiz) + { + ALOGV("sendHistogramData" ); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_SEND_DATA, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "send histogram data failed"); + } + } + static void android_hardware_Camera_setHistogramMode(JNIEnv *env, jobject thiz, jboolean mode) + { + ALOGV("setHistogramMode: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set histogram mode failed"); + } + } void JNICameraContext::addCallbackBuffer( JNIEnv *env, jbyteArray cbb, int msgType) { @@ -783,7 +868,25 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t context->setCallbackMode(env, installed, manualBuffer); } -static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, jint msgType) { +static void android_hardware_Camera_setMetadataCb(JNIEnv *env, jobject thiz, jboolean mode) +{ + ALOGV("setMetadataCb: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_METADATA_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_METADATA_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set metadata mode failed"); + } +} + +static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) { ALOGV("addCallbackBuffer: 0x%x", msgType); JNICameraContext* context = reinterpret_cast(env->GetLongField(thiz, fields.context)); @@ -1048,10 +1151,22 @@ static int32_t android_hardware_Camera_getAudioRestriction( return ret; } +static void android_hardware_Camera_sendVendorCommand(JNIEnv *env, jobject thiz, + jint cmd, jint arg1, jint arg2) +{ + ALOGV("sendVendorCommand"); + sp camera = get_native_camera(env, thiz, NULL); + if (camera == 0) return; + + if (camera->sendCommand(cmd, arg1, arg2) != NO_ERROR) { + jniThrowRuntimeException(env, "sending vendor command failed"); + } +} + //------------------------------------------------- static const JNINativeMethod camMethods[] = { - { "getNumberOfCameras", + { "_getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras }, { "_getCameraInfo", @@ -1096,6 +1211,18 @@ static const JNINativeMethod camMethods[] = { { "native_takePicture", "(I)V", (void *)android_hardware_Camera_takePicture }, + { "native_setHistogramMode", + "(Z)V", + (void *)android_hardware_Camera_setHistogramMode }, + { "native_setMetadataCb", + "(Z)V", + (void *)android_hardware_Camera_setMetadataCb }, + { "native_sendHistogramData", + "()V", + (void *)android_hardware_Camera_sendHistogramData }, + { "native_setLongshot", + "(Z)V", + (void *)android_hardware_Camera_setLongshot }, { "native_setParameters", "(Ljava/lang/String;)V", (void *)android_hardware_Camera_setParameters }, @@ -1138,6 +1265,9 @@ static const JNINativeMethod camMethods[] = { { "getAudioRestriction", "()I", (void *)android_hardware_Camera_getAudioRestriction}, + { "_sendVendorCommand", + "(III)V", + (void *)android_hardware_Camera_sendVendorCommand }, }; struct field { @@ -1180,6 +1310,27 @@ int register_android_hardware_Camera(JNIEnv *env) { "android/graphics/Point", "y", "I", &fields.point_y}, }; + field extendedfacefields_to_find[] = { + { "com/qualcomm/qti/camera/ExtendedFace", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, + { "com/qualcomm/qti/camera/ExtendedFace", "score", "I", &fields.face_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "id", "I", &fields.face_id }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftEye", "Landroid/graphics/Point;", &fields.face_left_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "rightEye", "Landroid/graphics/Point;", &fields.face_right_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "mouth", "Landroid/graphics/Point;", &fields.face_mouth }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileDegree", "I", &fields.face_sm_degree }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileScore", "I", &fields.face_sm_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "blinkDetected", "I", &fields.face_blink_detected }, + { "com/qualcomm/qti/camera/ExtendedFace", "faceRecognized", "I", &fields.face_recognised }, + { "com/qualcomm/qti/camera/ExtendedFace", "gazeAngle", "I", &fields.face_gaze_angle }, + { "com/qualcomm/qti/camera/ExtendedFace", "updownDir", "I", &fields.face_updown_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightDir", "I", &fields.face_leftright_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "rollDir", "I", &fields.face_roll_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leyeBlink", "I", &fields.face_leye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "reyeBlink", "I", &fields.face_reye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightGaze", "I", &fields.face_left_right_gaze }, + { "com/qualcomm/qti/camera/ExtendedFace", "topbottomGaze", "I", &fields.face_top_bottom_gaze }, + }; + find_fields(env, fields_to_find, NELEM(fields_to_find)); jclass clazz = FindClassOrDie(env, "android/hardware/Camera"); @@ -1199,6 +1350,14 @@ int register_android_hardware_Camera(JNIEnv *env) return -1; } + clazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != clazz) { + fields.face_constructor = env->GetMethodID(clazz, "", "()V"); + find_fields(env, extendedfacefields_to_find, NELEM(extendedfacefields_to_find)); + }else { + env->ExceptionClear(); + } + // Register native functions return RegisterMethodsOrDie(env, "android/hardware/Camera", camMethods, NELEM(camMethods)); } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index f24c66695052d..156dadc8dda45 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -37,6 +37,7 @@ #include #include +#include #include "android_media_AudioAttributes.h" #include "android_media_AudioDescriptor.h" #include "android_media_AudioDeviceAttributes.h" @@ -193,6 +194,9 @@ static struct { static jclass gAudioDescriptorClass; static jmethodID gAudioDescriptorCstor; +static jclass gAppVolumeClass; +static jmethodID gAppVolumeCstor; + // // JNI Initialization for OpenSLES routing // @@ -843,6 +847,90 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) return balance; } +static jint +android_media_AudioSystem_setAppVolume(JNIEnv *env, jobject thiz, jstring packageName, jfloat value) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), + env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppVolume(package8, value)); +} + +static jint +android_media_AudioSystem_setAppMute(JNIEnv *env, jobject thiz, jstring packageName, jboolean mute) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), + env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppMute(package8, mute)); +} + +jint convertAppVolumeFromNative(JNIEnv *env, jobject *jAppVolume, const media::AppVolume *AppVolume) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jstring jPackageName; + jfloat jVolume; + jboolean jMute; + jboolean jActive; + + if (AppVolume == NULL || jAppVolume == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + jPackageName = env->NewStringUTF(AppVolume->packageName); + jVolume = AppVolume->volume; + jMute = AppVolume->muted; + jActive = AppVolume->active; + + *jAppVolume = env->NewObject(gAppVolumeClass, gAppVolumeCstor, + jPackageName, jMute, jVolume, jActive); + + env->DeleteLocalRef(jPackageName); +exit: + return jStatus; +} + +static jint +android_media_AudioSystem_listAppVolumes(JNIEnv *env, jobject clazz, jobject jVolumes) +{ + ALOGV("listAppVolumes"); + + if (jVolumes == NULL) { + ALOGE("listAppVolumes NULL AppVolume ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jVolumes, gArrayListClass)) { + ALOGE("listAppVolumes not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + std::vector volumes; + + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + status_t status = AudioSystem::listAppVolumes(&volumes); + + if (status != NO_ERROR) { + ALOGE("AudioSystem::listAppVolumes error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + + for (size_t i = 0; i < volumes.size(); i++) { + jobject jAppVolume; + jStatus = convertAppVolumeFromNative(env, &jAppVolume, &volumes[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jVolumes, gArrayListMethods.add, jAppVolume); + env->DeleteLocalRef(jAppVolume); + } + + return jStatus; +} + static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { @@ -3103,7 +3191,13 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_getDirectPlaybackSupport}, {"getDirectProfilesForAttributes", "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getDirectProfilesForAttributes}}; + (void *)android_media_AudioSystem_getDirectProfilesForAttributes}, + {"setAppVolume", "(Ljava/lang/String;F)I", + (void *)android_media_AudioSystem_setAppVolume}, + {"setAppMute", "(Ljava/lang/String;Z)I", + (void *)android_media_AudioSystem_setAppMute}, + {"listAppVolumes", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioSystem_listAppVolumes}}; static const JNINativeMethod gEventHandlerMethods[] = { {"native_setup", @@ -3342,6 +3436,11 @@ int register_android_media_AudioSystem(JNIEnv *env) gVibratorMethods.getMaxAmplitude = GetMethodIDOrDie(env, vibratorClass, "getHapticChannelMaximumAmplitude", "()F"); + jclass AppVolumeClass = FindClassOrDie(env, "android/media/AppVolume"); + gAppVolumeClass = MakeGlobalRefOrDie(env, AppVolumeClass); + gAppVolumeCstor = GetMethodIDOrDie(env, AppVolumeClass, "", + "(Ljava/lang/String;ZFZ)V"); + AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback); RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 21bbac0b0a7d8..c56a5fe42c914 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -128,8 +128,6 @@ static jclass gZygoteInitClass; static jmethodID gGetOrCreateSystemServerClassLoader; static jmethodID gPrefetchStandaloneSystemServerJars; -static bool gIsSecurityEnforced = true; - /** * True if the app process is running in its mount namespace. */ @@ -634,11 +632,6 @@ static void PreApplicationInit() { } static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { - if (!gIsSecurityEnforced) { - ALOGI("seccomp disabled by setenforce 0"); - return; - } - // Apply system or app filter based on uid. if (uid >= AID_APP_START) { if (is_child_zygote) { @@ -2317,11 +2310,6 @@ static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork( static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter( JNIEnv* env, jclass, jint uidGidMin, jint uidGidMax) { - if (!gIsSecurityEnforced) { - ALOGI("seccomp disabled by setenforce 0"); - return; - } - bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax); if (!installed) { RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter."); @@ -2398,10 +2386,6 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc * Security Initialization */ - // security_getenforce is not allowed on app process. Initialize and cache - // the value before zygote forks. - gIsSecurityEnforced = security_getenforce(); - selinux_android_seapp_context_init(); /* diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7439b2f0921ff..cec12e557fa95 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -816,6 +816,17 @@ + + + + + + + + + + + @@ -6518,6 +6529,11 @@ + + + @@ -6542,6 +6558,21 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/anim/custom_app_out.xml b/core/res/res/anim/custom_app_out.xml new file mode 100644 index 0000000000000..ef042528eabe0 --- /dev/null +++ b/core/res/res/anim/custom_app_out.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/anim/honami_toast_enter.xml b/core/res/res/anim/honami_toast_enter.xml new file mode 100644 index 0000000000000..5603d1500a42d --- /dev/null +++ b/core/res/res/anim/honami_toast_enter.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/core/res/res/anim/honami_toast_exit.xml b/core/res/res/anim/honami_toast_exit.xml new file mode 100644 index 0000000000000..0bbf34d73694f --- /dev/null +++ b/core/res/res/anim/honami_toast_exit.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/core/res/res/anim/rotate.xml b/core/res/res/anim/rotate.xml new file mode 100644 index 0000000000000..dc2e6e294697b --- /dev/null +++ b/core/res/res/anim/rotate.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/core/res/res/anim/slide_in_down.xml b/core/res/res/anim/slide_in_down.xml new file mode 100644 index 0000000000000..6cf96d866963d --- /dev/null +++ b/core/res/res/anim/slide_in_down.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/core/res/res/anim/slide_out_up.xml b/core/res/res/anim/slide_out_up.xml new file mode 100644 index 0000000000000..f390d9143320e --- /dev/null +++ b/core/res/res/anim/slide_out_up.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/core/res/res/anim/tn_toast_enter.xml b/core/res/res/anim/tn_toast_enter.xml new file mode 100644 index 0000000000000..cf2337f94f72b --- /dev/null +++ b/core/res/res/anim/tn_toast_enter.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/core/res/res/anim/tn_toast_exit.xml b/core/res/res/anim/tn_toast_exit.xml new file mode 100644 index 0000000000000..31bfad82212af --- /dev/null +++ b/core/res/res/anim/tn_toast_exit.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/core/res/res/anim/xylon_toast_enter.xml b/core/res/res/anim/xylon_toast_enter.xml new file mode 100644 index 0000000000000..1067034feb274 --- /dev/null +++ b/core/res/res/anim/xylon_toast_enter.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/core/res/res/anim/xylon_toast_exit.xml b/core/res/res/anim/xylon_toast_exit.xml new file mode 100644 index 0000000000000..37b861a9cec82 --- /dev/null +++ b/core/res/res/anim/xylon_toast_exit.xml @@ -0,0 +1,4 @@ + + diff --git a/core/res/res/color/config_progress_background_tint.xml b/core/res/res/color/config_progress_background_tint.xml index b086e20bf1619..dfc914e76c8d0 100644 --- a/core/res/res/color/config_progress_background_tint.xml +++ b/core/res/res/color/config_progress_background_tint.xml @@ -15,5 +15,5 @@ --> - + diff --git a/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png index c9c72ba61947b..4e7eb8ad4324e 100644 Binary files a/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png index 910de294cebdb..7a17ebe44cf14 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim1.png b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png index 0b1aa3488cb52..7aec9a110d311 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim1.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim10.png b/core/res/res/drawable-hdpi/stat_sys_download_anim10.png new file mode 100644 index 0000000000000..9fdabbb8ed4f4 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim10.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim11.png b/core/res/res/drawable-hdpi/stat_sys_download_anim11.png new file mode 100644 index 0000000000000..2343d7b117e5b Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim11.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim12.png b/core/res/res/drawable-hdpi/stat_sys_download_anim12.png new file mode 100644 index 0000000000000..ad0e62a2e7048 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim12.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim13.png b/core/res/res/drawable-hdpi/stat_sys_download_anim13.png new file mode 100644 index 0000000000000..2fdc15538334d Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim13.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim14.png b/core/res/res/drawable-hdpi/stat_sys_download_anim14.png new file mode 100644 index 0000000000000..9729b2caa22ad Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim14.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim15.png b/core/res/res/drawable-hdpi/stat_sys_download_anim15.png new file mode 100644 index 0000000000000..c192d805ce778 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim15.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim16.png b/core/res/res/drawable-hdpi/stat_sys_download_anim16.png new file mode 100644 index 0000000000000..759bfb021df79 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim16.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim17.png b/core/res/res/drawable-hdpi/stat_sys_download_anim17.png new file mode 100644 index 0000000000000..af24259654778 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim17.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim18.png b/core/res/res/drawable-hdpi/stat_sys_download_anim18.png new file mode 100644 index 0000000000000..f272313ea4aa0 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim18.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim2.png b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png index bc1877dab0be5..335c6764d9742 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim2.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim3.png b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png index 9f41092baabf1..58b1d48e5140d 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim3.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim4.png b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png index 5fa63053785ec..ff6c1ee84237a 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim4.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim5.png b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png index 703759ae5837e..1b8f7c8eb7f5f 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_download_anim5.png and b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim6.png b/core/res/res/drawable-hdpi/stat_sys_download_anim6.png new file mode 100644 index 0000000000000..bc8230dcaae09 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim6.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim7.png b/core/res/res/drawable-hdpi/stat_sys_download_anim7.png new file mode 100644 index 0000000000000..b05c706a8be73 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim7.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim8.png b/core/res/res/drawable-hdpi/stat_sys_download_anim8.png new file mode 100644 index 0000000000000..b13dea81c92c6 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim8.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim9.png b/core/res/res/drawable-hdpi/stat_sys_download_anim9.png new file mode 100644 index 0000000000000..efb9b8bcbda66 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_download_anim9.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png index 78f54b7f57a99..a90f83f9bb5c1 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png index 3a9031e8e0049..c27f30bd39237 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim10.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim10.png new file mode 100644 index 0000000000000..e165b0f3fbd7f Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim10.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim11.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim11.png new file mode 100644 index 0000000000000..7bcf332dddea1 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim11.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim12.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim12.png new file mode 100644 index 0000000000000..5a9ce2d2f8cd9 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim12.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim13.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim13.png new file mode 100644 index 0000000000000..e5730d6445209 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim13.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim14.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim14.png new file mode 100644 index 0000000000000..ed5c8c48d11e8 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim14.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim15.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim15.png new file mode 100644 index 0000000000000..730d1c5537454 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim15.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim16.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim16.png new file mode 100644 index 0000000000000..ae987bbf3d131 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim16.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim17.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim17.png new file mode 100644 index 0000000000000..d3f346bbbca40 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim17.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim18.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim18.png new file mode 100644 index 0000000000000..a3bcc9c9decb2 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim18.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png index 39d2c95ffab21..65cfe84b9f1e3 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png index 937720fcf6249..20f4a707e3c5b 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png index b35672cc27db0..f9cad19fb6801 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png index 4611122f5adce..d3c53f86ddb3b 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim6.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim6.png new file mode 100644 index 0000000000000..dddb6a75c2fa4 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim6.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim7.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim7.png new file mode 100644 index 0000000000000..57eab54dafc84 Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim7.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim8.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim8.png new file mode 100644 index 0000000000000..d48638f5a589e Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim8.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim9.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim9.png new file mode 100644 index 0000000000000..fe1c389f1854e Binary files /dev/null and b/core/res/res/drawable-hdpi/stat_sys_upload_anim9.png differ diff --git a/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png index 076fc16642176..97d250f889a2b 100644 Binary files a/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png index 25324f645f849..ad78265dfb447 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png index 6d1fb4a0a8cf1..c8a83d65f94ad 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim10.png b/core/res/res/drawable-mdpi/stat_sys_download_anim10.png new file mode 100644 index 0000000000000..95c25753d633e Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim10.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim11.png b/core/res/res/drawable-mdpi/stat_sys_download_anim11.png new file mode 100644 index 0000000000000..7b0b8a87d6dca Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim11.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim12.png b/core/res/res/drawable-mdpi/stat_sys_download_anim12.png new file mode 100644 index 0000000000000..c1cfb9296e7d6 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim12.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim13.png b/core/res/res/drawable-mdpi/stat_sys_download_anim13.png new file mode 100644 index 0000000000000..e5af82d6d7799 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim13.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim14.png b/core/res/res/drawable-mdpi/stat_sys_download_anim14.png new file mode 100644 index 0000000000000..2bf150d0e4385 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim14.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim15.png b/core/res/res/drawable-mdpi/stat_sys_download_anim15.png new file mode 100644 index 0000000000000..c6241be2aa9e3 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim15.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim16.png b/core/res/res/drawable-mdpi/stat_sys_download_anim16.png new file mode 100644 index 0000000000000..c38872c8322e1 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim16.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim17.png b/core/res/res/drawable-mdpi/stat_sys_download_anim17.png new file mode 100644 index 0000000000000..a6703f6fa6575 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim17.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim18.png b/core/res/res/drawable-mdpi/stat_sys_download_anim18.png new file mode 100644 index 0000000000000..f16ff9f3a813f Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim18.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png index 4c3e96399fa18..5f106ab8d82c7 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png index 2aae62541a8dc..f7daef0c82ce1 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png index 55dbe12011516..18cd62f920471 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png index 53fda4441ddf6..cf97a00380bc4 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png and b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim6.png b/core/res/res/drawable-mdpi/stat_sys_download_anim6.png new file mode 100644 index 0000000000000..b4c2e2ab026e2 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim6.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim7.png b/core/res/res/drawable-mdpi/stat_sys_download_anim7.png new file mode 100644 index 0000000000000..8382d02aa8c77 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim7.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim8.png b/core/res/res/drawable-mdpi/stat_sys_download_anim8.png new file mode 100644 index 0000000000000..2e7e2f88eb91e Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim8.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim9.png b/core/res/res/drawable-mdpi/stat_sys_download_anim9.png new file mode 100644 index 0000000000000..6af098d3a1af9 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_download_anim9.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png index 6402aa5ae1c6b..b70ce210a2394 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png index 217ea4eb65792..09982a9bb473d 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim10.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim10.png new file mode 100644 index 0000000000000..372d2effb3ab3 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim10.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim11.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim11.png new file mode 100644 index 0000000000000..bf962cb1ff08c Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim11.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim12.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim12.png new file mode 100644 index 0000000000000..4b69212f1c3a7 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim12.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim13.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim13.png new file mode 100644 index 0000000000000..8d8803a92cc76 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim13.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim14.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim14.png new file mode 100644 index 0000000000000..baba70fef4d7c Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim14.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim15.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim15.png new file mode 100644 index 0000000000000..a65995d4f2fc8 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim15.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim16.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim16.png new file mode 100644 index 0000000000000..ee44e464d2f66 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim16.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim17.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim17.png new file mode 100644 index 0000000000000..943e948ac3381 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim17.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim18.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim18.png new file mode 100644 index 0000000000000..5d1762182beed Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim18.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png index b9c364c92b156..ed9033ff4b6f5 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png index e22ec6ded41fd..8d16ecb46ba34 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png index a86d5cdfa1c4b..611564f38700d 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png index 3387dbb27be42..42e7102708748 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim6.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim6.png new file mode 100644 index 0000000000000..f67436a569365 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim6.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim7.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim7.png new file mode 100644 index 0000000000000..c77f472f85353 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim7.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim8.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim8.png new file mode 100644 index 0000000000000..355169ed3f1d4 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim8.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim9.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim9.png new file mode 100644 index 0000000000000..bf92fcecd1a59 Binary files /dev/null and b/core/res/res/drawable-mdpi/stat_sys_upload_anim9.png differ diff --git a/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png index 97eb5fe801ead..a893b5790a6a4 100644 Binary files a/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim0.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim0.png index 73cbc96f903e3..e72c7286f98a7 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim0.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim0.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim1.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim1.png index 3e39abb05b043..1ef32845d0e4e 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim1.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim1.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim10.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim10.png new file mode 100644 index 0000000000000..a2b24bc9d7f3a Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim10.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim11.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim11.png new file mode 100644 index 0000000000000..17944d08a1ca6 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim11.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim12.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim12.png new file mode 100644 index 0000000000000..7022b36844d00 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim12.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim13.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim13.png new file mode 100644 index 0000000000000..1743f2a7e9259 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim13.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim14.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim14.png new file mode 100644 index 0000000000000..036df908ee6bc Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim14.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim15.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim15.png new file mode 100644 index 0000000000000..840d0995d0699 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim15.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim16.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim16.png new file mode 100644 index 0000000000000..13be45707de1b Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim16.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim17.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim17.png new file mode 100644 index 0000000000000..db67e47b8ee3f Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim17.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim18.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim18.png new file mode 100644 index 0000000000000..70e4825297df8 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim18.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim2.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim2.png index fc9b0dec53bfa..93191c6a274c8 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim2.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim2.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim3.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim3.png index 94bc01220c59c..a50c3eef05988 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim3.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim3.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim4.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim4.png index e6b5857fecde8..9ab112e1d1b25 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim4.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim4.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim5.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim5.png index f1df0c878bee0..4f43a7e1c7c7c 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_download_anim5.png and b/core/res/res/drawable-xhdpi/stat_sys_download_anim5.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim6.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim6.png new file mode 100644 index 0000000000000..c79b8566a91b6 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim6.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim7.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim7.png new file mode 100644 index 0000000000000..cdcaf8d91f95d Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim7.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim8.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim8.png new file mode 100644 index 0000000000000..fc449c293f140 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim8.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_download_anim9.png b/core/res/res/drawable-xhdpi/stat_sys_download_anim9.png new file mode 100644 index 0000000000000..2760256252ffa Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_download_anim9.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim0.png index 2fbdaf8b269da..9dabd4043e746 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim0.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim0.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png index e443f4573f1ae..b4782e96e9eb6 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim10.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim10.png new file mode 100644 index 0000000000000..e9292a2664bd3 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim10.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim11.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim11.png new file mode 100644 index 0000000000000..4937c428229b3 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim11.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim12.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim12.png new file mode 100644 index 0000000000000..eaa6951e8d9ba Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim12.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim13.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim13.png new file mode 100644 index 0000000000000..a8b69353c4e8b Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim13.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim14.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim14.png new file mode 100644 index 0000000000000..7875c63d5742f Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim14.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim15.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim15.png new file mode 100644 index 0000000000000..d3ec0f90ad8af Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim15.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim16.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim16.png new file mode 100644 index 0000000000000..6fa08ce1c1a10 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim16.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim17.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim17.png new file mode 100644 index 0000000000000..4877a5f3ddcd0 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim17.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim18.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim18.png new file mode 100644 index 0000000000000..09e66f450c567 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim18.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png index cd0ca73608da7..677d429cf9b18 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim3.png index 8fb42f8ea5fd8..083e2a37c01d9 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim3.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim3.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim4.png index 2fb18024e9d00..5da42d0061e67 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim4.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim4.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim5.png index c1d9db5a62c3d..1a80a0c16a632 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim5.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim5.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim6.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim6.png new file mode 100644 index 0000000000000..d1dce235f749a Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim6.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim7.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim7.png new file mode 100644 index 0000000000000..91aa7f600d263 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim7.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim8.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim8.png new file mode 100644 index 0000000000000..6d51cb8e41036 Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim8.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim9.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim9.png new file mode 100644 index 0000000000000..b7bde70e461bf Binary files /dev/null and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim9.png differ diff --git a/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png index b7a99402eb58c..3b6fa1a6c4072 100644 Binary files a/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png index 5bc3add437709..79466790a02c7 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim10.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim10.png new file mode 100644 index 0000000000000..740063a937ba8 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim10.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim11.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim11.png new file mode 100644 index 0000000000000..37b527f61ddf9 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim11.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim12.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim12.png new file mode 100644 index 0000000000000..0601906c0a78c Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim12.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim13.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim13.png new file mode 100644 index 0000000000000..26bb2170f8c74 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim13.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim14.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim14.png new file mode 100644 index 0000000000000..726827a50d38a Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim14.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim15.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim15.png new file mode 100644 index 0000000000000..0101fdb4dbbcc Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim15.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim16.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim16.png new file mode 100644 index 0000000000000..9cea4887761a6 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim16.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim17.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim17.png new file mode 100644 index 0000000000000..45a835e8f331f Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim17.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim18.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim18.png new file mode 100644 index 0000000000000..443302dc78dcd Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim18.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png index 962c450ff916a..a0ee1e703f749 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png index e1d0d552fbb5d..6df42c2e1b4cd 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png index 84420def97757..031705374f1a1 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png index b495943b5c250..c602a2832758b 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim6.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim6.png new file mode 100644 index 0000000000000..9ccc63b4181b4 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim6.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim7.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim7.png new file mode 100644 index 0000000000000..bb9b7fae0f6ef Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim7.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim8.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim8.png new file mode 100644 index 0000000000000..1d35ca382f192 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim8.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim9.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim9.png new file mode 100644 index 0000000000000..c66e91c110092 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_download_anim9.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png index b828430b766a4..7a7c63f092022 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim10.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim10.png new file mode 100644 index 0000000000000..b9b6c3be40242 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim10.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim11.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim11.png new file mode 100644 index 0000000000000..d034ef30e7cfe Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim11.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim12.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim12.png new file mode 100644 index 0000000000000..ace6cabfc9d12 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim12.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim13.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim13.png new file mode 100644 index 0000000000000..e7d9265d04053 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim13.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim14.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim14.png new file mode 100644 index 0000000000000..aae212a07a2b0 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim14.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim15.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim15.png new file mode 100644 index 0000000000000..da1fb76c78b01 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim15.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim16.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim16.png new file mode 100644 index 0000000000000..1c98dc9622213 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim16.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim17.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim17.png new file mode 100644 index 0000000000000..22245aae62b1c Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim17.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim18.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim18.png new file mode 100644 index 0000000000000..b076296c7984d Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim18.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png index 39dd3b800cb5d..9b46812c28186 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png index 78344607c6fdc..103b109c67b34 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png index 34c6f27b98a2d..f2eb7a9baba08 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png index 1270acf0d238a..344c809a7b785 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim6.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim6.png new file mode 100644 index 0000000000000..94973dcc7e48a Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim6.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim7.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim7.png new file mode 100644 index 0000000000000..a79c50ff43b9f Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim7.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim8.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim8.png new file mode 100644 index 0000000000000..2139e267f070d Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim8.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim9.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim9.png new file mode 100644 index 0000000000000..9e6367f32238c Binary files /dev/null and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim9.png differ diff --git a/core/res/res/drawable/ic_ab_back_material.xml b/core/res/res/drawable/ic_ab_back_material.xml index e4bc6ccfbf076..0418309c88baf 100644 --- a/core/res/res/drawable/ic_ab_back_material.xml +++ b/core/res/res/drawable/ic_ab_back_material.xml @@ -1,26 +1,28 @@ - + android:tint="?android:colorControlNormal" + android:autoMirrored="true" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + diff --git a/core/res/res/drawable/ic_ab_back_material_dark.xml b/core/res/res/drawable/ic_ab_back_material_dark.xml index 7cdd1393a4a38..cfbc20d598ef0 100644 --- a/core/res/res/drawable/ic_ab_back_material_dark.xml +++ b/core/res/res/drawable/ic_ab_back_material_dark.xml @@ -1,27 +1,28 @@ - + android:tint="@color/secondary_text_material_dark" + android:autoMirrored="true" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FFFFFF" + android:pathData="M24,0l-0,24l-24,0l-0,-24z" + android:strokeAlpha="0" + android:fillAlpha="0"/> + diff --git a/core/res/res/drawable/ic_ab_back_material_light.xml b/core/res/res/drawable/ic_ab_back_material_light.xml index 65dab8112ef23..0ad0bbbf34924 100644 --- a/core/res/res/drawable/ic_ab_back_material_light.xml +++ b/core/res/res/drawable/ic_ab_back_material_light.xml @@ -1,27 +1,28 @@ - + android:tint="@color/secondary_text_material_light" + android:autoMirrored="true" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FFFFFF" + android:pathData="M24,0l-0,24l-24,0l-0,-24z" + android:strokeAlpha="0" + android:fillAlpha="0"/> + diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml index 938e36d4ecb59..5f5e44410d0c4 100644 --- a/core/res/res/drawable/ic_ab_back_material_settings.xml +++ b/core/res/res/drawable/ic_ab_back_material_settings.xml @@ -1,29 +1,28 @@ - - + Copyright 2021-2022 The xdroidOSS & xyzprjkt + 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. +--> - + android:tint="?attr/colorControlNormal" + android:width="24dp" + android:height="24dp" + android:autoMirrored="true" + android:viewportWidth="24" + android:viewportHeight="24"> + + - diff --git a/core/res/res/drawable/ic_doc_album.xml b/core/res/res/drawable/ic_doc_album.xml new file mode 100644 index 0000000000000..3c46e0fa48b71 --- /dev/null +++ b/core/res/res/drawable/ic_doc_album.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/drawable/ic_doc_apk.xml b/core/res/res/drawable/ic_doc_apk.xml index 197445ef598d0..e9e494575e9f8 100644 --- a/core/res/res/drawable/ic_doc_apk.xml +++ b/core/res/res/drawable/ic_doc_apk.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M17 22.5l-10 0c-0.6 0 -1 -0.4 -1 -1L6 11l12 0 0 10.5c0 0.6 -0.4 1 -1 1zm-9 -2l8 0 0 -7.5 -8 0 0 7.5zM16.2 4.9c0.5 -0.6 0.9 -1.1 1.4 -1.7 0.3 -0.4 0.4 -1 0 -1.4 -0.3 -0.3 -1.1 -0.4 -1.4 0 -0.5 0.7 -1 1.3 -1.6 2C13.8 3.4 12.9 3.2 12 3.2 11.1 3.2 10.2 3.4 9.4 3.8 8.9 3.1 8.4 2.5 7.8 1.8 7.5 1.4 6.7 1.4 6.4 1.8 6 2.2 6.1 2.8 6.4 3.2 6.9 3.8 7.3 4.3 7.8 4.9 6.7 6 6 7.5 6 9.1l0 1.2 12 0 0 -1.2C18 7.5 17.3 6 16.2 4.9ZM8.1 8.4c0.4 -1.8 2 -3.2 3.9 -3.2 1.9 0 3.6 1.4 3.9 3.2l-7.8 0z"/> diff --git a/core/res/res/drawable/ic_doc_audio.xml b/core/res/res/drawable/ic_doc_audio.xml index 454eea3bbad90..65f9ccd7727fb 100644 --- a/core/res/res/drawable/ic_doc_audio.xml +++ b/core/res/res/drawable/ic_doc_audio.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M18.1 22l-3 0 0 -8.4 4 0 0 -2.2C19.1 7.4 15.9 4 12 4 8.1 4 4.9 7.4 4.9 11.5l0 2.2 4.1 0 0 8.3 -3 0C4.3 22 3 20.6 3 18.9L3 11.5C2.9 6.2 7 2 12 2c5 0 9.1 4.2 9.1 9.5l0 7.4c0 1.7 -1.4 3.1 -3 3.1zm-1 -2l1 0c0.6 0 1 -0.5 1 -1.1l0 -3.2 -2 0 0 4.3zm-12.2 -4.4l0 3.2c0 0.6 0.5 1.1 1 1.1l1 0 0 -4.4 -2 0z" /> diff --git a/core/res/res/drawable/ic_doc_compressed.xml b/core/res/res/drawable/ic_doc_compressed.xml index e0eb669af33ab..2bc4749d03f9d 100644 --- a/core/res/res/drawable/ic_doc_compressed.xml +++ b/core/res/res/drawable/ic_doc_compressed.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Zm9 6l-2 0 0 2 2 0 0 2 -2 0 0 -2 -2 0 0 -2 2 0 0 -2 -2 0 0 -2 2 0 0 2 2 0 0 2zm0 8l-2 0 0 -2 -2 0 0 -2 2 0 0 2 2 0 0 2z"/> diff --git a/core/res/res/drawable/ic_doc_contact.xml b/core/res/res/drawable/ic_doc_contact.xml index a77cb6ba1152d..7fedde74eed52 100644 --- a/core/res/res/drawable/ic_doc_contact.xml +++ b/core/res/res/drawable/ic_doc_contact.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Zm7 8.3C10.3 12.3 8.9 10.9 8.9 9.2 8.9 7.5 10.3 6 12 6c1.7 0 3.1 1.4 3.1 3.1 0 1.7 -1.4 3.2 -3.1 3.2zM12 8c-0.6 0 -1.1 0.5 -1.1 1.1 0 0.6 0.5 1.1 1.1 1.1 0.6 0 1.1 -0.5 1.1 -1.1C13.1 8.5 12.6 8 12 8Zm6 10l-12 0 0 -2.8c0 -1.6 2.9 -2.3 4.7 -2.6 0.9 -0.1 1.8 -0.1 2.7 0 1.8 0.3 4.7 1 4.7 2.6L18 18l0 0zm-10 -2l8 0 0 -0.5c-0.7 -0.4 -3.1 -1 -4 -1 -0.9 0 -3.3 0.6 -4 1L8 16Zm6.4 -3.2m-4.7 0"/> diff --git a/core/res/res/drawable/ic_doc_document.xml b/core/res/res/drawable/ic_doc_document.xml index 29251ad799f05..00f6a077f1a47 100644 --- a/core/res/res/drawable/ic_doc_document.xml +++ b/core/res/res/drawable/ic_doc_document.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M17 9L7 9 7 7 17 7 17 9Zm0 4l-10 0 0 -2 10 0 0 2zm-3 4l-7 0 0 -2 7 0 0 2zm5 5l-14 0C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_doc_event.xml b/core/res/res/drawable/ic_doc_event.xml index 113f079c8670c..5fee889901367 100644 --- a/core/res/res/drawable/ic_doc_event.xml +++ b/core/res/res/drawable/ic_doc_event.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M17 12l-5 0 0 5 5 0 0 -5zM19 3L18 3 18 1 16 1 16 3 8 3 8 1 6 1 6 3 5 3C3.9 3 3 3.9 3 5l0 14c0 1.1 0.9 2 2 2l14 0c1.1 0 2 -0.9 2 -2L21 5C21 3.9 20.1 3 19 3ZM19 19L5 19 5 9 19 9 19 19ZM5 7L5 5 19 5 19 7 5 7Z"/> diff --git a/core/res/res/drawable/ic_doc_excel.xml b/core/res/res/drawable/ic_doc_excel.xml index 3ed27e1f7f62b..0c5346232838d 100644 --- a/core/res/res/drawable/ic_doc_excel.xml +++ b/core/res/res/drawable/ic_doc_excel.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M16.5 6.5l-2.4 0 -2.1 3.9 -2.1 -3.9 -2.4 0 3.3 5.4 -3.3 5.4 2.4 0 2.1 -3.8 2.1 3.8 0 0.1 2.4 0 -3.3 -5.6c0 0.1 3.3 -5.3 3.3 -5.3zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_doc_folder.xml b/core/res/res/drawable/ic_doc_folder.xml index dcbce010810ed..ac29ba6656095 100644 --- a/core/res/res/drawable/ic_doc_folder.xml +++ b/core/res/res/drawable/ic_doc_folder.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /> diff --git a/core/res/res/drawable/ic_doc_font.xml b/core/res/res/drawable/ic_doc_font.xml index 4c13d71e47d01..0e6d507c6ad3e 100644 --- a/core/res/res/drawable/ic_doc_font.xml +++ b/core/res/res/drawable/ic_doc_font.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M13 6l-1.8 0 -4.7 12 2.1 0 1 -2.7 4.8 0 1 2.7 2.1 0L13 6Zm-2.6 7.2l1.6 -4.5 1.7 4.5 -3.3 0zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_doc_generic.xml b/core/res/res/drawable/ic_doc_generic.xml index 006dfba4a3bb5..297b77aec39dc 100644 --- a/core/res/res/drawable/ic_doc_generic.xml +++ b/core/res/res/drawable/ic_doc_generic.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M14.3 2L7 2C5.3 2 4 3.3 4 4.9L4 19.1C4 20.7 5.3 22 7 22l10 0c1.7 0 3 -1.3 3 -2.9L20 7.4 14.3 2ZM18 19.1C18 19.6 17.6 20 17 20L7 20C6.4 20 6 19.6 6 19.1L6 4.9C6 4.4 6.4 4 7 4l5.3 0 0 5.4 5.7 0 0 9.7z" /> diff --git a/core/res/res/drawable/ic_doc_image.xml b/core/res/res/drawable/ic_doc_image.xml index 23953f71fd5ea..f4b37df9f24ad 100644 --- a/core/res/res/drawable/ic_doc_image.xml +++ b/core/res/res/drawable/ic_doc_image.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M14.1 11.4l-3 4.6 -2.1 -3.1 -3 4.6 12 0 -3.9 -6.1 0 0zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z" /> diff --git a/core/res/res/drawable/ic_doc_pdf.xml b/core/res/res/drawable/ic_doc_pdf.xml index b2d01938c480d..d073c7420365a 100644 --- a/core/res/res/drawable/ic_doc_pdf.xml +++ b/core/res/res/drawable/ic_doc_pdf.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M8 9l-2.5 0 0 6 1.5 0 0 -2 1 0c0.8 0 1.5 -0.7 1.5 -1.5l0 -1C9.5 9.7 8.8 9 8 9Zm0 2.5l-1 0 0 -1 1 0 0 1zm9 -1l0 1 1.5 0 0 1.5 -1.5 0 0 2 -1.5 0 0 -6 4 0 0 1.5 -2.5 0zM13 9l-2.5 0 0 6 2.5 0c0.8 0 1.5 -0.7 1.5 -1.5l0 -3C14.5 9.7 13.8 9 13 9Zm0 4.5l-1 0 0 -3 1 0 0 3zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_doc_powerpoint.xml b/core/res/res/drawable/ic_doc_powerpoint.xml index aa5bfc8186925..547355cd3411f 100644 --- a/core/res/res/drawable/ic_doc_powerpoint.xml +++ b/core/res/res/drawable/ic_doc_powerpoint.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M15.2 7.8C14.5 7.2 13.9 6.9 12.3 6.9l-4.4 0 0 10.3 2.1 0 0 -3.6 2.4 0c1.6 0 2.3 -0.3 2.9 -0.9 0.7 -0.6 1 -1.3 1 -2.5C16.2 9.1 15.8 8.4 15.2 7.8Zm-1.8 3.6c-0.2 0.2 -0.6 0.5 -1.3 0.5l-2.2 0 0 -3.4 2.1 0c0.6 0 1.1 0.2 1.4 0.6 0.4 0.3 0.5 0.7 0.5 1.2 0 0.5 -0.1 0.9 -0.5 1.1zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_doc_presentation.xml b/core/res/res/drawable/ic_doc_presentation.xml index 7937bc12aa3b9..1ed45d7ab9674 100644 --- a/core/res/res/drawable/ic_doc_presentation.xml +++ b/core/res/res/drawable/ic_doc_presentation.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M18 15l-12 0 0 -6 12 0 0 6zm1 6l-14 0C3.3 21 2 19.7 2 18L2 6C2 4.3 3.3 3 5 3l14 0c1.7 0 3 1.3 3 3l0 12c0 1.7 -1.3 3 -3 3zM5 5C4.4 5 4 5.4 4 6l0 12c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 6C20 5.4 19.6 5 19 5L5 5Z"/> diff --git a/core/res/res/drawable/ic_doc_spreadsheet.xml b/core/res/res/drawable/ic_doc_spreadsheet.xml index 1663c0a1ae1bb..1ad2b838d2a21 100644 --- a/core/res/res/drawable/ic_doc_spreadsheet.xml +++ b/core/res/res/drawable/ic_doc_spreadsheet.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.5 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Zm13.5 7.1l-7.4 0 0 7.4 -1.9 0 0 -7.4 -3.7 0 0 -1.9 3.7 0 0 -3.7 1.9 0 0 3.7 7.4 0 0 1.9z"/> diff --git a/core/res/res/drawable/ic_doc_text.xml b/core/res/res/drawable/ic_doc_text.xml index 7fc04e811b1cc..3dcca15f0575b 100644 --- a/core/res/res/drawable/ic_doc_text.xml +++ b/core/res/res/drawable/ic_doc_text.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M16 14l-8 0 0 -2 8 0 0 2zm0 4l-8 0 0 -2 8 0 0 2zM14.3 2L7 2C5.3 2 4 3.3 4 4.9L4 19.1C4 20.7 5.3 22 7 22l10 0c1.7 0 3 -1.3 3 -2.9L20 7.4 14.3 2ZM18 19.1C18 19.6 17.6 20 17 20L7 20C6.4 20 6 19.6 6 19.1L6 4.9C6 4.4 6.4 4 7 4l5.3 0 0 5.4 5.7 0 0 9.7z" /> diff --git a/core/res/res/drawable/ic_doc_video.xml b/core/res/res/drawable/ic_doc_video.xml index ad4dae89ccdc0..c70cef2a16757 100644 --- a/core/res/res/drawable/ic_doc_video.xml +++ b/core/res/res/drawable/ic_doc_video.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M5.8 10L20 10 20 18 4 18 4 6.5M22 4L18 4 20 8 17 8 15 4 13 4 15 8 12 8 10 4 8 4 10 8 7 8 5 4 4 4C2.9 4 2 4.9 2 6l0 12c0 1.1 0.9 2 2 2l16 0c1.1 0 2 -0.9 2 -2l0 -14 0 0z" /> diff --git a/core/res/res/drawable/ic_doc_word.xml b/core/res/res/drawable/ic_doc_word.xml index 7a3a0ec9b5e02..cdcef939b55fb 100644 --- a/core/res/res/drawable/ic_doc_word.xml +++ b/core/res/res/drawable/ic_doc_word.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M16.1 6.9l-1.4 7 -1.8 -7 -1.6 0 -1.8 7 -1.4 -7 -2 0 2.5 10.3 1.7 0 1.9 -7.1 1.9 7 0 0.1 1.7 0L18 7l0 -0.2 -1.9 0.1 0 0zM19 22L5 22C3.3 22 2 20.7 2 19L2 5C2 3.3 3.3 2 5 2l14 0c1.7 0 3 1.3 3 3l0 14c0 1.7 -1.3 3 -3 3zM5 4C4.4 4 4 4.4 4 5l0 14c0 0.6 0.4 1 1 1l14 0c0.6 0 1 -0.4 1 -1L20 5C20 4.4 19.6 4 19 4L5 4Z"/> diff --git a/core/res/res/drawable/ic_link.xml b/core/res/res/drawable/ic_link.xml new file mode 100644 index 0000000000000..97322a4f24bdf --- /dev/null +++ b/core/res/res/drawable/ic_link.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/res/res/drawable/ic_livedisplay_auto.xml b/core/res/res/drawable/ic_livedisplay_auto.xml new file mode 100644 index 0000000000000..39d1f8cc62052 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_auto.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_livedisplay_day.xml b/core/res/res/drawable/ic_livedisplay_day.xml new file mode 100644 index 0000000000000..f454d81f6d4b6 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_day.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_livedisplay_night.xml b/core/res/res/drawable/ic_livedisplay_night.xml new file mode 100644 index 0000000000000..88a6764c748c3 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_night.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_livedisplay_notif.xml b/core/res/res/drawable/ic_livedisplay_notif.xml new file mode 100644 index 0000000000000..a7cb8c69646b9 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_notif.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_livedisplay_off.xml b/core/res/res/drawable/ic_livedisplay_off.xml new file mode 100644 index 0000000000000..f454d81f6d4b6 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_off.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_livedisplay_outdoor.xml b/core/res/res/drawable/ic_livedisplay_outdoor.xml new file mode 100644 index 0000000000000..66ead51185c25 --- /dev/null +++ b/core/res/res/drawable/ic_livedisplay_outdoor.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_pocket_lock.xml b/core/res/res/drawable/ic_pocket_lock.xml new file mode 100644 index 0000000000000..3494a8f38b840 --- /dev/null +++ b/core/res/res/drawable/ic_pocket_lock.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/res/res/drawable/ic_sleep.xml b/core/res/res/drawable/ic_sleep.xml new file mode 100644 index 0000000000000..1dddd7baf35e5 --- /dev/null +++ b/core/res/res/drawable/ic_sleep.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/core/res/res/drawable/perm_group_location.xml b/core/res/res/drawable/perm_group_location.xml index a87fc0dc43df8..633610ae55f97 100644 --- a/core/res/res/drawable/perm_group_location.xml +++ b/core/res/res/drawable/perm_group_location.xml @@ -15,16 +15,16 @@ limitations under the License. --> + android:pathData="M 10 2 C 6.13 2 3 5.13 3 9 c 0 5.25 7 13 7 13 s 7 -7.75 7 -13 C 17 5.13 13.87 2 10 2 z M 5 9 c 0 -2.76 2.24 -5 5 -5 s 5 2.24 5 5 c 0 2.88 -2.88 7.19 -5 9.88 C 7.92 16.21 5 11.85 5 9 z"/> + android:pathData="M 10 9 m -2.5 0 a 2.5 2.5 0 1 1 5 0 a 2.5 2.5 0 1 1 -5 0"/> diff --git a/core/res/res/drawable/pocket_mode_img.xml b/core/res/res/drawable/pocket_mode_img.xml new file mode 100644 index 0000000000000..81bb727f0bd3d --- /dev/null +++ b/core/res/res/drawable/pocket_mode_img.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 4f016bcc2e83e..da36512cf38f2 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml index c690ed4e0e9ae..181f0014c2517 100644 --- a/core/res/res/drawable/progress_large_white.xml +++ b/core/res/res/drawable/progress_large_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index eb1bd50d17d73..74fb0b05a836f 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml index b4f9b318a902a..be4059b454abd 100644 --- a/core/res/res/drawable/progress_medium_white.xml +++ b/core/res/res/drawable/progress_medium_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e0ee5e47d8305..25956f0770e40 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index 8cfba864b5b2c..cc6fd1ffb2c40 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml index 8cfba864b5b2c..cc6fd1ffb2c40 100644 --- a/core/res/res/drawable/progress_small_white.xml +++ b/core/res/res/drawable/progress_small_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml index 31a77c30cf2a6..82869cdd34356 100644 --- a/core/res/res/drawable/search_spinner.xml +++ b/core/res/res/drawable/search_spinner.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_20" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="60" + android:frameDuration="17" /> diff --git a/core/res/res/drawable/seekbar_progress.xml b/core/res/res/drawable/seekbar_progress.xml new file mode 100644 index 0000000000000..944d2bfc1d53c --- /dev/null +++ b/core/res/res/drawable/seekbar_progress.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/core/res/res/drawable/seekbar_progress_drawable.xml b/core/res/res/drawable/seekbar_progress_drawable.xml new file mode 100644 index 0000000000000..91e4771eeab8d --- /dev/null +++ b/core/res/res/drawable/seekbar_progress_drawable.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + diff --git a/core/res/res/drawable/stat_sys_download.xml b/core/res/res/drawable/stat_sys_download.xml index 77ecf8588b80a..8c08d04dd69d8 100644 --- a/core/res/res/drawable/stat_sys_download.xml +++ b/core/res/res/drawable/stat_sys_download.xml @@ -3,6 +3,7 @@ /* //device/apps/common/res/drawable/status_icon_background.xml ** ** Copyright 2008, The Android Open Source Project +** Copyright 2013, SlimRoms Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -20,11 +21,24 @@ - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/drawable/stat_sys_upload.xml b/core/res/res/drawable/stat_sys_upload.xml index a9d960950761f..b5f5c11599dab 100644 --- a/core/res/res/drawable/stat_sys_upload.xml +++ b/core/res/res/drawable/stat_sys_upload.xml @@ -3,6 +3,7 @@ /* //device/apps/common/res/drawable/status_icon_background.xml ** ** Copyright 2008, The Android Open Source Project +** Copyright 2013, SlimRoms Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -20,11 +21,24 @@ - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml index a8cdef6d05f13..34987394b2ece 100644 --- a/core/res/res/drawable/toast_frame.xml +++ b/core/res/res/drawable/toast_frame.xml @@ -17,7 +17,7 @@ --> - + diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index 33d6fa2862f76..70905ca19192d 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -45,7 +45,7 @@ android:textAppearance="?attr/textAppearanceSmall" android:textColor="?attr/textColorPrimary" android:textSize="12sp" - android:fontFamily="sans-serif-condensed" + android:fontFamily="@*android:string/config_bodyFontFamily" android:gravity="top|center_horizontal" android:minLines="2" android:maxLines="2" diff --git a/core/res/res/layout/accessibility_enable_service_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml index 01ef10177c5a3..fc6f498375193 100644 --- a/core/res/res/layout/accessibility_enable_service_warning.xml +++ b/core/res/res/layout/accessibility_enable_service_warning.xml @@ -51,7 +51,7 @@ android:gravity="center" android:textSize="20sp" android:textColor="?android:attr/textColorPrimary" - android:fontFamily="google-sans-medium"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily" /> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml index 178505c264a46..b1510fdcb93dd 100644 --- a/core/res/res/layout/alert_dialog_material.xml +++ b/core/res/res/layout/alert_dialog_material.xml @@ -54,7 +54,7 @@ android:layout_height="wrap_content" android:paddingEnd="?attr/dialogPreferredPadding" android:paddingStart="?attr/dialogPreferredPadding" - style="@style/TextAppearance.Material.Subhead" /> + style="@style/TextAppearance.DeviceDefault.Subhead" /> +