("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 extends View> constructor = sConstructorMap.get(name);
- if (constructor != null && !verifyClassLoader(constructor)) {
- constructor = null;
- sConstructorMap.remove(name);
- }
+ String prefixedName = prefix != null ? (prefix + name) : name;
Class extends View> 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 extends View> 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