Merge tag 'android-14.0.0_r61' into leaf-3.2
Android 14.0.0 Release 61 (AP2A.240805.005.F1)
* tag 'android-14.0.0_r61': (21 commits)
Move showing keyguard after the UserSwitchObservers.
Move showing keyguard after the UserSwitchObservers.
Revert "Move showing keyguard after the UserSwitchObservers."
Move showing keyguard after the UserSwitchObservers.
Remove Dependency#get call from ToggleSeekBar.
Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS
Remove Dependency#get call from ToggleSeekBar.
Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS
Remove Dependency#get call from ToggleSeekBar.
Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS
[RESTRICT AUTOMERGE] Messaging child requestLayout
Revert "Security fix for VPN app killable via lockscreen."
Revert "Security fix for VPN app killable via lockscreen."
Security fix for VPN app killable via lockscreen.
Security fix for VPN app killable via lockscreen.
Ensure device_owners2.xml is always written.
Add unit test to test data overflow when using BinaryXmlSerializer
Restrict USB poups while setup is in progress
Rate limiting PiP aspect ratio change request
Fix READ/WRITE operation access issues on Restricted appOps.
...
Change-Id: Ia09ed2bf2bae9345927e0cb7186737aefdce528a
diff --git a/Android.bp b/Android.bp
index 8d7ab98..4b10c78 100644
--- a/Android.bp
+++ b/Android.bp
@@ -265,6 +265,8 @@
"com.android.sysprop.init",
"com.android.sysprop.localization",
"PlatformProperties",
+ "SurfaceFlingerProperties",
+ "vendor.lineage.health-V1-java",
],
sdk_version: "core_platform",
installable: false,
diff --git a/core/api/current.txt b/core/api/current.txt
index 62980ed..642c670 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -98,6 +98,7 @@
field public static final String EXECUTE_APP_ACTION = "android.permission.EXECUTE_APP_ACTION";
field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";
+ field public static final String FAKE_PACKAGE_SIGNATURE = "android.permission.FAKE_PACKAGE_SIGNATURE";
field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
field public static final String FOREGROUND_SERVICE_CAMERA = "android.permission.FOREGROUND_SERVICE_CAMERA";
field public static final String FOREGROUND_SERVICE_CONNECTED_DEVICE = "android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE";
@@ -342,6 +343,7 @@
field public static final String CALL_LOG = "android.permission-group.CALL_LOG";
field public static final String CAMERA = "android.permission-group.CAMERA";
field public static final String CONTACTS = "android.permission-group.CONTACTS";
+ field public static final String FAKE_PACKAGE = "android.permission-group.FAKE_PACKAGE";
field public static final String LOCATION = "android.permission-group.LOCATION";
field public static final String MICROPHONE = "android.permission-group.MICROPHONE";
field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES";
@@ -37520,6 +37522,7 @@
field public static final String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final String SETTINGS_CLASSNAME = "settings_classname";
field public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
+ field public static final String TETHERING_ALLOW_VPN_UPSTREAMS = "tethering_allow_vpn_upstreams";
field public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
field @Deprecated public static final String TTS_DEFAULT_COUNTRY = "tts_default_country";
field @Deprecated public static final String TTS_DEFAULT_LANG = "tts_default_lang";
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1b0da05..234815e 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1109,6 +1109,10 @@
Documentation mentions 'TODO'
+UnflaggedApi: android.Manifest.permission#FAKE_PACKAGE_SIGNATURE:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.FAKE_PACKAGE_SIGNATURE
+UnflaggedApi: android.Manifest.permission_group#FAKE_PACKAGE:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission_group.FAKE_PACKAGE
UnflaggedApi: android.R.color#on_surface_disabled_material:
New API must be flagged with @FlaggedApi: field android.R.color.on_surface_disabled_material
UnflaggedApi: android.R.color#outline_disabled_material:
diff --git a/core/java/android/app/AppLockData.aidl b/core/java/android/app/AppLockData.aidl
new file mode 100644
index 0000000..073d1ef
--- /dev/null
+++ b/core/java/android/app/AppLockData.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable AppLockData;
\ No newline at end of file
diff --git a/core/java/android/app/AppLockData.java b/core/java/android/app/AppLockData.java
new file mode 100644
index 0000000..69d5fed
--- /dev/null
+++ b/core/java/android/app/AppLockData.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class to hold package level information about an
+ * application protected with app lock.
+ *
+ * @hide
+ */
+public final class AppLockData implements Parcelable {
+
+ public static final Parcelable.Creator<AppLockData> CREATOR =
+ new Parcelable.Creator<AppLockData>() {
+
+ @Override
+ public AppLockData createFromParcel(Parcel in) {
+ return new AppLockData(in);
+ }
+
+ @Override
+ public AppLockData[] newArray(int size) {
+ return new AppLockData[size];
+ }
+ };
+
+ private final String mPackageName;
+ private final boolean mShouldRedactNotification;
+ private final boolean mHideFromLauncher;
+
+ /** @hide */
+ public AppLockData(
+ @NonNull final String packageName,
+ final boolean shouldRedactNotification,
+ final boolean hideFromLauncher
+ ) {
+ mPackageName = packageName;
+ mShouldRedactNotification = shouldRedactNotification;
+ mHideFromLauncher = hideFromLauncher;
+ }
+
+ private AppLockData(final Parcel in) {
+ mPackageName = in.readString();
+ mShouldRedactNotification = in.readBoolean();
+ mHideFromLauncher = in.readBoolean();
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public boolean getShouldRedactNotification() {
+ return mShouldRedactNotification;
+ }
+
+ public boolean getHideFromLauncher() {
+ return mHideFromLauncher;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel parcel, final int flags) {
+ parcel.writeString(mPackageName);
+ parcel.writeBoolean(mShouldRedactNotification);
+ parcel.writeBoolean(mHideFromLauncher);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "AppLockData[ packageName = " + mPackageName +
+ ", shouldRedactNotification = " + mShouldRedactNotification +
+ ", hideFromLauncher = " + mHideFromLauncher + " ]";
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java
new file mode 100644
index 0000000..3cd0e60
--- /dev/null
+++ b/core/java/android/app/AppLockManager.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.RequiresPermission;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+@SystemService(Context.APP_LOCK_SERVICE)
+public final class AppLockManager {
+
+ /** @hide */
+ public static final long DEFAULT_TIMEOUT = 10 * 1000;
+
+ /** @hide */
+ public static final boolean DEFAULT_BIOMETRICS_ALLOWED = true;
+
+ /** @hide */
+ public static final boolean DEFAULT_REDACT_NOTIFICATION = false;
+
+ /** @hide */
+ public static final boolean DEFAULT_HIDE_IN_LAUNCHER = false;
+
+ /**
+ * Intent action for starting credential activity in SystemUI.
+ * @hide
+ */
+ public static final String ACTION_UNLOCK_APP = "android.app.action.UNLOCK_APP";
+
+ /**
+ * Intent extra to indicate whether usage of biometrics is allowed.
+ * @hide
+ */
+ public static final String EXTRA_ALLOW_BIOMETRICS = "android.app.AppLockManager.ALLOW_BIOMETRICS";
+
+ /**
+ * Intent extra for the name of the application to unlock.
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE_LABEL = "android.app.AppLockManager.PACKAGE_LABEL";
+
+ private final Context mContext;
+ private final IAppLockManagerService mService;
+
+ /** @hide */
+ AppLockManager(Context context, IAppLockManagerService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Add an application to be protected. Package should be an user
+ * installed application or a system app whitelisted in
+ * {@link config_appLockAllowedSystemApps}.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the package name of the app to add.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void addPackage(@NonNull String packageName) {
+ try {
+ mService.addPackage(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove an application from the protected packages list.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the package name of the app to remove.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void removePackage(@NonNull String packageName) {
+ try {
+ mService.removePackage(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current auto lock timeout.
+ *
+ * @param userId the user id given by the caller.
+ * @return the timeout in milliseconds if configuration for
+ * current user exists, -1 otherwise.
+ * @hide
+ */
+ @UserHandleAware
+ public long getTimeout() {
+ try {
+ return mService.getTimeout(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set auto lock timeout.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param timeout the timeout in milliseconds. Must be >= 5.
+ * @param userId the user id given by the caller.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void setTimeout(long timeout) {
+ try {
+ mService.setTimeout(timeout, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get all the packages protected with app lock.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @return a unique list of {@link AppLockData} of the protected apps.
+ * @hide
+ */
+ @UserHandleAware
+ @NonNull
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public List<AppLockData> getPackageData() {
+ try {
+ return mService.getPackageData(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether notification content should be redacted for a package
+ * in locked state. Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the package name.
+ * @param shouldRedactNotification true to hide notification content.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void setShouldRedactNotification(@NonNull String packageName, boolean shouldRedactNotification) {
+ try {
+ mService.setShouldRedactNotification(packageName, shouldRedactNotification, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether to allow unlocking with biometrics.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param biometricsAllowed whether to use biometrics.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void setBiometricsAllowed(boolean biometricsAllowed) {
+ try {
+ mService.setBiometricsAllowed(biometricsAllowed, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Check whether biometrics is allowed for unlocking.
+ *
+ * @return true if biometrics will be used for unlocking, false otheriwse.
+ * @hide
+ */
+ @UserHandleAware
+ public boolean isBiometricsAllowed() {
+ try {
+ return mService.isBiometricsAllowed(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unlock a package following authentication with credentials.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the name of the package to unlock.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void unlockPackage(@NonNull String packageName) {
+ try {
+ mService.unlockPackage(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Hide or unhide an application from launcher.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the name of the package to hide or unhide.
+ * @param hide whether to hide or not.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ public void setPackageHidden(@NonNull String packageName, boolean hide) {
+ try {
+ mService.setPackageHidden(packageName, hide, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the list of applications hidden from launcher.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @return list of package names of the hidden apps.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ @NonNull
+ public List<String> getHiddenPackages() {
+ try {
+ return mService.getHiddenPackages(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 18dc1ce..5b00820 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -28,6 +28,9 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
+import android.graphics.Typeface;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build;
import android.os.LocaleList;
import android.os.Trace;
import android.util.DisplayMetrics;
@@ -179,6 +182,7 @@
final Application app = mActivityThread.getApplication();
final Resources appResources = app.getResources();
+ Typeface.updateDefaultFont(appResources);
mResourcesManager.applyConfigurationToResources(config, compat);
updateLocaleListFromAppContext(app.getApplicationContext());
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7a95720..9ebdb22 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -966,4 +966,11 @@
*/
oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err);
int getBindingUidProcessState(int uid, in String callingPackage);
+
+ /**
+ * @hide
+ *
+ * Should disable touch if three fingers to screen shot is active?
+ */
+ boolean isSwipeToScreenshotGestureActive();
}
diff --git a/core/java/android/app/IAppLockManagerService.aidl b/core/java/android/app/IAppLockManagerService.aidl
new file mode 100644
index 0000000..5b552e6
--- /dev/null
+++ b/core/java/android/app/IAppLockManagerService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.app.AppLockData;
+
+/**
+ * Interface for managing app lock.
+ * @hide
+ */
+interface IAppLockManagerService {
+
+ void addPackage(in String packageName, in int userId);
+
+ void removePackage(in String packageName, in int userId);
+
+ long getTimeout(in int userId);
+
+ void setTimeout(in long timeout, in int userId);
+
+ List<AppLockData> getPackageData(in int userId);
+
+ void setShouldRedactNotification(in String packageName, in boolean secure, in int userId);
+
+ void setBiometricsAllowed(in boolean biometricsAllowed, in int userId);
+
+ boolean isBiometricsAllowed(in int userId);
+
+ void unlockPackage(in String packageName, in int userId);
+
+ void setPackageHidden(in String packageName, boolean hide, in int userId);
+
+ List<String> getHiddenPackages(in int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index db216b1..c364e858 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -63,6 +63,8 @@
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.gmscompat.AttestationHooks;
+
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1351,6 +1353,7 @@
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
+ AttestationHooks.initApplicationBeforeOnCreate(context);
return app;
}
@@ -1368,6 +1371,7 @@
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
+ AttestationHooks.initApplicationBeforeOnCreate(context);
return app;
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index b5e5074..20b4fd2 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -1306,9 +1306,11 @@
CharSequence pinStr = new String(password);
return LockscreenCredential.createPin(pinStr);
case PATTERN:
+ byte patternSize = new LockPatternUtils(mContext).getLockPatternSize(
+ mContext.getUserId());
List<LockPatternView.Cell> pattern =
- LockPatternUtils.byteArrayToPattern(password);
- return LockscreenCredential.createPattern(pattern);
+ LockPatternUtils.byteArrayToPattern(password, patternSize);
+ return LockscreenCredential.createPattern(pattern, patternSize);
default:
throw new IllegalArgumentException("Unknown lock type " + lockType);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d01626e..19c85df 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1620,6 +1620,17 @@
return new E2eeContactKeysManager(ctx);
}});
+ registerService(Context.APP_LOCK_SERVICE, AppLockManager.class,
+ new CachedServiceFetcher<AppLockManager>() {
+ @Override
+ public AppLockManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder binder = ServiceManager.getServiceOrThrow(
+ Context.APP_LOCK_SERVICE);
+ return new AppLockManager(ctx,
+ IAppLockManagerService.Stub.asInterface(binder));
+ }});
+
// DO NOT do a flag check like this unless the flag is read-only.
// (because this code is executed during preload in zygote.)
// If the flag is mutable, the check should be inside CachedServiceFetcher.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c7e5d88..c04ffe2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -44,6 +44,7 @@
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.AppLockManager;
import android.app.BroadcastOptions;
import android.app.GameManager;
import android.app.GrammaticalInflectionManager;
@@ -6594,6 +6595,14 @@
public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
/**
+ * {@link AppLockManager}.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String APP_LOCK_SERVICE = "app_lock";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7..a1f0e57 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3637,11 +3637,7 @@
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
}
- if (sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture,
- owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
- }
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
if (sa.getBoolean(
R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
@@ -4719,10 +4715,8 @@
* ratio set.
*/
private void setMaxAspectRatio(Package owner) {
- // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
- // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
- float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O
- ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+ // Start at an unlimited aspect ratio unless we get a more restrictive one
+ float maxAspectRatio = 0;
if (owner.applicationInfo.maxAspectRatio != 0) {
// Use the application max aspect ration as default if set.
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index f3efd89..a92373b 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -166,6 +166,20 @@
public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS";
/**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value representing the estimated battery full charge capacity in microampere-hours.
+ * {@hide}
+ */
+ public static final String EXTRA_MAXIMUM_CAPACITY = "android.os.extra.MAXIMUM_CAPACITY";
+
+ /**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value representing the battery full charge design capacity in microampere-hours.
+ * {@hide}
+ */
+ public static final String EXTRA_DESIGN_CAPACITY = "android.os.extra.DESIGN_CAPACITY";
+
+ /**
* Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
* Contains list of Bundles representing battery events
* @hide
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dbb6f92..763d7e2 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -170,4 +170,6 @@
const int GO_TO_SLEEP_REASON_MAX = 10;
const int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0;
+ // Lineage custom API
+ void rebootCustom(boolean confirm, String reason, boolean wait);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f4795f8..3dc11b4 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -857,6 +857,27 @@
public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
/**
+ * The value to pass as the 'reason' argument to reboot() to
+ * reboot into bootloader mode
+ * @hide
+ */
+ public static final String REBOOT_BOOTLOADER = "bootloader";
+
+ /**
+ * The value to pass as the 'reason' argument to reboot() to
+ * reboot into download mode
+ * @hide
+ */
+ public static final String REBOOT_DOWNLOAD = "download";
+
+ /**
+ * The value to pass as the 'reason' argument to reboot() to
+ * reboot into fastboot mode
+ * @hide
+ */
+ public static final String REBOOT_FASTBOOT = "fastboot";
+
+ /**
* The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
* the device.
* @hide
@@ -1878,6 +1899,24 @@
}
/**
+ * Reboot the device. Will not return if the reboot is successful.
+ * <p>
+ * Requires the {@link android.Manifest.permission#REBOOT} permission.
+ * </p>
+ *
+ * @param reason code to pass to the kernel (e.g., "recovery", "bootloader", "download") to
+ * request special boot modes, or null.
+ * @hide
+ */
+ public void rebootCustom(String reason) {
+ try {
+ mService.rebootCustom(false, reason, true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Reboot the device. Will not return if the reboot is successful.
* <p>
* Requires the {@link android.Manifest.permission#REBOOT} permission.
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fa9f03d..5056340 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -253,6 +253,9 @@
private List<SplitPermissionInfo> mSplitPermissionInfos;
+ private static String[] sLocationProviderPkgNames;
+ private static String[] sLocationExtraPkgNames;
+
/**
* Creates a new instance.
*
@@ -1356,6 +1359,16 @@
pkgNames.add(exemptedPackage);
}
}
+ for (String pkgName: sLocationProviderPkgNames) {
+ if (pkgName != null) {
+ pkgNames.add(pkgName);
+ }
+ }
+ for (String pkgName: sLocationExtraPkgNames) {
+ if (pkgName != null) {
+ pkgNames.add(pkgName);
+ }
+ }
return pkgNames;
}
@@ -1371,6 +1384,10 @@
for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
}
+ sLocationProviderPkgNames = context.getResources().getStringArray(
+ R.array.config_locationProviderPackageNames);
+ sLocationExtraPkgNames = context.getResources().getStringArray(
+ R.array.config_locationExtraPackageNames);
}
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 51585af..9cf9703 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5713,6 +5713,19 @@
public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";
/**
+ * Control the type of rotation which can be performed using the accelerometer
+ * if ACCELEROMETER_ROTATION is enabled.
+ * Value is a bitwise combination of
+ * 1 = 0 degrees (portrait)
+ * 2 = 90 degrees (left)
+ * 4 = 180 degrees (inverted portrait)
+ * 8 = 270 degrees (right)
+ * Setting to 0 is effectively orientation lock
+ * @hide
+ */
+ public static final String ACCELEROMETER_ROTATION_ANGLES = "accelerometer_rotation_angles";
+
+ /**
* Default screen rotation when no other policy applies.
* When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a
* preference, this rotation value will be used. Must be one of the
@@ -6134,6 +6147,13 @@
public static final String EGG_MODE = "egg_mode";
/**
+ * Swipe to screenshot gesture
+ * Also known as Three Fingers Screenshot.
+ * @hide
+ */
+ public static final String THREE_FINGER_GESTURE = "three_finger_gesture";
+
+ /**
* Setting to determine whether or not to show the battery percentage in the status bar.
* 0 - Don't show percentage
* 1 - Show percentage
@@ -6143,6 +6163,14 @@
public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
/**
+ * 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 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.
@@ -6165,6 +6193,39 @@
public static final String LOCALE_PREFERENCES = "locale_preferences";
/**
+ * Whether charging control should be enabled.
+ * The value is boolean (1 or 0).
+ * @hide
+ */
+ public static final String CHARGING_CONTROL_ENABLED = "charging_control_enabled";
+
+ /**
+ * Charging control mode, one of AUTO (1; default), CUSTOM (2), or LIMIT (3).
+ * @hide
+ */
+ public static final String CHARGING_CONTROL_MODE = "charging_control_mode";
+
+ /**
+ * Time when charging control is automatically activated in CUSTOM mode.
+ * The value is represented as seconds from midnight.
+ * @hide
+ */
+ public static final String CHARGING_CONTROL_START_TIME = "charging_control_start_time";
+
+ /**
+ * Target time when battery is fully charged in CUSTOM mode.
+ * The value is represented as seconds from midnight.
+ * @hide
+ */
+ public static final String CHARGING_CONTROL_TARGET_TIME = "charging_control_target_time";
+
+ /**
+ * Limit to stop charging.
+ * @hide
+ */
+ public static final String CHARGING_CONTROL_LIMIT = "charging_control_charging_limit";
+
+ /**
* Setting to enable camera flash notification feature.
* <ul>
* <li> 0 = Off
@@ -6195,6 +6256,40 @@
"screen_flash_notification_color_global";
/**
+ * Whether the HighTouchSensitivity is activated or not.
+ * 0 = off, 1 = on
+ * @hide
+ */
+ public static final String HIGH_TOUCH_SENSITIVITY_ENABLE =
+ "high_touch_sensitivity_enable";
+
+ /**
+ * 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";
+
+ /**
+ * Whether to enable taskbar.
+ * @hide
+ */
+ public static final String ENABLE_TASKBAR = "enable_taskbar";
+
+ /**
+ * Whether to scramble a pin unlock layout
+ * 0 = 0ff, 1 = on
+ * @hide
+ */
+ public static final String LOCKSCREEN_PIN_SCRAMBLE_LAYOUT =
+ "lockscreen_scramble_pin_layout";
+
+ /**
* 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
@@ -6394,6 +6489,24 @@
}
/**
+ * Whether to display reboot in the power menu
+ * @hide
+ */
+ public static final String POWERMENU_ADVANCED = "powermenu_advanced";
+
+ /**
+ * Whether to show power menu on lockscreen
+ * @hide
+ */
+ public static final String HIDE_POWERMENU_LOCKSCREEN = "hide_powermenu_lockscreen";
+
+ /**
+ * Enable/Disable fingerprint lockout
+ * @hide
+ */
+ public static final String FINGERPRINT_LOCKOUT = "fingerprint_lockout";
+
+ /**
* When to use Wi-Fi calling
*
* @see android.telephony.TelephonyManager.WifiCallingChoices
@@ -6622,6 +6735,12 @@
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
/**
+ * Whether to display 4G icon instead LTE
+ * @hide
+ */
+ public static final String SHOW_FOURG_ICON = "show_fourg_icon";
+
+ /**
* @deprecated Use
* {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead
*/
@@ -6657,6 +6776,16 @@
public static final String WIFI_WATCHDOG_PING_DELAY_MS = Secure.WIFI_WATCHDOG_PING_DELAY_MS;
/**
+ * 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.
+ */
+
+ /** @hide */
+ public static final String BACK_GESTURE_HEIGHT = "back_gesture_height";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS}
* instead
*/
@@ -6720,10 +6849,13 @@
@UnsupportedAppUsage
private static final HashSet<String> MOVED_TO_GLOBAL;
static {
- MOVED_TO_LOCK_SETTINGS = new HashSet<>(3);
+ MOVED_TO_LOCK_SETTINGS = new HashSet<>(6);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_SIZE);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_DOTS_VISIBLE);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_SHOW_ERROR_PATH);
MOVED_TO_GLOBAL = new HashSet<>();
MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
@@ -8239,6 +8371,24 @@
LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
/**
+ * Determines the width and height of the LockPatternView widget
+ * @hide
+ */
+ public static final String LOCK_PATTERN_SIZE = "lock_pattern_size";
+
+ /**
+ * Whether lock pattern will show dots (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String LOCK_DOTS_VISIBLE = "lock_pattern_dotsvisible";
+
+ /**
+ * Whether lockscreen error pattern is visible (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String LOCK_SHOW_ERROR_PATH = "lock_pattern_show_error_path";
+
+ /**
* This preference allows the device to be locked given time after screen goes off,
* subject to current DeviceAdmin policy limits.
* @hide
@@ -11864,6 +12014,12 @@
public static final String TAP_GESTURE = "tap_gesture";
/**
+ * Whether tethering is allowed to use VPN upstreams
+ */
+ @SuppressLint({"NoSettingsProvider", "UnflaggedApi"})
+ public static final String TETHERING_ALLOW_VPN_UPSTREAMS = "tethering_allow_vpn_upstreams";
+
+ /**
* Controls whether the people strip is enabled.
* @hide
*/
@@ -12145,6 +12301,18 @@
"com.android.server.display.HBM_SETTING_KEY";
/**
+ * boolean value. toggles swipe up hint in gestural nav mode
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_HINT = "navigation_bar_hint";
+
+ /**
+ * Whether to use black theme for dark mode
+ * @hide
+ */
+ public static final String BERRY_BLACK_THEME = "berry_black_theme";
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -12415,6 +12583,68 @@
public static final String STYLUS_POINTER_ICON_ENABLED = "stylus_pointer_icon_enabled";
/**
+ * 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";
+
+ /**
+ * Quicksettings tile shape
+ * @hide
+ */
+ public static final String QS_TILE_SHAPE = "qs_tile_shape";
+
+ /**
+ * Quicksettings UI Style
+ * @hide
+ */
+ public static final String QS_STYLE_ROUND = "qs_style_round";
+
+ /**
+ * Quicksettings columns
+ * @hide
+ */
+ public static final String QS_NUM_COLUMNS = "qs_num_columns";
+
+ /**
+ * Quicksettings columns landscape
+ * @hide
+ */
+ public static final String QS_NUM_COLUMNS_LANDSCAPE = "qs_num_columns_landscape";
+
+ /**
+ * Quick QS columns
+ * @hide
+ */
+ public static final String QQS_NUM_COLUMNS = "qqs_num_columns";
+
+ /**
+ * Quick QS columns landscape
+ * @hide
+ */
+ public static final String QQS_NUM_COLUMNS_LANDSCAPE = "qqs_num_columns_landscape";
+
+ /**
+ * Brightness slider styles
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi")
+ public static final String BRIGHTNESS_SLIDER_STYLE = "brightness_slider_style";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index d821074..759f8bc 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -71,10 +71,13 @@
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 @@
this.overrideGroupKey = overrideGroupKey;
this.key = key();
this.groupKey = groupKey();
+ mIsContentSecure = isContentSecure;
}
/**
@@ -137,6 +141,7 @@
}
this.key = key();
this.groupKey = groupKey();
+ mIsContentSecure = in.readBoolean();
}
/**
@@ -254,6 +259,7 @@
} else {
out.writeInt(0);
}
+ out.writeBoolean(mIsContentSecure);
}
public int describeContents() {
@@ -295,7 +301,8 @@
public 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;
}
@@ -579,4 +586,24 @@
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.
+ * @hide
+ */
+ public boolean getIsContentSecure() {
+ return mIsContentSecure;
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a7cb169..41cce5e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7552,6 +7552,10 @@
mLastClickToolType = event.getToolType(event.getActionIndex());
}
+ if (event.getPointerCount() == 3 && isSwipeToScreenshotGestureActive()) {
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ }
+
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
// If the event was fully handled by the handwriting initiator, then don't dispatch it
@@ -12575,6 +12579,14 @@
mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage);
}
+ private boolean isSwipeToScreenshotGestureActive() {
+ try {
+ return ActivityManager.getService().isSwipeToScreenshotGestureActive();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* Get the value of mIsFrameRatePowerSavingsBalanced
* Can be used to checked if toolkit dVRR feature is enabled. The default value is true.
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d54addb..376707d 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1531,6 +1531,7 @@
final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
MATCH_PARENT, height);
backgroundView.addView(contentView, listParams);
+ backgroundView.setClipToOutline(true);
return backgroundView;
}
diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
index 5628b7e..b71d7ae 100644
--- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
+++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
@@ -89,7 +89,8 @@
final MediaRouter router = context.getSystemService(MediaRouter.class);
MediaRouter.RouteInfo route = router.getSelectedRoute();
- if (route.isDefault() || !route.matchesTypes(routeTypes)) {
+ if (route.isDefault() || !route.matchesTypes(routeTypes)
+ || route.getStatusCode() == MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE) {
final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme,
showProgressBarWhenEmpty);
d.setRouteTypes(routeTypes);
diff --git a/core/java/com/android/internal/gmscompat/AttestationHooks.java b/core/java/com/android/internal/gmscompat/AttestationHooks.java
new file mode 100644
index 0000000..28bcba1
--- /dev/null
+++ b/core/java/com/android/internal/gmscompat/AttestationHooks.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * (C) 2023 ArrowOS
+ * (C) 2023 The LibreMobileOS Foundation
+ * (C) 2024 The LeafOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package com.android.internal.gmscompat;
+
+import android.app.ActivityTaskManager;
+import android.app.Application;
+import android.app.TaskStackListener;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/** @hide */
+public final class AttestationHooks {
+ private static final String TAG = AttestationHooks.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String PACKAGE_GMS = "com.google.android.gms";
+ private static final String PROCESS_UNSTABLE = "com.google.android.gms.unstable";
+ private static final String SAMSUNG = "com.samsung.android.";
+ private static final String DATA_FILE = "gms_certified_props.json";
+
+ private static final boolean SPOOF_GMS =
+ SystemProperties.getBoolean("persist.sys.spoof.gms", true);
+
+ private static final ComponentName GMS_ADD_ACCOUNT_ACTIVITY =
+ ComponentName.unflattenFromString(
+ "com.google.android.gms/.auth.uiflows.minutemaid.MinuteMaidActivity");
+
+ private static volatile String sProcessName;
+ private static volatile boolean sIsGms = false;
+
+ private AttestationHooks() {}
+
+ private static void setBuildField(String key, String value) {
+ try {
+ // Unlock
+ Class clazz = Build.class;
+ if (key.startsWith("VERSION:")) {
+ clazz = Build.VERSION.class;
+ key = key.substring(8);
+ }
+ Field field = clazz.getDeclaredField(key);
+ field.setAccessible(true);
+
+ // Edit
+ if (field.getType().equals(Long.TYPE)) {
+ field.set(null, Long.parseLong(value));
+ } else if (field.getType().equals(Integer.TYPE)) {
+ field.set(null, Integer.parseInt(value));
+ } else {
+ field.set(null, value);
+ }
+
+ // Lock
+ field.setAccessible(false);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to spoof Build." + key, e);
+ }
+ }
+
+ public static void initApplicationBeforeOnCreate(Context context) {
+ final String packageName = context.getPackageName();
+ final String processName = Application.getProcessName();
+
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(processName)) {
+ Log.e(TAG, "Null package or process name");
+ return;
+ }
+
+ if (SPOOF_GMS && PACKAGE_GMS.equals(packageName)) {
+ setBuildField("TIME", String.valueOf(System.currentTimeMillis()));
+ if (PROCESS_UNSTABLE.equals(processName)) {
+ sProcessName = processName;
+ sIsGms = true;
+ setGmsCertifiedProps();
+ }
+ }
+
+ // Samsung apps like SmartThings, Galaxy Wearable crashes
+ // on samsung devices running AOSP
+ if (packageName.startsWith(SAMSUNG)) {
+ setBuildField("BRAND", "google");
+ setBuildField("MANUFACTURER", "google");
+ }
+ }
+
+ private static void setGmsCertifiedProps() {
+ File dataFile = new File(Environment.getDataSystemDirectory(), DATA_FILE);
+ String savedProps = readFromFile(dataFile);
+
+ if (TextUtils.isEmpty(savedProps)) {
+ Log.e(TAG, "No props found to spoof");
+ return;
+ }
+
+ dlog("Found props");
+ final boolean was = isGmsAddAccountActivityOnTop();
+ final TaskStackListener taskStackListener =
+ new TaskStackListener() {
+ @Override
+ public void onTaskStackChanged() {
+ final boolean is = isGmsAddAccountActivityOnTop();
+ if (is ^ was) {
+ // process will restart automatically later
+ dlog(
+ "GmsAddAccountActivityOnTop is:"
+ + is
+ + " was:"
+ + was
+ + ", killing myself!");
+ Process.killProcess(Process.myPid());
+ }
+ }
+ };
+ if (!was) {
+ try {
+ JSONObject parsedProps = new JSONObject(savedProps);
+ Iterator<String> keys = parsedProps.keys();
+
+ while (keys.hasNext()) {
+ String key = keys.next();
+ String value = parsedProps.getString(key);
+ dlog(key + ": " + value);
+
+ setBuildField(key, value);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Error parsing JSON data", e);
+ }
+ } else {
+ dlog("Skip spoofing build for GMS, because GmsAddAccountActivityOnTop");
+ }
+ try {
+ ActivityTaskManager.getService().registerTaskStackListener(taskStackListener);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register task stack listener!", e);
+ }
+ }
+
+ private static boolean isGmsAddAccountActivityOnTop() {
+ try {
+ final ActivityTaskManager.RootTaskInfo focusedTask =
+ ActivityTaskManager.getService().getFocusedRootTaskInfo();
+ return focusedTask != null
+ && focusedTask.topActivity != null
+ && focusedTask.topActivity.equals(GMS_ADD_ACCOUNT_ACTIVITY);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to get top activity!", e);
+ }
+ return false;
+ }
+
+ public static boolean shouldBypassTaskPermission(Context context) {
+ // GMS doesn't have MANAGE_ACTIVITY_TASKS permission
+ final int callingUid = Binder.getCallingUid();
+ final int gmsUid;
+ try {
+ gmsUid = context.getPackageManager().getApplicationInfo(PACKAGE_GMS, 0).uid;
+ dlog("shouldBypassTaskPermission: gmsUid:" + gmsUid + " callingUid:" + callingUid);
+ } catch (Exception e) {
+ return false;
+ }
+ return gmsUid == callingUid;
+ }
+
+ 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();
+ }
+ }
+
+ private static String readFromFile(File file) {
+ StringBuilder content = new StringBuilder();
+
+ if (file.exists()) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ content.append(line);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading from file", e);
+ }
+ }
+ return content.toString();
+ }
+
+ private static void dlog(String message) {
+ if (DEBUG) Log.d(TAG, "[" + sProcessName + "] " + message);
+ }
+}
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 0000000..1e55bfb
--- /dev/null
+++ b/core/java/com/android/internal/lineage/app/LineageContextConstants.java
@@ -0,0 +1,45 @@
+/**
+ * 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 lineageos.health.HealthInterface} to access the Health interface.
+ *
+ * @see android.content.Context#getSystemService
+ * @see lineageos.health.HealthInterface
+ *
+ * @hide
+ */
+ public static final String LINEAGE_HEALTH_INTERFACE = "lineagehealth";
+
+}
diff --git a/core/java/com/android/internal/lineage/health/HealthInterface.java b/core/java/com/android/internal/lineage/health/HealthInterface.java
new file mode 100644
index 0000000..5fc7f1c
--- /dev/null
+++ b/core/java/com/android/internal/lineage/health/HealthInterface.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2023 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.health;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.lineage.app.LineageContextConstants;
+
+public class HealthInterface {
+ /**
+ * No config set. This value is invalid and does not have any effects
+ */
+ public static final int MODE_NONE = 0;
+
+ /**
+ * Automatic config
+ */
+ public static final int MODE_AUTO = 1;
+
+ /**
+ * Manual config mode
+ */
+ public static final int MODE_MANUAL = 2;
+
+ /**
+ * Limit config mode
+ */
+ public static final int MODE_LIMIT = 3;
+
+ private static final String TAG = "HealthInterface";
+ private static IHealthInterface sService;
+ private static HealthInterface sInstance;
+ private Context mContext;
+ private HealthInterface(Context context) {
+ Context appContext = context.getApplicationContext();
+ mContext = appContext == null ? context : appContext;
+ sService = getService();
+ }
+ /**
+ * Get or create an instance of the {@link lineageos.health.HealthInterface}
+ *
+ * @param context Used to get the service
+ * @return {@link HealthInterface}
+ */
+ public static synchronized HealthInterface getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new HealthInterface(context);
+ }
+ return sInstance;
+ }
+ /** @hide **/
+ public static IHealthInterface getService() {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(LineageContextConstants.LINEAGE_HEALTH_INTERFACE);
+ sService = IHealthInterface.Stub.asInterface(b);
+ if (sService == null) {
+ Log.e(TAG, "null health service, SAD!");
+ return null;
+ }
+ return sService;
+ }
+
+ /**
+ * @return true if service is valid
+ */
+ private boolean checkService() {
+ if (sService == null) {
+ Log.w(TAG, "not connected to LineageHardwareManagerService");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether charging control is supported
+ *
+ * @return true if charging control is supported
+ */
+ public boolean isChargingControlSupported() {
+ try {
+ return checkService() && sService.isChargingControlSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getLocalizedMessage(), e);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether charging control is supported
+ *
+ * @return true if charging control is supported
+ */
+ public static boolean isChargingControlSupported(Context context) {
+ try {
+ return getInstance(context).isChargingControlSupported();
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getLocalizedMessage(), e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the charging control enabled status
+ *
+ * @return whether charging control has been enabled
+ */
+ public boolean getEnabled() {
+ try {
+ return checkService() && sService.getChargingControlEnabled();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Set charging control enable status
+ *
+ * @param enabled whether charging control should be enabled
+ * @return true if the enabled status was successfully set
+ */
+ public boolean setEnabled(boolean enabled) {
+ try {
+ return checkService() && sService.setChargingControlEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the current charging control mode
+ *
+ * @return id of the charging control mode
+ */
+ public int getMode() {
+ try {
+ return checkService() ? sService.getChargingControlMode() : MODE_NONE;
+ } catch (RemoteException e) {
+ return MODE_NONE;
+ }
+ }
+
+ /**
+ * Selects the new charging control mode
+ *
+ * @param mode the new charging control mode
+ * @return true if the mode was successfully set
+ */
+ public boolean setMode(int mode) {
+ try {
+ return checkService() && sService.setChargingControlMode(mode);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the charging control start time
+ *
+ * @return the seconds of the day of the start time
+ */
+ public int getStartTime() {
+ try {
+ return checkService() ? sService.getChargingControlStartTime() : 0;
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the charging control start time
+ *
+ * @param time the seconds of the day of the start time
+ * @return true if the start time was successfully set
+ */
+ public boolean setStartTime(int time) {
+ try {
+ return checkService() && sService.setChargingControlStartTime(time);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the charging control target time
+ *
+ * @return the seconds of the day of the target time
+ */
+ public int getTargetTime() {
+ try {
+ return checkService() ? sService.getChargingControlTargetTime() : 0;
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the charging control target time
+ *
+ * @param time the seconds of the day of the target time
+ * @return true if the target time was successfully set
+ */
+ public boolean setTargetTime(int time) {
+ try {
+ return checkService() && sService.setChargingControlTargetTime(time);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the charging control limit
+ *
+ * @return the charging control limit
+ */
+ public int getLimit() {
+ try {
+ return checkService() ? sService.getChargingControlLimit() : 100;
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the charging control limit
+ *
+ * @param limit the charging control limit
+ * @return true if the limit was successfully set
+ */
+ public boolean setLimit(int limit) {
+ try {
+ return checkService() && sService.setChargingControlLimit(limit);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Resets the charging control setting to default
+ *
+ * @return true if the setting was successfully reset
+ */
+ public boolean reset() {
+ try {
+ return checkService() && sService.resetChargingControl();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns whether the device's battery control bypasses battery
+ *
+ * @return true if the charging control bypasses battery
+ */
+ public boolean allowFineGrainedSettings() {
+ try {
+ return checkService() && sService.allowFineGrainedSettings();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/lineage/health/IHealthInterface.aidl b/core/java/com/android/internal/lineage/health/IHealthInterface.aidl
new file mode 100644
index 0000000..c2e9759c
--- /dev/null
+++ b/core/java/com/android/internal/lineage/health/IHealthInterface.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2023 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.health;
+
+/** @hide */
+interface IHealthInterface {
+ boolean isChargingControlSupported();
+
+ boolean getChargingControlEnabled();
+ boolean setChargingControlEnabled(boolean enabled);
+
+ int getChargingControlMode();
+ boolean setChargingControlMode(int mode);
+
+ int getChargingControlStartTime();
+ boolean setChargingControlStartTime(int time);
+
+ int getChargingControlTargetTime();
+ boolean setChargingControlTargetTime(int time);
+
+ int getChargingControlLimit();
+ boolean setChargingControlLimit(int limit);
+
+ boolean resetChargingControl();
+ boolean allowFineGrainedSettings();
+}
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 95ecd47..2270034 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -2845,9 +2845,8 @@
* ratio set.
*/
private static void setMaxAspectRatio(ParsingPackage pkg) {
- // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
- // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
- float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+ // Start at an unlimited aspect ratio unless we get a more restrictive one
+ float maxAspectRatio = 0;
float packageMaxAspectRatio = pkg.getMaxAspectRatio();
if (packageMaxAspectRatio != 0) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6ffc638..475d95a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -173,7 +173,7 @@
void showPinningEnterExitToast(boolean entering);
void showPinningEscapeToast();
- void showShutdownUi(boolean isReboot, String reason);
+ void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom);
/**
* Used to show the authentication dialog (Biometrics, Device Credential).
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index fc60f06..1c6ac0f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -102,7 +102,7 @@
* These methods are needed for global actions control which the UI is shown in sysui.
*/
void shutdown();
- void reboot(boolean safeMode);
+ void reboot(boolean safeMode, String reason);
/** just restarts android without rebooting device. Used for some feature flags. */
void restart();
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 6e45796..b1c8eeb 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -26,6 +26,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.sysprop.SurfaceFlingerProperties;
+import android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
@@ -34,6 +36,8 @@
import com.android.internal.R;
+import java.util.Optional;
+
/**
* Provides helper functions for configuring the display rotation policy.
*/
@@ -41,7 +45,7 @@
private static final String TAG = "RotationPolicy";
private static final int CURRENT_ROTATION = -1;
- public static final int NATURAL_ROTATION = Surface.ROTATION_0;
+ private static int sNaturalRotation = -1;
private RotationPolicy() {
}
@@ -72,7 +76,7 @@
* otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable.
*/
public static int getRotationLockOrientation(Context context) {
- if (areAllRotationsAllowed(context)) {
+ if (isCurrentRotationAllowed(context)) {
return Configuration.ORIENTATION_UNDEFINED;
}
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -106,9 +110,9 @@
* Enables or disables rotation lock from the system UI toggle.
*/
public static void setRotationLock(Context context, final boolean enabled, String caller) {
- final int rotation = areAllRotationsAllowed(context)
+ final int rotation = isCurrentRotationAllowed(context)
|| useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION
- : NATURAL_ROTATION;
+ : getNaturalRotation();
setRotationLockAtAngle(context, enabled, rotation, caller);
}
@@ -135,11 +139,43 @@
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
- setRotationLock(enabled, NATURAL_ROTATION, caller);
+ setRotationLock(enabled, getNaturalRotation(), caller);
}
- private static boolean areAllRotationsAllowed(Context context) {
- return context.getResources().getBoolean(R.bool.config_allowAllRotations);
+ public static boolean isRotationAllowed(int rotation,
+ int userRotationAngles, boolean allowAllRotations) {
+ if (userRotationAngles < 0) {
+ // Not set by user so use these defaults
+ userRotationAngles = allowAllRotations ?
+ (1 | 2 | 4 | 8) : // All angles
+ (1 | 2 | 8); // All except 180
+ }
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return (userRotationAngles & 1) != 0;
+ case Surface.ROTATION_90:
+ return (userRotationAngles & 2) != 0;
+ case Surface.ROTATION_180:
+ return (userRotationAngles & 4) != 0;
+ case Surface.ROTATION_270:
+ return (userRotationAngles & 8) != 0;
+ }
+ return false;
+ }
+
+ private static boolean isCurrentRotationAllowed(Context context) {
+ int userRotationAngles = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1, UserHandle.USER_CURRENT);
+ boolean allowAllRotations = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_allowAllRotations);
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ try {
+ return isRotationAllowed(wm.getDefaultDisplayRotation(), userRotationAngles,
+ allowAllRotations);
+ } catch (RemoteException exc) {
+ Log.w(TAG, "Unable to getWindowManagerService.getDefaultDisplayRotation()");
+ }
+ return false;
}
private static boolean useCurrentRotationOnRotationLockChange(Context context) {
@@ -196,6 +232,35 @@
context.getContentResolver().unregisterContentObserver(listener.mObserver);
}
+ public static int getNaturalRotation() {
+ if (sNaturalRotation == -1) {
+ sNaturalRotation = getNaturalRotationConfig();
+ }
+ return sNaturalRotation;
+ }
+
+ private static int getNaturalRotationConfig() {
+ primary_display_orientation_values orientation =
+ primary_display_orientation_values.ORIENTATION_0;
+ Optional<primary_display_orientation_values> primaryDisplayOrientation =
+ SurfaceFlingerProperties.primary_display_orientation();
+ if (primaryDisplayOrientation.isPresent()) {
+ orientation = primaryDisplayOrientation.get();
+ }
+
+ if (orientation == primary_display_orientation_values.ORIENTATION_90) {
+ return Surface.ROTATION_90;
+ }
+ if (orientation == primary_display_orientation_values.ORIENTATION_180) {
+ return Surface.ROTATION_180;
+ }
+ if (orientation == primary_display_orientation_values.ORIENTATION_270) {
+ return Surface.ROTATION_270;
+ }
+
+ return Surface.ROTATION_0;
+ }
+
/**
* Listener that is invoked whenever a change occurs that might affect the rotation policy.
*/
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 8236783..4d4ff1d 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -109,4 +109,5 @@
boolean isWeakEscrowTokenActive(long handle, int userId);
boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
void unlockUserKeyIfUnsecured(int userId);
+ byte getLockPatternSize(int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e46b8d7..dcccad0 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -101,6 +101,11 @@
*/
public static final int MIN_LOCK_PASSWORD_SIZE = 4;
+ /*
+ * The default size of the pattern lockscreen. Ex: 3x3
+ */
+ public static final byte PATTERN_SIZE_DEFAULT = 3;
+
/**
* The minimum number of dots the user must include in a wrong pattern attempt for it to be
* counted.
@@ -953,16 +958,18 @@
* @param bytes The pattern serialized with {@link #patternToByteArray}
* @return The pattern.
*/
- public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes) {
+ public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes, byte gridSize) {
if (bytes == null) {
return null;
}
List<LockPatternView.Cell> result = Lists.newArrayList();
+ LockPatternView.Cell.updateSize(gridSize);
+
for (int i = 0; i < bytes.length; i++) {
byte b = (byte) (bytes[i] - '1');
- result.add(LockPatternView.Cell.of(b / 3, b % 3));
+ result.add(LockPatternView.Cell.of(b / gridSize, b % gridSize, gridSize));
}
return result;
}
@@ -972,7 +979,7 @@
* @param pattern The pattern.
* @return The pattern in byte array form.
*/
- public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
+ public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern, byte gridSize) {
if (pattern == null) {
return new byte[0];
}
@@ -981,7 +988,7 @@
byte[] res = new byte[patternSize];
for (int i = 0; i < patternSize; i++) {
LockPatternView.Cell cell = pattern.get(i);
- res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
+ res[i] = (byte) (cell.getRow() * gridSize + cell.getColumn() + '1');
}
return res;
}
@@ -1052,6 +1059,40 @@
}
/**
+ * @return the pattern lockscreen size
+ */
+ public byte getLockPatternSize(int userId) {
+ long size = getLong(Settings.Secure.LOCK_PATTERN_SIZE, -1, userId);
+ if (size > 0 && size < 128) {
+ return (byte) size;
+ }
+ return LockPatternUtils.PATTERN_SIZE_DEFAULT;
+ }
+
+ /**
+ * Set the pattern lockscreen size
+ */
+ public void setLockPatternSize(long size, int userId) {
+ setLong(Settings.Secure.LOCK_PATTERN_SIZE, size, userId);
+ }
+
+ public void setVisibleDotsEnabled(boolean enabled, int userId) {
+ setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled, userId);
+ }
+
+ public boolean isVisibleDotsEnabled(int userId) {
+ return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true, userId);
+ }
+
+ public void setShowErrorPath(boolean enabled, int userId) {
+ setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled, userId);
+ }
+
+ public boolean isShowErrorPath(int userId) {
+ return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true, userId);
+ }
+
+ /**
* @param userId the user for which to report the value
* @return Whether the lock screen is secured.
*/
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 66b0158..1dcf494 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -58,13 +58,14 @@
import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.widget.LockPatternUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Displays and detects the user's unlock attempt, which is a drag of a finger
- * across 9 regions of the screen.
+ * across regions of the screen.
*
* Is also capable of displaying a static pattern in "in progress", "wrong" or
* "correct" states.
@@ -82,7 +83,7 @@
private static final int DOT_RADIUS_DECREASE_DURATION_MILLIS = 192;
private static final int ALPHA_MAX_VALUE = 255;
private static final float MIN_DOT_HIT_FACTOR = 0.2f;
- private final CellState[][] mCellStates;
+ private CellState[][] mCellStates;
private static final int CELL_ACTIVATE = 0;
private static final int CELL_DEACTIVATE = 1;
@@ -110,6 +111,8 @@
*/
private static final int MILLIS_PER_CIRCLE_ANIMATING = 700;
+ private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT;
+
/**
* This can be used to avoid updating the display for very small motions or noisy panels.
* It didn't seem to have much impact on the devices tested, so currently set to 0.
@@ -121,7 +124,7 @@
private OnPatternListener mOnPatternListener;
@UnsupportedAppUsage
- private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
+ private ArrayList<Cell> mPattern = new ArrayList<Cell>(mPatternSize * mPatternSize);
/**
* Lookup table for the circles of the pattern we are currently drawing.
@@ -129,7 +132,7 @@
* in which case we use this to hold the cells we are drawing for the in
* progress animation.
*/
- private final boolean[][] mPatternDrawLookup = new boolean[3][3];
+ private boolean[][] mPatternDrawLookup = new boolean[mPatternSize][mPatternSize];
/**
* the in progress point:
@@ -140,7 +143,7 @@
private float mInProgressY = -1;
private long mAnimatingPeriodStart;
- private long[] mLineFadeStart = new long[9];
+ private long[] mLineFadeStart = new long[mPatternSize * mPatternSize];
@UnsupportedAppUsage
private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
@@ -150,6 +153,8 @@
@UnsupportedAppUsage
private boolean mPatternInProgress = false;
private boolean mFadePattern = true;
+ private boolean mVisibleDots = true;
+ private boolean mShowErrorPath = true;
private boolean mFadeClear = false;
private int mFadeAnimationAlpha = ALPHA_MAX_VALUE;
@@ -185,8 +190,10 @@
private Drawable mNotSelectedDrawable;
private boolean mUseLockPatternDrawable;
+ private LockPatternUtils mLockPatternUtils;
+
/**
- * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
+ * Represents a cell in the matrix of the unlock pattern view.
*/
public static final class Cell {
@UnsupportedAppUsage
@@ -194,25 +201,18 @@
@UnsupportedAppUsage
final int column;
- // keep # objects limited to 9
- private static final Cell[][] sCells = createCells();
-
- private static Cell[][] createCells() {
- Cell[][] res = new Cell[3][3];
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- res[i][j] = new Cell(i, j);
- }
- }
- return res;
+ static Cell[][] sCells;
+ static {
+ updateSize(LockPatternUtils.PATTERN_SIZE_DEFAULT);
}
/**
* @param row The row of the cell.
* @param column The column of the cell.
+ * @param size The size of the cell.
*/
- private Cell(int row, int column) {
- checkRange(row, column);
+ private Cell(int row, int column, byte size) {
+ checkRange(row, column, size);
this.row = row;
this.column = column;
}
@@ -225,20 +225,28 @@
return column;
}
- public static Cell of(int row, int column) {
- checkRange(row, column);
+ public static Cell of(int row, int column, byte size) {
+ checkRange(row, column, size);
return sCells[row][column];
}
- private static void checkRange(int row, int column) {
- if (row < 0 || row > 2) {
- throw new IllegalArgumentException("row must be in range 0-2");
- }
- if (column < 0 || column > 2) {
- throw new IllegalArgumentException("column must be in range 0-2");
+ public static void updateSize(byte size) {
+ sCells = new Cell[size][size];
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ sCells[i][j] = new Cell(i, j, size);
+ }
}
}
+ private static void checkRange(int row, int column, byte size) {
+ if (row < 0 || row > size - 1) {
+ throw new IllegalArgumentException("row must be in range 0-" + (size - 1));
+ }
+ if (column < 0 || column > size - 1) {
+ throw new IllegalArgumentException("column must be in range 0-" + (size - 1));
+ }
+ }
@Override
public String toString() {
return "(row=" + row + ",clmn=" + column + ")";
@@ -311,8 +319,9 @@
/**
* A pattern was detected from the user.
* @param pattern The pattern.
+ * @param patternSize The pattern size.
*/
- void onPatternDetected(List<Cell> pattern);
+ void onPatternDetected(List<Cell> pattern, byte patternSize);
}
public LockPatternView(Context context) {
@@ -388,9 +397,9 @@
mPaint.setAntiAlias(true);
mPaint.setDither(true);
- mCellStates = new CellState[3][3];
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
+ mCellStates = new CellState[mPatternSize][mPatternSize];
+ for (int i = 0; i < mPatternSize; i++) {
+ for (int j = 0; j < mPatternSize; j++) {
mCellStates[i][j] = new CellState();
mCellStates[i][j].radius = mDotSize/2;
mCellStates[i][j].row = i;
@@ -430,6 +439,13 @@
}
/**
+ * @return the current pattern lockscreen size.
+ */
+ public byte getLockPatternSize() {
+ return mPatternSize;
+ }
+
+ /**
* Set whether the view is in stealth mode. If true, there will be no
* visible feedback as the user enters the pattern.
*
@@ -440,6 +456,22 @@
mInStealthMode = inStealthMode;
}
+ public void setVisibleDots(boolean visibleDots) {
+ mVisibleDots = visibleDots;
+ }
+
+ public boolean isVisibleDots() {
+ return mVisibleDots;
+ }
+
+ public void setShowErrorPath(boolean showErrorPath) {
+ mShowErrorPath = showErrorPath;
+ }
+
+ public boolean isShowErrorPath() {
+ return mShowErrorPath;
+ }
+
/**
* Set whether the pattern should fade as it's being drawn. If
* true, each segment of the pattern fades over time.
@@ -449,6 +481,36 @@
}
/**
+ * Set the pattern size of the lockscreen
+ *
+ * @param size The pattern size.
+ */
+ public void setLockPatternSize(byte size) {
+ mPatternSize = size;
+ Cell.updateSize(size);
+ mCellStates = new CellState[mPatternSize][mPatternSize];
+ for (int i = 0; i < mPatternSize; i++) {
+ for (int j = 0; j < mPatternSize; j++) {
+ mCellStates[i][j] = new CellState();
+ mCellStates[i][j].radius = mDotSize / 2;
+ mCellStates[i][j].row = i;
+ mCellStates[i][j].col = j;
+ }
+ }
+ mPattern = new ArrayList<Cell>(size * size);
+ mLineFadeStart = new long[size * size];
+ mPatternDrawLookup = new boolean[size][size];
+ }
+
+ /**
+ * Set the LockPatternUtil instance used to encode a pattern to a string
+ * @param utils The instance.
+ */
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ /**
* Set the call back for pattern detection.
* @param onPatternListener The call back.
*/
@@ -625,7 +687,7 @@
private void notifyPatternDetected() {
sendAccessEvent(R.string.lockscreen_access_pattern_detected);
if (mOnPatternListener != null) {
- mOnPatternListener.onPatternDetected(mPattern);
+ mOnPatternListener.onPatternDetected(mPattern, mPatternSize);
}
}
@@ -698,10 +760,10 @@
* the next attempt.
*/
private void clearPatternDrawLookup() {
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < mPatternSize; i++) {
+ for (int j = 0; j < mPatternSize; j++) {
mPatternDrawLookup[i][j] = false;
- mLineFadeStart[i+j*3] = 0;
+ mLineFadeStart[i * mPatternSize + j] = 0;
}
}
}
@@ -726,11 +788,11 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
final int width = w - mPaddingLeft - mPaddingRight;
- mSquareWidth = width / 3.0f;
+ mSquareWidth = width / (float) mPatternSize;
if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")");
final int height = h - mPaddingTop - mPaddingBottom;
- mSquareHeight = height / 3.0f;
+ mSquareHeight = height / (float) mPatternSize;
mExploreByTouchHelper.invalidateRoot();
mDotHitMaxRadius = Math.min(mSquareHeight / 2, mSquareWidth / 2);
mDotHitRadius = mDotHitMaxRadius * mDotHitFactor;
@@ -793,7 +855,6 @@
if (cell != null) {
// check for gaps in existing pattern
- Cell fillInGapCell = null;
final ArrayList<Cell> pattern = mPattern;
Cell lastCell = null;
if (!pattern.isEmpty()) {
@@ -804,27 +865,17 @@
int fillInRow = lastCell.row;
int fillInColumn = lastCell.column;
- if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) {
- fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1);
+ if (dRow == 0 || dColumn == 0 || Math.abs(dRow) == Math.abs(dColumn)) {
+ while (true) {
+ fillInRow += Integer.signum(dRow);
+ fillInColumn += Integer.signum(dColumn);
+ if (fillInRow == cell.row && fillInColumn == cell.column) break;
+ Cell fillInGapCell = Cell.of(fillInRow, fillInColumn, mPatternSize);
+ if (!mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
+ addCellToPattern(fillInGapCell);
+ }
+ }
}
-
- if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) {
- fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1);
- }
-
- fillInGapCell = Cell.of(fillInRow, fillInColumn);
- }
-
- if (fillInGapCell != null &&
- !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
- addCellToPattern(fillInGapCell);
- if (mKeepDotActivated) {
- startCellDeactivatedAnimation(fillInGapCell);
- }
- }
-
- if (mKeepDotActivated && lastCell != null) {
- startCellDeactivatedAnimation(lastCell);
}
addCellToPattern(cell);
@@ -1007,8 +1058,8 @@
/** Helper method to find which cell a point maps to. */
@Nullable
private Cell detectCellHit(float x, float y) {
- for (int row = 0; row < 3; row++) {
- for (int column = 0; column < 3; column++) {
+ for (int row = 0; row < mPatternSize; row++) {
+ for (int column = 0; column < mPatternSize; column++) {
float centerY = getCenterYForRow(row);
float centerX = getCenterXForColumn(column);
float hitRadiusSquared;
@@ -1027,7 +1078,7 @@
if ((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)
< hitRadiusSquared) {
- return Cell.of(row, column);
+ return Cell.of(row, column, mPatternSize);
}
}
}
@@ -1174,11 +1225,6 @@
deactivateLastCell();
}
notifyPatternDetected();
- // Also clear pattern if fading is enabled
- if (mFadePattern) {
- clearPatternDrawLookup();
- mPatternDisplayMode = DisplayMode.Correct;
- }
invalidate();
}
if (PROFILE_DRAWING) {
@@ -1195,8 +1241,8 @@
}
private void cancelLineAnimations() {
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < mPatternSize; i++) {
+ for (int j = 0; j < mPatternSize; j++) {
CellState state = mCellStates[i][j];
if (state.activationAnimator != null) {
state.activationAnimator.cancel();
@@ -1318,7 +1364,9 @@
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
// draw the path of the pattern (unless we are in stealth mode)
- final boolean drawPath = !mInStealthMode;
+ final boolean drawWrongPath = mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath;
+ final boolean drawPath = (!mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong)
+ || drawWrongPath;
if (drawPath && !mFadeClear) {
mPathPaint.setColor(getCurrentColor(true /* partOfPattern */));
@@ -1387,24 +1435,26 @@
}
// draw the circles
- for (int i = 0; i < 3; i++) {
- float centerY = getCenterYForRow(i);
- for (int j = 0; j < 3; j++) {
- CellState cellState = mCellStates[i][j];
- float centerX = getCenterXForColumn(j);
- float translationY = cellState.translationY;
+ if (mVisibleDots) {
+ for (int i = 0; i < mPatternSize; i++) {
+ float centerY = getCenterYForRow(i);
+ for (int j = 0; j < mPatternSize; j++) {
+ CellState cellState = mCellStates[i][j];
+ float centerX = getCenterXForColumn(j);
+ float translationY = cellState.translationY;
- if (mUseLockPatternDrawable) {
- drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
- } else {
- if (isHardwareAccelerated() && cellState.hwAnimating) {
- RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
- cellState.hwRadius, cellState.hwPaint);
+ if (mUseLockPatternDrawable) {
+ drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
} else {
- drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- cellState.radius, drawLookup[i][j], cellState.alpha,
- cellState.activationAnimationProgress);
+ if (isHardwareAccelerated() && cellState.hwAnimating) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+ cellState.hwRadius, cellState.hwPaint);
+ } else {
+ drawCircle(canvas, (int) centerX, (int) centerY + translationY,
+ cellState.radius, drawLookup[i][j], cellState.alpha,
+ cellState.activationAnimationProgress);
+ }
}
}
}
@@ -1486,7 +1536,10 @@
}
private int getCurrentColor(boolean partOfPattern) {
- if (!partOfPattern || mInStealthMode || mPatternInProgress) {
+ if (!partOfPattern
+ || (mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong)
+ || (mPatternDisplayMode == DisplayMode.Wrong && !mShowErrorPath)
+ || mPatternInProgress) {
// unselected circle
return mRegularColor;
} else if (mPatternDisplayMode == DisplayMode.Wrong) {
@@ -1545,12 +1598,13 @@
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
- byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern);
+ byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern, mPatternSize);
String patternString = patternBytes != null ? new String(patternBytes) : null;
return new SavedState(superState,
patternString,
mPatternDisplayMode.ordinal(),
- mInputEnabled, mInStealthMode);
+ mPatternSize, mInputEnabled, mInStealthMode,
+ mVisibleDots, mShowErrorPath);
}
@Override
@@ -1559,10 +1613,14 @@
super.onRestoreInstanceState(ss.getSuperState());
setPattern(
DisplayMode.Correct,
- LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
+ LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes(),
+ ss.getPatternSize()));
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
+ mPatternSize = ss.getPatternSize();
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
+ mVisibleDots = ss.isVisibleDots();
+ mShowErrorPath = ss.isShowErrorPath();
}
@Override
@@ -1579,20 +1637,27 @@
private final String mSerializedPattern;
private final int mDisplayMode;
+ private final byte mPatternSize;
private final boolean mInputEnabled;
private final boolean mInStealthMode;
+ private final boolean mVisibleDots;
+ private final boolean mShowErrorPath;
/**
* Constructor called from {@link LockPatternView#onSaveInstanceState()}
*/
@UnsupportedAppUsage
private SavedState(Parcelable superState, String serializedPattern, int displayMode,
- boolean inputEnabled, boolean inStealthMode) {
+ byte patternSize, boolean inputEnabled, boolean inStealthMode,
+ boolean visibleDots, boolean showErrorPath) {
super(superState);
mSerializedPattern = serializedPattern;
mDisplayMode = displayMode;
+ mPatternSize = patternSize;
mInputEnabled = inputEnabled;
mInStealthMode = inStealthMode;
+ mVisibleDots = visibleDots;
+ mShowErrorPath = showErrorPath;
}
/**
@@ -1603,8 +1668,11 @@
super(in);
mSerializedPattern = in.readString();
mDisplayMode = in.readInt();
+ mPatternSize = (byte) in.readByte();
mInputEnabled = (Boolean) in.readValue(null);
mInStealthMode = (Boolean) in.readValue(null);
+ mVisibleDots = (Boolean) in.readValue(null);
+ mShowErrorPath = (Boolean) in.readValue(null);
}
public String getSerializedPattern() {
@@ -1615,6 +1683,10 @@
return mDisplayMode;
}
+ public byte getPatternSize() {
+ return mPatternSize;
+ }
+
public boolean isInputEnabled() {
return mInputEnabled;
}
@@ -1623,13 +1695,24 @@
return mInStealthMode;
}
+ public boolean isVisibleDots() {
+ return mVisibleDots;
+ }
+
+ public boolean isShowErrorPath() {
+ return mShowErrorPath;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(mSerializedPattern);
dest.writeInt(mDisplayMode);
+ dest.writeByte(mPatternSize);
dest.writeValue(mInputEnabled);
dest.writeValue(mInStealthMode);
+ dest.writeValue(mVisibleDots);
+ dest.writeValue(mShowErrorPath);
}
@SuppressWarnings({ "unused", "hiding" }) // Found using reflection
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 18d5f6d..57e9b01 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -121,9 +121,11 @@
/**
* Creates a LockscreenCredential object representing the given pattern.
*/
- public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
+ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern,
+ byte gridSize) {
return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
- LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false);
+ LockPatternUtils.patternToByteArray(pattern, gridSize),
+ /* hasInvalidChars= */ false);
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92fee51..c5f183d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -838,6 +838,10 @@
<protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
<protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
+ <!-- App lock -->
+ <protected-broadcast android:name="com.android.server.app.AppLockManagerService.APP_LOCK_TIMEOUT" />
+ <protected-broadcast android:name="android.app.action.UNLOCK_APP" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -4473,6 +4477,21 @@
android:description="@string/permdesc_getPackageSize"
android:protectionLevel="normal" />
+ <!-- Dummy user-facing group for faking package signature -->
+ <permission-group android:name="android.permission-group.FAKE_PACKAGE"
+ android:label="@string/permgrouplab_fake_package_signature"
+ android:description="@string/permgroupdesc_fake_package_signature"
+ android:request="@string/permgrouprequest_fake_package_signature"
+ android:priority="100" />
+
+ <!-- Allows an application to change the package signature as
+ seen by applications -->
+ <permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/permlab_fakePackageSignature"
+ android:description="@string/permdesc_fakePackageSignature" />
+
<!-- @deprecated No longer useful, see
{@link android.content.pm.PackageManager#addPackageToPreferred}
for details. -->
@@ -8043,7 +8062,6 @@
<permission android:name="android.permission.GET_ANY_PROVIDER_TYPE"
android:protectionLevel="signature" />
-
<!-- @hide Allows internal applications to read and synchronize non-core flags.
Apps without this permission can only read a subset of flags specifically intended
for use in "core", (i.e. third party apps). Apps with this permission can define their
@@ -8102,6 +8120,11 @@
<permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to manage app lock
+ @hide -->
+ <permission android:name="com.android.permission.MANAGE_APP_LOCK"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide @SystemApi
@FlaggedApi("com.android.server.notification.flags.redact_otp_notifications_from_untrusted_listeners")
Allows apps with a NotificationListenerService to receive notifications with sensitive
@@ -8159,6 +8182,8 @@
<!-- Attribution for Gnss Time Update service. -->
<attribution android:tag="GnssTimeUpdateService"
android:label="@string/gnss_time_update_service"/>
+ <!-- LineageHealth -->
+ <protected-broadcast android:name="lineageos.platform.intent.action.CHARGING_CONTROL_CANCEL_ONCE" />
<!-- Attribution for MusicRecognitionManagerService.
<p>Not for use by third-party applications.</p> -->
<attribution android:tag="MusicRecognitionManagerService"
diff --git a/core/res/res/color/surface_header_dark_sysui.xml b/core/res/res/color/surface_header_dark_sysui.xml
new file mode 100644
index 0000000..ec070c9
--- /dev/null
+++ b/core/res/res/color/surface_header_dark_sysui.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/system_neutral1_500" android:lStar="5" />
+</selector>
diff --git a/core/res/res/drawable/ic_charging_control.xml b/core/res/res/drawable/ic_charging_control.xml
new file mode 100644
index 0000000..8308532
--- /dev/null
+++ b/core/res/res/drawable/ic_charging_control.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.2,22.5H7.8c-1.3,0 -2.3,-1 -2.3,-2.3V5.8c0,-1.3 1,-2.3 2.3,-2.3h0.7v-2h7v2h0.7c1.3,0 2.3,1.1 2.3,2.3v14.3C18.5,21.5 17.5,22.5 16.2,22.5zM7.8,5.5c-0.2,0 -0.3,0.2 -0.3,0.3v14.3c0,0.2 0.2,0.3 0.3,0.3h8.3c0.2,0 0.3,-0.1 0.3,-0.3V5.8c0,-0.2 -0.1,-0.3 -0.3,-0.3h-2.7v-2h-3v2H7.8z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.17,18.42v-4.58H9.5l3.33,-6.25v4.58h1.67L11.17,18.42z"/>
+</vector>
diff --git a/core/res/res/layout/accessibility_enable_service_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml
index 01ef101..84520a2 100644
--- a/core/res/res/layout/accessibility_enable_service_warning.xml
+++ b/core/res/res/layout/accessibility_enable_service_warning.xml
@@ -51,7 +51,7 @@
android:gravity="center"
android:textSize="20sp"
android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="google-sans-medium"/>
+ android:fontFamily="@string/config_headlineFontFamilyMedium"/>
<TextView
android:id="@+id/accessibility_permissionDialog_description"
diff --git a/core/res/res/layout/time_picker_material.xml b/core/res/res/layout/time_picker_material.xml
index 7597379..ee733f1 100644
--- a/core/res/res/layout/time_picker_material.xml
+++ b/core/res/res/layout/time_picker_material.xml
@@ -44,7 +44,7 @@
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:includeFontPadding="false"
- android:fontFamily="sans-serif-medium"
+ android:fontFamily="@string/config_bodyFontFamilyMedium"
android:textSize="34sp"
android:textColor="@color/white"
android:text="@string/time_picker_header_text"/>
diff --git a/core/res/res/values-af/cm_strings.xml b/core/res/res/values-af/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-af/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ar/cm_strings.xml b/core/res/res/values-ar/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-ar/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-as/cm_strings.xml b/core/res/res/values-as/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-as/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ast-rES/cm_strings.xml b/core/res/res/values-ast-rES/cm_strings.xml
new file mode 100644
index 0000000..e945f27
--- /dev/null
+++ b/core/res/res/values-ast-rES/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Control de la carga</string>
+ <string name="charging_control_notification_title">Control de la carga</string>
+ <string name="charging_control_notification_cancel_once">Encaboxar</string>
+ <string name="charging_control_notification_content_limit">La batería va cargar hasta\'l %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">La batería ta cargada hasta\'l %1$d%%</string>
+ <string name="charging_control_notification_content_target">La batería va cargar al completu a la hora: %1$s</string>
+ <string name="charging_control_notification_content_target_reached">La batería ta cargada</string>
+</resources>
diff --git a/core/res/res/values-az/cm_strings.xml b/core/res/res/values-az/cm_strings.xml
new file mode 100644
index 0000000..8997881
--- /dev/null
+++ b/core/res/res/values-az/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Doldurma nəzarəti</string>
+ <string name="charging_control_notification_title">Doldurma nəzarəti</string>
+ <string name="charging_control_notification_cancel_once">İmtina</string>
+ <string name="charging_control_notification_content_limit">Batareya %1$d%% doldurulacaq</string>
+ <string name="charging_control_notification_content_limit_reached">Batareya %1$d%% doldu</string>
+ <string name="charging_control_notification_content_target">Batareya %1$s ərzində tam dolacaq</string>
+ <string name="charging_control_notification_content_target_reached">Batareya doldu</string>
+</resources>
diff --git a/core/res/res/values-be/cm_strings.xml b/core/res/res/values-be/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-be/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-bg/cm_strings.xml b/core/res/res/values-bg/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-bg/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ca/cm_strings.xml b/core/res/res/values-ca/cm_strings.xml
new file mode 100644
index 0000000..48b665a
--- /dev/null
+++ b/core/res/res/values-ca/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Control de càrrega</string>
+ <string name="charging_control_notification_title">Control de càrrega</string>
+ <string name="charging_control_notification_cancel_once">Cancel·la</string>
+ <string name="charging_control_notification_content_limit">La bateria es carregarà al %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">La bateria està carregada al %1$d%%</string>
+ <string name="charging_control_notification_content_target">La bateria es carregarà completament en %1$s</string>
+ <string name="charging_control_notification_content_target_reached">La bateria està carregada</string>
+</resources>
diff --git a/core/res/res/values-cs/cm_strings.xml b/core/res/res/values-cs/cm_strings.xml
new file mode 100644
index 0000000..84d0ebb
--- /dev/null
+++ b/core/res/res/values-cs/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kontrola nabíjení</string>
+ <string name="charging_control_notification_title">Kontrola nabíjení</string>
+ <string name="charging_control_notification_cancel_once">Zrušit</string>
+ <string name="charging_control_notification_content_limit">Baterie bude nabita na %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Baterie je nabitá na %1$d%%</string>
+ <string name="charging_control_notification_content_target">Baterie bude plně nabita do %s</string>
+ <string name="charging_control_notification_content_target_reached">Baterie je nabitá</string>
+</resources>
diff --git a/core/res/res/values-cy/cm_strings.xml b/core/res/res/values-cy/cm_strings.xml
new file mode 100644
index 0000000..831a097
--- /dev/null
+++ b/core/res/res/values-cy/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Rheolaeth gwefru</string>
+ <string name="charging_control_notification_title">Rheolaeth gwefru</string>
+ <string name="charging_control_notification_cancel_once">Diddymu</string>
+ <string name="charging_control_notification_content_limit">Caiff y batri ei wefru at %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Batri wedi\u2019i wefru at %1$d%%</string>
+ <string name="charging_control_notification_content_target">Caiff y batri ei wefru\u2019n llawn am %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Batri wedi\u2019i wefru</string>
+</resources>
diff --git a/core/res/res/values-da/cm_strings.xml b/core/res/res/values-da/cm_strings.xml
new file mode 100644
index 0000000..38e81e0
--- /dev/null
+++ b/core/res/res/values-da/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Opladningskontrol</string>
+ <string name="charging_control_notification_title">Opladningskontrol</string>
+ <string name="charging_control_notification_cancel_once">Afbryd</string>
+ <string name="charging_control_notification_content_limit">Batteriet vil blive opladet til %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Batteriet ert opladet til %1$d%%</string>
+ <string name="charging_control_notification_content_target">Batteriet er fuldt opladet ved %1$d%%</string>
+ <string name="charging_control_notification_content_target_reached">Batteri er opladet</string>
+</resources>
diff --git a/core/res/res/values-de/cm_strings.xml b/core/res/res/values-de/cm_strings.xml
new file mode 100644
index 0000000..d31daa96
--- /dev/null
+++ b/core/res/res/values-de/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Ladekontrolle</string>
+ <string name="charging_control_notification_title">Ladekontrolle</string>
+ <string name="charging_control_notification_cancel_once">Abbrechen</string>
+ <string name="charging_control_notification_content_limit">Akku wird bis %1$d%% geladen</string>
+ <string name="charging_control_notification_content_limit_reached">Akku ist bis %1$d%% geladen</string>
+ <string name="charging_control_notification_content_target">Akku wird bis %1$s vollständig geladen sein</string>
+ <string name="charging_control_notification_content_target_reached">Akku ist aufgeladen</string>
+</resources>
diff --git a/core/res/res/values-el/cm_strings.xml b/core/res/res/values-el/cm_strings.xml
new file mode 100644
index 0000000..e12b122
--- /dev/null
+++ b/core/res/res/values-el/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Έλεγχος φόρτισης</string>
+ <string name="charging_control_notification_title">Έλεγχος φόρτισης</string>
+ <string name="charging_control_notification_cancel_once">Ακύρωση</string>
+ <string name="charging_control_notification_content_limit">Η μπαταρία θα φορτιστεί έως %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Η μπαταρία φορτίστηκε έως %1$d%%</string>
+ <string name="charging_control_notification_content_target">Η μπαταρία θα είναι πλήρως φορτισμένη στις %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Η μπαταρία φορτίστηκε</string>
+</resources>
diff --git a/core/res/res/values-en-rAU/cm_strings.xml b/core/res/res/values-en-rAU/cm_strings.xml
new file mode 100644
index 0000000..2bb7972
--- /dev/null
+++ b/core/res/res/values-en-rAU/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Charging control</string>
+ <string name="charging_control_notification_title">Charging control</string>
+ <string name="charging_control_notification_cancel_once">Cancel</string>
+ <string name="charging_control_notification_content_limit">Battery will be charged to %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Battery is charged to %1$d%%</string>
+ <string name="charging_control_notification_content_target">Battery will be fully charged at %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Battery is charged</string>
+</resources>
diff --git a/core/res/res/values-en-rCA/cm_strings.xml b/core/res/res/values-en-rCA/cm_strings.xml
new file mode 100644
index 0000000..2bb7972
--- /dev/null
+++ b/core/res/res/values-en-rCA/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Charging control</string>
+ <string name="charging_control_notification_title">Charging control</string>
+ <string name="charging_control_notification_cancel_once">Cancel</string>
+ <string name="charging_control_notification_content_limit">Battery will be charged to %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Battery is charged to %1$d%%</string>
+ <string name="charging_control_notification_content_target">Battery will be fully charged at %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Battery is charged</string>
+</resources>
diff --git a/core/res/res/values-en-rGB/cm_strings.xml b/core/res/res/values-en-rGB/cm_strings.xml
new file mode 100644
index 0000000..2bb7972
--- /dev/null
+++ b/core/res/res/values-en-rGB/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Charging control</string>
+ <string name="charging_control_notification_title">Charging control</string>
+ <string name="charging_control_notification_cancel_once">Cancel</string>
+ <string name="charging_control_notification_content_limit">Battery will be charged to %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Battery is charged to %1$d%%</string>
+ <string name="charging_control_notification_content_target">Battery will be fully charged at %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Battery is charged</string>
+</resources>
diff --git a/core/res/res/values-en-rIN/cm_strings.xml b/core/res/res/values-en-rIN/cm_strings.xml
new file mode 100644
index 0000000..2bb7972
--- /dev/null
+++ b/core/res/res/values-en-rIN/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Charging control</string>
+ <string name="charging_control_notification_title">Charging control</string>
+ <string name="charging_control_notification_cancel_once">Cancel</string>
+ <string name="charging_control_notification_content_limit">Battery will be charged to %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Battery is charged to %1$d%%</string>
+ <string name="charging_control_notification_content_target">Battery will be fully charged at %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Battery is charged</string>
+</resources>
diff --git a/core/res/res/values-eo/cm_strings.xml b/core/res/res/values-eo/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-eo/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-es-rMX/cm_strings.xml b/core/res/res/values-es-rMX/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-es-rMX/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-es-rUS/cm_strings.xml b/core/res/res/values-es-rUS/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-es-rUS/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-es/cm_strings.xml b/core/res/res/values-es/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-es/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-et/cm_strings.xml b/core/res/res/values-et/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-et/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-eu/cm_strings.xml b/core/res/res/values-eu/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-eu/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-fa/cm_strings.xml b/core/res/res/values-fa/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-fa/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-fi/cm_strings.xml b/core/res/res/values-fi/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-fi/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-fr/cm_strings.xml b/core/res/res/values-fr/cm_strings.xml
new file mode 100644
index 0000000..c1d5a36
--- /dev/null
+++ b/core/res/res/values-fr/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Contrôle de la charge</string>
+ <string name="charging_control_notification_title">Contrôle de la charge</string>
+ <string name="charging_control_notification_cancel_once">Annuler</string>
+ <string name="charging_control_notification_content_limit">La batterie sera chargée à %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">La batterie est chargée à %1$d%%</string>
+ <string name="charging_control_notification_content_target">La batterie sera complètement chargée à %1$s</string>
+ <string name="charging_control_notification_content_target_reached">La batterie est chargée</string>
+</resources>
diff --git a/core/res/res/values-fur-rIT/cm_strings.xml b/core/res/res/values-fur-rIT/cm_strings.xml
new file mode 100644
index 0000000..07759a8
--- /dev/null
+++ b/core/res/res/values-fur-rIT/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Control de cjarie</string>
+ <string name="charging_control_notification_title">Control de cjarie</string>
+ <string name="charging_control_notification_cancel_once">Anule</string>
+ <string name="charging_control_notification_content_limit">La batarie e vignarà cjariade fin al %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">La batarie e je cjarie al %1$d%%</string>
+ <string name="charging_control_notification_content_target">La batarie e sarà cjariade dal dut aes %1$s</string>
+ <string name="charging_control_notification_content_target_reached">La batarie e je cjarie</string>
+</resources>
diff --git a/core/res/res/values-fy-rNL/cm_strings.xml b/core/res/res/values-fy-rNL/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-fy-rNL/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-gd/cm_strings.xml b/core/res/res/values-gd/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-gd/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-gl/cm_strings.xml b/core/res/res/values-gl/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-gl/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-gu/cm_strings.xml b/core/res/res/values-gu/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-gu/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-hr/cm_strings.xml b/core/res/res/values-hr/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-hr/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-hu/cm_strings.xml b/core/res/res/values-hu/cm_strings.xml
new file mode 100644
index 0000000..bdf743b
--- /dev/null
+++ b/core/res/res/values-hu/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kontrollált töltés</string>
+ <string name="charging_control_notification_title">Kontrollált töltés</string>
+ <string name="charging_control_notification_cancel_once">Mégse</string>
+ <string name="charging_control_notification_content_limit">Az akkumulátor %1$d%%-ig lesz feltöltve</string>
+ <string name="charging_control_notification_content_limit_reached">Az akkumulátor feltöltődött %1$d%%-ig</string>
+ <string name="charging_control_notification_content_target">Az akkumulátor teljesen feltöltődik ekkorra: %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Az akku feltöltve</string>
+</resources>
diff --git a/core/res/res/values-in/cm_strings.xml b/core/res/res/values-in/cm_strings.xml
new file mode 100644
index 0000000..1efef47
--- /dev/null
+++ b/core/res/res/values-in/cm_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kontrol pengisian daya</string>
+ <string name="charging_control_notification_title">Kontrol pengisian daya</string>
+ <string name="charging_control_notification_cancel_once">Batal</string>
+</resources>
diff --git a/core/res/res/values-is/cm_strings.xml b/core/res/res/values-is/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-is/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-it/cm_strings.xml b/core/res/res/values-it/cm_strings.xml
new file mode 100644
index 0000000..343f977
--- /dev/null
+++ b/core/res/res/values-it/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Controllo della carica</string>
+ <string name="charging_control_notification_title">Controllo della carica</string>
+ <string name="charging_control_notification_cancel_once">Annulla</string>
+ <string name="charging_control_notification_content_limit">La batteria verrà caricata fino al %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">La batteria è carica al %1$d%%</string>
+ <string name="charging_control_notification_content_target">La batteria sarà completamente carica alle %s</string>
+ <string name="charging_control_notification_content_target_reached">La batteria è carica</string>
+</resources>
diff --git a/core/res/res/values-iw/cm_strings.xml b/core/res/res/values-iw/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-iw/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ja/cm_strings.xml b/core/res/res/values-ja/cm_strings.xml
new file mode 100644
index 0000000..17fb180
--- /dev/null
+++ b/core/res/res/values-ja/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">充電コントロール</string>
+ <string name="charging_control_notification_title">充電コントロール</string>
+ <string name="charging_control_notification_cancel_once">キャンセル</string>
+ <string name="charging_control_notification_content_limit">バッテリーは %1$d%% まで充電されます</string>
+ <string name="charging_control_notification_content_limit_reached">バッテリーは %1$d%% まで充電されています</string>
+ <string name="charging_control_notification_content_target">バッテリーは %1$s に完全に充電されます</string>
+ <string name="charging_control_notification_content_target_reached">バッテリーを充電しました</string>
+</resources>
diff --git a/core/res/res/values-ka/cm_strings.xml b/core/res/res/values-ka/cm_strings.xml
new file mode 100644
index 0000000..abffa48
--- /dev/null
+++ b/core/res/res/values-ka/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">დამუხტვის მართვა</string>
+ <string name="charging_control_notification_title">დამუხტვის მართვა</string>
+ <string name="charging_control_notification_cancel_once">გაუქმება</string>
+ <string name="charging_control_notification_content_limit">დაიმუხტება არაუმეტეს %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">დამუხტულია არაუმეტეს %1$d%%</string>
+ <string name="charging_control_notification_content_target">სრულად დამუხტვის დროა %1$s</string>
+ <string name="charging_control_notification_content_target_reached">დამუხტულია</string>
+</resources>
diff --git a/core/res/res/values-kab-rDZ/cm_strings.xml b/core/res/res/values-kab-rDZ/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-kab-rDZ/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-kn/cm_strings.xml b/core/res/res/values-kn/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-kn/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ko/cm_strings.xml b/core/res/res/values-ko/cm_strings.xml
new file mode 100644
index 0000000..20d60e2
--- /dev/null
+++ b/core/res/res/values-ko/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">충전 제어</string>
+ <string name="charging_control_notification_title">충전 제어</string>
+ <string name="charging_control_notification_cancel_once">취소</string>
+ <string name="charging_control_notification_content_limit">배터리가 %1$d%% 까지 충전됩니다</string>
+ <string name="charging_control_notification_content_limit_reached">배터리가 %1$d%% 까지 충전되었습니다</string>
+ <string name="charging_control_notification_content_target">배터리가 %1$d%% 에 완전히 충전됩니다</string>
+ <string name="charging_control_notification_content_target_reached">배터리 충전됨</string>
+</resources>
diff --git a/core/res/res/values-lb/cm_strings.xml b/core/res/res/values-lb/cm_strings.xml
new file mode 100644
index 0000000..0ea6db6
--- /dev/null
+++ b/core/res/res/values-lb/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ (C) 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-lt/cm_strings.xml b/core/res/res/values-lt/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-lt/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-lu/cm_strings.xml b/core/res/res/values-lu/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-lu/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ml/cm_strings.xml b/core/res/res/values-ml/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-ml/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-mr/cm_strings.xml b/core/res/res/values-mr/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-mr/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-nb/cm_strings.xml b/core/res/res/values-nb/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-nb/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index d3f998f..4f7b9e0 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,6 +33,7 @@
<color name="overview_background">@color/overview_background_dark</color>
+ <color name="user_icon_1">@color/system_accent1_100</color>
<color name="user_icon_4">#fff439a0</color><!-- pink -->
<color name="user_icon_6">#ff4ecde6</color><!-- cyan -->
<color name="user_icon_7">#fffbbc04</color><!-- yellow -->
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 7cfdba7..970dba3 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -87,7 +87,7 @@
<style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
<item name="windowLightNavigationBar">false</item>
- <item name="colorBackgroundFloating">@android:color/GM2_grey_800</item>
+ <item name="colorBackgroundFloating">?android:attr/colorPrimary</item>
<item name="textColorSecondary">@android:color/resolver_text_color_secondary_dark</item>
</style>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 1571fab..0683c20 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -22,6 +22,7 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorSurfaceHeader">@color/surface_header_dark_sysui</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
<item name="forceDarkAllowed">false</item>
diff --git a/core/res/res/values-nl/cm_strings.xml b/core/res/res/values-nl/cm_strings.xml
new file mode 100644
index 0000000..2e3f007
--- /dev/null
+++ b/core/res/res/values-nl/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Oplaadcontrole</string>
+ <string name="charging_control_notification_title">Oplaadcontrole</string>
+ <string name="charging_control_notification_cancel_once">Annuleer</string>
+ <string name="charging_control_notification_content_limit">Batterij wordt tot %1$d%% opgeladen</string>
+ <string name="charging_control_notification_content_limit_reached">Batterij is opgeladen tot %1$d%%</string>
+ <string name="charging_control_notification_content_target">Batterij zal volledig opgeladen worden op %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Batterij is opgeladen</string>
+</resources>
diff --git a/core/res/res/values-or/cm_strings.xml b/core/res/res/values-or/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-or/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-pl/cm_strings.xml b/core/res/res/values-pl/cm_strings.xml
new file mode 100644
index 0000000..238e794
--- /dev/null
+++ b/core/res/res/values-pl/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kontrola ładowania</string>
+ <string name="charging_control_notification_title">Kontrola ładowania</string>
+ <string name="charging_control_notification_cancel_once">Anuluj</string>
+ <string name="charging_control_notification_content_limit">Bateria zostanie naładowana do %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Bateria jest naładowana w %1$d%%</string>
+ <string name="charging_control_notification_content_target">Bateria zostanie w pełni załadowana o %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Bateria jest naładowana</string>
+</resources>
diff --git a/core/res/res/values-pt-rBR/cm_strings.xml b/core/res/res/values-pt-rBR/cm_strings.xml
new file mode 100644
index 0000000..96620c2
--- /dev/null
+++ b/core/res/res/values-pt-rBR/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Controle de carregamento</string>
+ <string name="charging_control_notification_title">Controle de carregamento</string>
+ <string name="charging_control_notification_cancel_once">Cancelar</string>
+ <string name="charging_control_notification_content_limit">A bateria carregará até %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Bateria estará carregada até %1$d%%</string>
+ <string name="charging_control_notification_content_target">A bateria estará totalmente carregada às %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Bateria carregada</string>
+</resources>
diff --git a/core/res/res/values-pt-rPT/cm_strings.xml b/core/res/res/values-pt-rPT/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-pt-rPT/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-ro/cm_strings.xml b/core/res/res/values-ro/cm_strings.xml
new file mode 100644
index 0000000..5b06254
--- /dev/null
+++ b/core/res/res/values-ro/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Control încărcare</string>
+ <string name="charging_control_notification_title">Control încărcare</string>
+ <string name="charging_control_notification_cancel_once">Anulare</string>
+ <string name="charging_control_notification_content_limit">Bateria va fi încărcată la %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Bateria este încărcată la %1$d%%</string>
+ <string name="charging_control_notification_content_target">Bateria va fi încărcată complet la %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Bateria este încărcată</string>
+</resources>
diff --git a/core/res/res/values-ru/cm_strings.xml b/core/res/res/values-ru/cm_strings.xml
new file mode 100644
index 0000000..e8425ac
--- /dev/null
+++ b/core/res/res/values-ru/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Управление зарядкой</string>
+ <string name="charging_control_notification_title">Управление зарядкой</string>
+ <string name="charging_control_notification_cancel_once">Отмена</string>
+ <string name="charging_control_notification_content_limit">Батарея будет заряжена до %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Батарея заряжена до %1$d%%</string>
+ <string name="charging_control_notification_content_target">Батарея будет полностью заряжена в %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Батарея заряжена</string>
+</resources>
diff --git a/core/res/res/values-sc-rIT/cm_strings.xml b/core/res/res/values-sc-rIT/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-sc-rIT/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-sk/cm_strings.xml b/core/res/res/values-sk/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-sk/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-sl/cm_strings.xml b/core/res/res/values-sl/cm_strings.xml
new file mode 100644
index 0000000..5b7fde0
--- /dev/null
+++ b/core/res/res/values-sl/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Nadzorovano polnjenje</string>
+ <string name="charging_control_notification_title">Nadzorovano polnjenje</string>
+ <string name="charging_control_notification_cancel_once">Prekliči</string>
+ <string name="charging_control_notification_content_limit">Baterija bo napolnjena do %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Baterija je napolnjena do %1$d%%</string>
+ <string name="charging_control_notification_content_target">Baterija bo popolnoma napolnjena ob %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Baterija je napolnjena</string>
+</resources>
diff --git a/core/res/res/values-sq/cm_strings.xml b/core/res/res/values-sq/cm_strings.xml
new file mode 100644
index 0000000..6f21302
--- /dev/null
+++ b/core/res/res/values-sq/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kontrolli i karikimit</string>
+ <string name="charging_control_notification_title">Kontrolli i karikimit</string>
+ <string name="charging_control_notification_cancel_once">Anullo</string>
+ <string name="charging_control_notification_content_limit">Bateria do të karikohet në %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Bateria është e karikuar në %1$d%%</string>
+ <string name="charging_control_notification_content_target">Bateria do të karikohet plotësisht në %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Bateria është e karikuar</string>
+</resources>
diff --git a/core/res/res/values-sr/cm_strings.xml b/core/res/res/values-sr/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-sr/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-sv/cm_strings.xml b/core/res/res/values-sv/cm_strings.xml
new file mode 100644
index 0000000..722cde2
--- /dev/null
+++ b/core/res/res/values-sv/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Laddningskontroll</string>
+ <string name="charging_control_notification_title">Laddningskontroll</string>
+ <string name="charging_control_notification_cancel_once">Avbryt</string>
+ <string name="charging_control_notification_content_limit">Batteriet kommer att laddas till %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Batteriet är laddat till %1$d%%</string>
+ <string name="charging_control_notification_content_target">Batteriet kommer att vara fulladdat vid %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Batteriet är laddat</string>
+</resources>
diff --git a/core/res/res/values-ta/cm_strings.xml b/core/res/res/values-ta/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-ta/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-te/cm_strings.xml b/core/res/res/values-te/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-te/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-th/cm_strings.xml b/core/res/res/values-th/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-th/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-tr/cm_strings.xml b/core/res/res/values-tr/cm_strings.xml
new file mode 100644
index 0000000..3dd4f7b
--- /dev/null
+++ b/core/res/res/values-tr/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Şarj kontrolü</string>
+ <string name="charging_control_notification_title">Şarj kontrolü</string>
+ <string name="charging_control_notification_cancel_once">İptal</string>
+ <string name="charging_control_notification_content_limit">Pil %1$d%% seviyesine kadar şarj olacak</string>
+ <string name="charging_control_notification_content_limit_reached">Pil %1$d%% seviyesine kadar şarj edildi</string>
+ <string name="charging_control_notification_content_target">Pil şu saatte tamamen şarj olmuş olacak: %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Pil şarj oldu</string>
+</resources>
diff --git a/core/res/res/values-ug/cm_strings.xml b/core/res/res/values-ug/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-ug/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-uk/cm_strings.xml b/core/res/res/values-uk/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-uk/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-vi/cm_strings.xml b/core/res/res/values-vi/cm_strings.xml
new file mode 100644
index 0000000..a34237e
--- /dev/null
+++ b/core/res/res/values-vi/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">Kiểm soát sạc</string>
+ <string name="charging_control_notification_title">Kiểm soát sạc</string>
+ <string name="charging_control_notification_cancel_once">Hủy</string>
+ <string name="charging_control_notification_content_limit">Pin sẽ được sạc đến %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Pin đã được sạc đến %1$d%%</string>
+ <string name="charging_control_notification_content_target">Pin sẽ được sạc đến %1$d%%</string>
+ <string name="charging_control_notification_content_target_reached">Pin đã được sạc</string>
+</resources>
diff --git a/core/res/res/values-zh-rCN/cm_strings.xml b/core/res/res/values-zh-rCN/cm_strings.xml
new file mode 100644
index 0000000..b50f588
--- /dev/null
+++ b/core/res/res/values-zh-rCN/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">充电控制</string>
+ <string name="charging_control_notification_title">充电控制</string>
+ <string name="charging_control_notification_cancel_once">取消</string>
+ <string name="charging_control_notification_content_limit">电池将会被充电至 %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">电池已充电 %1$d%%</string>
+ <string name="charging_control_notification_content_target">电池将在 %1$s 完全充满</string>
+ <string name="charging_control_notification_content_target_reached">电池已充满</string>
+</resources>
diff --git a/core/res/res/values-zh-rHK/cm_strings.xml b/core/res/res/values-zh-rHK/cm_strings.xml
new file mode 100644
index 0000000..cb81350
--- /dev/null
+++ b/core/res/res/values-zh-rHK/cm_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/core/res/res/values-zh-rTW/cm_strings.xml b/core/res/res/values-zh-rTW/cm_strings.xml
new file mode 100644
index 0000000..d310662
--- /dev/null
+++ b/core/res/res/values-zh-rTW/cm_strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="charging_control_notification_channel">充電控制</string>
+ <string name="charging_control_notification_title">充電控制</string>
+ <string name="charging_control_notification_cancel_once">取消</string>
+ <string name="charging_control_notification_content_limit">電池將會充電至 %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">電池已充電至 %1$d%%</string>
+ <string name="charging_control_notification_content_target">電池已在 %1$s 完全充飽</string>
+ <string name="charging_control_notification_content_target_reached">電池已完成充電</string>
+</resources>
diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml
new file mode 100644
index 0000000..d6d4b7d
--- /dev/null
+++ b/core/res/res/values/cm_strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+ 2017-2023 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Health interface -->
+ <string name="charging_control_notification_channel">Charging control</string>
+ <string name="charging_control_notification_title">Charging control</string>
+ <string name="charging_control_notification_cancel_once">Cancel</string>
+ <string name="charging_control_notification_content_limit">Battery will be charged to %1$d%%</string>
+ <string name="charging_control_notification_content_limit_reached">Battery is charged to %1$d%%</string>
+ <string name="charging_control_notification_content_target">Battery will be fully charged at %1$s</string>
+ <string name="charging_control_notification_content_target_reached">Battery is charged</string>
+</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b879c97..d8191c0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -177,7 +177,7 @@
<color name="system_notification_accent_color">#00000000</color>
<!-- Default user icon colors -->
- <color name="user_icon_1">#ffe46962</color><!-- red -->
+ <color name="user_icon_1">@color/system_accent1_600</color>
<color name="user_icon_2">#ffaf5cf7</color><!-- purple -->
<color name="user_icon_3">#ff4c8df6</color><!-- blue -->
<color name="user_icon_4">#fff439a0</color><!-- pink -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8edf42a..752e81d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4946,7 +4946,7 @@
<integer translatable="false" name="config_storageManagerDaystoRetainDefault">90</integer>
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
- <string name="config_headlineFontFamily" translatable="false"></string>
+ <string name="config_headlineFontFamily" translatable="false">@string/config_bodyFontFamily</string>
<!-- Allows setting custom fontFeatureSettings on specific text. -->
<string name="config_headlineFontFeatureSettings" translatable="false"></string>
@@ -5031,7 +5031,7 @@
<!-- Controls whether system buttons use all caps for text -->
<bool name="config_buttonTextAllCaps">true</bool>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
- <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
+ <string name="config_headlineFontFamilyMedium" translateable="false">@string/config_bodyFontFamilyMedium</string>
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">sans-serif</string>
<!-- Name of a font family to use for medium body text. -->
diff --git a/core/res/res/values/donottranslate_material.xml b/core/res/res/values/donottranslate_material.xml
index 9cf9f6cf..013b9a8 100644
--- a/core/res/res/values/donottranslate_material.xml
+++ b/core/res/res/values/donottranslate_material.xml
@@ -17,16 +17,16 @@
<resources>
<string name="font_family_display_4_material">sans-serif-light</string>
- <string name="font_family_display_3_material">sans-serif</string>
- <string name="font_family_display_2_material">sans-serif</string>
- <string name="font_family_display_1_material">sans-serif</string>
- <string name="font_family_headline_material">sans-serif</string>
- <string name="font_family_title_material">sans-serif-medium</string>
- <string name="font_family_subhead_material">sans-serif</string>
- <string name="font_family_menu_material">sans-serif</string>
- <string name="font_family_body_2_material">sans-serif-medium</string>
- <string name="font_family_body_1_material">sans-serif</string>
- <string name="font_family_caption_material">sans-serif</string>
- <string name="font_family_button_material">sans-serif-medium</string>
+ <string name="font_family_display_3_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_display_2_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_display_1_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_headline_material">@string/config_headlineFontFamily</string>
+ <string name="font_family_title_material">@string/config_headlineFontFamilyMedium</string>
+ <string name="font_family_subhead_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_menu_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_body_2_material">@string/config_bodyFontFamilyMedium</string>
+ <string name="font_family_body_1_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_caption_material">@string/config_bodyFontFamily</string>
+ <string name="font_family_button_material">@string/config_bodyFontFamilyMedium</string>
</resources>
diff --git a/core/res/res/values/leaf_config.xml b/core/res/res/values/leaf_config.xml
new file mode 100644
index 0000000..9d3eb9c
--- /dev/null
+++ b/core/res/res/values/leaf_config.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The LeafOS Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <!-- Whether to cleanup fingerprints upon connection to the daemon and when
+ user switches -->
+ <bool name="config_cleanupUnusedFingerprints">true</bool>
+
+ <!-- Whether charging control should be enabled by default -->
+ <bool name="config_chargingControlEnabled">false</bool>
+ <!-- Default charging control mode.
+ This integer should be set to:
+ 1 - auto - Use the alarm to calculate the time range when to activate charging control
+ 2 - custom - Use time range when the device is usually charging for hours
+ 3 - limit - Just limit charging -->
+ <integer name="config_defaultChargingControlMode">1</integer>
+ <!-- Default time when charging control is activated.
+ Represented as seconds from midnight (e.g. 79200 == 10pm). -->
+ <integer name="config_defaultChargingControlStartTime">79200</integer>
+ <!-- Default time when battery will be fully charged.
+ Represented as seconds from midnight (e.g. 21600 == 6am). -->
+ <integer name="config_defaultChargingControlTargetTime">21600</integer>
+ <!-- Default charging limit. -->
+ <integer name="config_defaultChargingControlLimit">80</integer>
+ <!-- Considering the fact that the system might have an incorrect estimation of the time to
+ full. Set a time margin to make the device fully charged before the target time arrives.
+ The unit is minutes and the default value is 30 minutes. If you find that it is not enough
+ to make the device to be fully charged at the target time, increase the value
+ -->
+ <integer name="config_chargingControlTimeMargin">30</integer>
+ <!-- For a device that cannot bypass battery when charging stops (that is, the battery current
+ is 0mA when charging stops), the battery will gradually discharge. So we need to make it
+ recharge when the battery level is lower than a threshold. Set this so that the device
+ will be charged between (limit - val) and limit. -->
+ <integer name="config_chargingControlBatteryRechargeMargin">10</integer>
+
+ <!-- List of system apps that are allowed to be locked with app lock.
+ Use with extreme caution. -->
+ <string-array name="config_appLockAllowedSystemApps" translatable="false" />
+</resources>
diff --git a/core/res/res/values/leaf_strings.xml b/core/res/res/values/leaf_strings.xml
new file mode 100644
index 0000000..47cb9a8
--- /dev/null
+++ b/core/res/res/values/leaf_strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2023 The LeafOS Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- App lock -->
+ <string name="unlock_application">Unlock <xliff:g id="label" example="Telegram">%1$s</xliff:g></string>
+
+</resources>
diff --git a/core/res/res/values/leaf_symbols.xml b/core/res/res/values/leaf_symbols.xml
new file mode 100644
index 0000000..03ce27f
--- /dev/null
+++ b/core/res/res/values/leaf_symbols.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The LeafOS Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <!-- Whether to cleanup fingerprints upon connection to the daemon and when
+ user switches -->
+ <java-symbol type="bool" name="config_cleanupUnusedFingerprints" />
+
+ <!-- Health interface -->
+ <java-symbol type="bool" name="config_chargingControlEnabled" />
+ <java-symbol type="integer" name="config_defaultChargingControlMode" />
+ <java-symbol type="integer" name="config_defaultChargingControlStartTime" />
+ <java-symbol type="integer" name="config_defaultChargingControlTargetTime" />
+ <java-symbol type="integer" name="config_defaultChargingControlLimit" />
+ <java-symbol type="drawable" name="ic_charging_control" />
+ <java-symbol type="integer" name="config_chargingControlTimeMargin" />
+ <java-symbol type="integer" name="config_chargingControlBatteryRechargeMargin" />
+ <java-symbol type="string" name="charging_control_notification_channel" />
+ <java-symbol type="string" name="charging_control_notification_title" />
+ <java-symbol type="string" name="charging_control_notification_cancel_once" />
+ <java-symbol type="string" name="charging_control_notification_content_limit" />
+ <java-symbol type="string" name="charging_control_notification_content_limit_reached" />
+ <java-symbol type="string" name="charging_control_notification_content_target" />
+ <java-symbol type="string" name="charging_control_notification_content_target_reached" />
+
+ <!-- For dynamic default font styles -->
+ <java-symbol type="string" name="config_bodyFontFamily" />
+
+ <!-- App lock -->
+ <java-symbol type="string" name="unlock_application" />
+ <java-symbol type="array" name="config_appLockAllowedSystemApps" />
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59066eb..d79dd39 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1016,6 +1016,18 @@
<!-- Permissions -->
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fakePackageSignature">Spoof package signature</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fakePackageSignature">Allows the app to pretend to be a different app. Malicious applications might be able to use this to access private application data. Legitimate uses include an emulator pretending to be what it emulates. Grant this permission with caution only!</string>
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permgrouplab_fake_package_signature">Spoof package signature</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permgroupdesc_fake_package_signature">allow to spoof package signature</string>
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+ <string name="permgrouprequest_fake_package_signature">Allow
+ <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to spoof package signature?</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_statusBar">disable or modify status bar</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_statusBar">Allows the app to disable the status bar or add and remove system icons.</string>
@@ -5158,16 +5170,16 @@
<string name="time_placeholder">--</string>
<!-- DO NOT TRANSLATE -->
- <string name="radial_numbers_typeface">sans-serif</string>
+ <string name="radial_numbers_typeface">@string/config_bodyFontFamily</string>
<!-- DO NOT TRANSLATE -->
- <string name="sans_serif">sans-serif</string>
+ <string name="sans_serif">@string/config_bodyFontFamily</string>
<!-- DO NOT TRANSLATE -->
- <string name="date_picker_month_typeface">sans-serif-medium</string>
+ <string name="date_picker_month_typeface">@string/config_bodyFontFamilyMedium</string>
<!-- DO NOT TRANSLATE -->
- <string name="date_picker_day_of_week_typeface">sans-serif-medium</string>
+ <string name="date_picker_day_of_week_typeface">@string/config_bodyFontFamilyMedium</string>
<!-- DO NOT TRANSLATE -->
- <string name="date_picker_day_typeface">sans-serif-medium</string>
+ <string name="date_picker_day_typeface">@string/config_bodyFontFamilyMedium</string>
<!-- Lock-to-app unlock pin string -->
<string name="lock_to_app_unlock_pin">Ask for PIN before unpinning</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5945f81..6127939 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -997,7 +997,7 @@
</style>
<style name="TextAppearance.Tooltip">
- <item name="fontFamily">sans-serif</item>
+ <item name="fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="textSize">14sp</item>
</style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index eec6ae3..2446054 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -429,7 +429,7 @@
<style name="TextAppearance.Material.TimePicker.InputHeader" parent="TextAppearance.Material">
<item name="textSize">@dimen/text_size_display_1_material</item>
<item name="textColor">@color/white</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.Material.TimePicker.InputField" parent="TextAppearance.Material">
@@ -438,31 +438,31 @@
<style name="TextAppearance.Material.TimePicker.PromptLabel" parent="TextAppearance.Material">
<item name="textSize">@dimen/timepicker_text_size_normal</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
<item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="textSize">@dimen/date_picker_year_label_size</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.Material.DatePicker.DateLabel" parent="TextAppearance.Material">
<item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="textSize">@dimen/date_picker_date_label_size</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.Material.DatePicker.List.YearLabel" parent="TextAppearance.Material">
<item name="textColor">?attr/textColorPrimary</item>
<item name="textSize">@dimen/datepicker_list_year_label_size</item>
- <item name="fontFamily">sans-serif</item>
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.Material.DatePicker.List.YearLabel.Activated">
<item name="textColor">?attr/colorControlActivated</item>
<item name="textSize">@dimen/datepicker_list_year_activated_label_size</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.Material.Notification">
@@ -474,13 +474,13 @@
<style name="TextAppearance.Material.Notification.Title">
<item name="textColor">@color/notification_primary_text_color_current</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
<item name="textSize">@dimen/notification_title_text_size</item>
</style>
<style name="TextAppearance.Material.Notification.BigTitle">
<item name="textColor">@color/notification_primary_text_color_current</item>
- <item name="fontFamily">sans-serif-medium</item>
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
<item name="textSize">@dimen/notification_big_title_text_size</item>
</style>
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternViewTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternViewTest.java
index 8ba4966..fd265c2 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternViewTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternViewTest.java
@@ -148,7 +148,7 @@
MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, mDot1x, mDot1y, 1));
mLockPatternView.onTouchEvent(
MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, mDot1x, mDot1y, 1));
- verify(mPatternListener).onPatternDetected(any());
+ verify(mPatternListener).onPatternDetected(any(), LockPatternUtils.PATTERN_SIZE_DEFAULT);
}
@UiThreadTest
@@ -183,7 +183,8 @@
mLockPatternView.onTouchEvent(
MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, mDot2x, mDot2y, 1));
- verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture());
+ verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture(),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
List<LockPatternView.Cell> patternCells = mCellsArgumentCaptor.getValue();
assertThat(patternCells, hasSize(2));
assertThat(patternCells,
@@ -200,7 +201,8 @@
mLockPatternView.onTouchEvent(
MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, mDot5x, mDot5y, 1));
- verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture());
+ verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture(),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
List<LockPatternView.Cell> patternCells = mCellsArgumentCaptor.getValue();
assertThat(patternCells, hasSize(2));
assertThat(patternCells,
@@ -220,7 +222,8 @@
MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, mViewSize - mDefaultError,
mViewSize - mDefaultError, 1));
- verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture());
+ verify(mPatternListener).onPatternDetected(mCellsArgumentCaptor.capture(),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
List<LockPatternView.Cell> patternCells = mCellsArgumentCaptor.getValue();
assertThat(patternCells, hasSize(7));
assertThat(patternCells,
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
index 5692742..8e735102 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -83,12 +83,12 @@
@Test
public void testPatternCredential() {
LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList(
- LockPatternView.Cell.of(0, 0),
- LockPatternView.Cell.of(0, 1),
- LockPatternView.Cell.of(0, 2),
- LockPatternView.Cell.of(1, 2),
- LockPatternView.Cell.of(2, 2)
- ));
+ LockPatternView.Cell.of(0, 0, LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternView.Cell.of(0, 1, LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternView.Cell.of(0, 2, LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternView.Cell.of(1, 2, LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternView.Cell.of(2, 2, LockPatternUtils.PATTERN_SIZE_DEFAULT)
+ ), LockPatternUtils.PATTERN_SIZE_DEFAULT);
assertTrue(pattern.isPattern());
assertEquals(5, pattern.size());
@@ -337,6 +337,7 @@
private LockscreenCredential createPattern(String patternString) {
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
- patternString.getBytes()));
+ patternString.getBytes(), LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
}
}
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 47e2e38..1bd1ac4 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -20,6 +20,7 @@
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.START_TASKS_FROM_RECENTS"/>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index fbe1b8e..c8aaf42 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -66,5 +66,6 @@
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<permission name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS" />
+ <permission name="com.android.permission.MANAGE_APP_LOCK" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index ce2543a..462a172 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -68,6 +68,7 @@
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.WATCH_APPOPS"/>
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
<permission name="android.permission.WRITE_DREAM_STATE"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 4c4e8fa..14cb0ec 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -29,6 +29,7 @@
import android.annotation.UiThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontStyle;
@@ -69,11 +70,13 @@
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -143,6 +146,8 @@
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
private static final Object sDynamicCacheLock = new Object();
+ // For dynamic default font styles
+ private static final HashMap<String, Typeface> sSystemFontOverrides = new HashMap<>();
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
static Typeface sDefaultTypeface;
@@ -896,7 +901,7 @@
* @return The best matching typeface.
*/
public static Typeface create(String familyName, @Style int style) {
- return create(getSystemDefaultTypeface(familyName), style);
+ return create(getSystemOverrideTypeface(familyName), style);
}
/**
@@ -1212,6 +1217,11 @@
mCleaner.run();
}
+ private static Typeface getSystemOverrideTypeface(@NonNull String familyName) {
+ Typeface tf = sSystemFontOverrides.get(familyName);
+ return tf == null ? getSystemDefaultTypeface(familyName) : tf;
+ }
+
private static Typeface getSystemDefaultTypeface(@NonNull String familyName) {
Typeface tf = sSystemFontMap.get(familyName);
return tf == null ? Typeface.DEFAULT : tf;
@@ -1393,6 +1403,74 @@
}
}
+ private static void setPublicDefaults(String familyName) {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ sDefaults = new Typeface[] {
+ DEFAULT,
+ DEFAULT_BOLD,
+ create(getSystemDefaultTypeface(familyName), Typeface.ITALIC),
+ create(getSystemDefaultTypeface(familyName), Typeface.BOLD_ITALIC),
+ };
+ }
+ }
+
+ private static void setFinalField(String fieldName, Typeface value) {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ try {
+ Field field = Typeface.class.getDeclaredField(fieldName);
+ // isAccessible bypasses final on ART
+ field.setAccessible(true);
+ field.set(null, value);
+ field.setAccessible(false);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ Log.e(TAG, "Failed to set Typeface." + fieldName, e);
+ }
+ }
+ }
+
+ /** @hide */
+ public static void updateDefaultFont(Resources res) {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ String familyName = res.getString(com.android.internal.R.string.config_bodyFontFamily);
+ String headlineFamilyName = res.getString(
+ com.android.internal.R.string.config_headlineFontFamily);
+ Typeface typeface = sSystemFontMap.get(familyName);
+ Typeface headlineTypeface = sSystemFontMap.get(headlineFamilyName);
+ if (typeface == null) {
+ // This should never happen, but if the system font family name is invalid, just
+ // return instead of crashing the app.
+ return;
+ }
+ if (headlineTypeface == null) {
+ // Fallback to body font
+ headlineTypeface = typeface;
+ }
+
+ setDefault(typeface);
+
+ // Static typefaces in public API
+ setFinalField("DEFAULT", create(getSystemDefaultTypeface(familyName), 0));
+ setFinalField("DEFAULT_BOLD", create(getSystemDefaultTypeface(familyName), Typeface.BOLD));
+ setFinalField("SANS_SERIF", DEFAULT);
+
+ // For default aliases used in framework styles
+ sSystemFontOverrides.put("sans-serif", typeface);
+ sSystemFontOverrides.put("sans-serif-thin", create(typeface, 100, false));
+ sSystemFontOverrides.put("sans-serif-light", create(typeface, 300, false));
+ sSystemFontOverrides.put("sans-serif-medium", create(typeface, 500, false));
+ sSystemFontOverrides.put("sans-serif-black", create(typeface, 900, false));
+
+ setPublicDefaults(familyName);
+
+ // Replace google fonts
+ sSystemFontOverrides.put("google-sans-text", typeface);
+ sSystemFontOverrides.put("google-sans-text-medium", create(typeface, 500, false));
+
+ sSystemFontOverrides.put("google-sans", headlineTypeface);
+ sSystemFontOverrides.put("google-sans-medium", create(headlineTypeface, 500, false));
+ }
+ }
+
/** @hide */
@VisibleForTesting
public static void setSystemFontMap(Map<String, Typeface> systemFontMap) {
@@ -1413,12 +1491,7 @@
nativeForceSetStaticFinalField("SERIF", create("serif", 0));
nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0));
- sDefaults = new Typeface[]{
- DEFAULT,
- DEFAULT_BOLD,
- create((String) null, Typeface.ITALIC),
- create((String) null, Typeface.BOLD_ITALIC),
- };
+ setPublicDefaults(null);
// A list of generic families to be registered in native.
// https://www.w3.org/TR/css-fonts-4/#generic-font-families
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index e6a63b9..6a80c14 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -48,6 +48,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.gmscompat.AttestationHooks;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -178,6 +179,8 @@
@Override
public Certificate[] engineGetCertificateChain(String alias) {
+ AttestationHooks.onEngineGetCertificateChain();
+
KeyEntryResponse response = getKeyMetadata(alias);
if (response == null || response.metadata.certificate == null) {
diff --git a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
index d29ed8b..8835289 100644
--- a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
+++ b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
@@ -42,7 +42,7 @@
android:layout_marginBottom="0dp"
android:gravity="center_horizontal"
android:textAlignment="center"
- android:fontFamily="google-sans-medium"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:text="@string/one_handed_tutorial_title"
android:textSize="16sp"
android:textColor="@android:color/white"/>
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4be282b..8bd62d9 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -722,12 +722,6 @@
*/
@CapturePolicy
public int getAllowedCapturePolicy() {
- if ((mFlags & FLAG_NO_SYSTEM_CAPTURE) == FLAG_NO_SYSTEM_CAPTURE) {
- return ALLOW_CAPTURE_BY_NONE;
- }
- if ((mFlags & FLAG_NO_MEDIA_PROJECTION) == FLAG_NO_MEDIA_PROJECTION) {
- return ALLOW_CAPTURE_BY_SYSTEM;
- }
return ALLOW_CAPTURE_BY_ALL;
}
@@ -1794,20 +1788,7 @@
* @hide
*/
public static int capturePolicyToFlags(@CapturePolicy int capturePolicy, int flags) {
- switch (capturePolicy) {
- case ALLOW_CAPTURE_BY_NONE:
- flags |= FLAG_NO_MEDIA_PROJECTION | FLAG_NO_SYSTEM_CAPTURE;
- break;
- case ALLOW_CAPTURE_BY_SYSTEM:
- flags |= FLAG_NO_MEDIA_PROJECTION;
- flags &= ~FLAG_NO_SYSTEM_CAPTURE;
- break;
- case ALLOW_CAPTURE_BY_ALL:
- flags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION;
- break;
- default:
- throw new IllegalArgumentException("Unknown allow playback capture policy");
- }
+ flags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION;
return flags;
}
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
index 1e63a67e..1fb8997 100644
--- a/packages/PrintSpooler/res/values/styles.xml
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -28,7 +28,7 @@
<item name="android:layout_marginTop">16dip</item>
<item name="android:layout_marginBottom">16dip</item>
<item name="android:textColor">?android:attr/colorAccent</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:textSize">14sp</item>
</style>
-</resources>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index f958037..1cd5460 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -49,7 +49,7 @@
<item name="android:layout_gravity">center</item>
<item name="android:textSize">18sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:layout_marginTop">8dp</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index f306918..ceb7b86 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -36,7 +36,6 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
-import com.android.settingslib.development.DevelopmentSettingsEnabler
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.CopyableBody
@@ -87,16 +86,14 @@
}
@Composable
- fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
+ fun FooterAppVersion() {
val context = LocalContext.current
- val footer = remember(packageInfo, showPackageName) {
+ val footer = remember(packageInfo) {
val list = mutableListOf<String>()
packageInfo.versionNameBidiWrapped?.let {
list += context.getString(R.string.version_text, it)
}
- if (showPackageName) {
- list += packageInfo.packageName
- }
+ list += packageInfo.packageName
list.joinToString(separator = System.lineSeparator())
}
if (footer.isBlank()) return
@@ -106,14 +103,6 @@
}
}
- @Composable
- private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
- val context = LocalContext.current
- return remember {
- DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
- }
- }
-
private companion object {
/** Wrapped the version name, so its directionality still keep same when RTL. */
val PackageInfo.versionNameBidiWrapped: String?
diff --git a/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml
index 1d048ae..1272ea7 100644
--- a/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml
+++ b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml
@@ -14,20 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="19.41dp"
- android:height="15dp"
- android:viewportWidth="22"
- android:viewportHeight="17">
-
+ android:width="27dp"
+ android:height="16dp"
+ android:viewportWidth="27.0"
+ android:viewportHeight="16.0">
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M5.32,10.13h1.11v1.03H5.32v2.31H4.11v-2.31H0.35v-0.75l3.7-6.9h1.27V10.13z M1.69,10.13h2.42V5.4L1.69,10.13z" />
+ android:fillColor="#FF000000"
+ android:pathData="M2,10.98V9.81l4.28,-6.83h1.6v6.59h1.19v1.41H7.88V13H6.4v-2.02H2zM3.68,9.57H6.4V5.33H6.31L3.68,9.57z"/>
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M14.15,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H12.9 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.97,6.37,8.96,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
+ android:fillColor="#FF000000"
+ android:pathData="M15,13.22c-0.88,0 -1.66,-0.21 -2.34,-0.64c-0.67,-0.44 -1.2,-1.05 -1.6,-1.83c-0.38,-0.79 -0.57,-1.71 -0.57,-2.76c0,-1.05 0.21,-1.96 0.62,-2.74c0.41,-0.79 0.97,-1.4 1.67,-1.83c0.7,-0.44 1.5,-0.66 2.39,-0.66c1.09,0 2.01,0.25 2.74,0.76c0.75,0.5 1.22,1.21 1.41,2.11l-1.47,0.39c-0.17,-0.56 -0.48,-1 -0.94,-1.32c-0.45,-0.33 -1.03,-0.49 -1.75,-0.49c-0.57,0 -1.09,0.14 -1.57,0.43c-0.48,0.29 -0.85,0.71 -1.13,1.27c-0.28,0.56 -0.42,1.25 -0.42,2.07c0,0.81 0.14,1.5 0.41,2.07c0.28,0.56 0.65,0.98 1.11,1.27c0.47,0.29 0.98,0.43 1.54,0.43c0.57,0 1.06,-0.11 1.47,-0.34c0.42,-0.23 0.75,-0.55 0.99,-0.94c0.25,-0.4 0.41,-0.85 0.46,-1.36h-2.88V7.79h4.37c0,0.87 0,1.74 0,2.6c0,0.87 0,1.74 0,2.6h-1.41v-1.4h-0.08c-0.28,0.49 -0.67,0.88 -1.18,1.18C16.34,13.07 15.73,13.22 15,13.22z"/>
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M 19.3 5.74 L 19.3 3.39 L 18 3.39 L 18 5.74 L 15.65 5.74 L 15.65 7.04 L 18 7.04 L 18 9.39 L 19.3 9.39 L 19.3 7.04 L 21.65 7.04 L 21.65 5.74 Z" />
- <path
- android:pathData="M 0 0 H 22 V 17 H 0 V 0 Z" />
+ android:fillColor="#FF000000"
+ android:pathData="M24.62 5.24 24.62 2.89 23.32 2.89 23.32 5.24 20.97 5.24 20.97 6.54 23.32 6.54 23.32 8.89 24.62 8.89 24.62 6.54 26.97 6.54 26.97 5.24Z"/>
</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml
index e5cdff3..d1f4b6f 100644
--- a/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml
+++ b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml
@@ -14,23 +14,20 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="22.94dp"
- android:height="15dp"
- android:viewportWidth="26"
- android:viewportHeight="17">
-
+ android:width="31dp"
+ android:height="16dp"
+ android:viewportWidth="31.0"
+ android:viewportHeight="16.0">
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M1.59,12.4h3.9v1.07H0.33V3.52h1.26V12.4z" />
+ android:fillColor="#FF000000"
+ android:pathData="M2,13V2.98h1.53v8.57H8.3V13H2z"/>
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M11.35,4.6H8.73v8.88H7.48V4.6H4.87V3.52h6.48V4.6z" />
+ android:fillColor="#FF000000"
+ android:pathData="M11.24,13V4.43H8.19V2.98h7.63v1.46h-3.05V13H11.24z"/>
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M17.59,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
+ android:fillColor="#FF000000"
+ android:pathData="M17.41,13V2.98h6.36v1.46h-4.83v2.65h4.4v1.46h-4.4v3.01h4.83V13H17.41z"/>
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M 23.32 5.74 L 23.32 3.39 L 22.02 3.39 L 22.02 5.74 L 19.67 5.74 L 19.67 7.04 L 22.02 7.04 L 22.02 9.39 L 23.32 9.39 L 23.32 7.04 L 25.67 7.04 L 25.67 5.74 Z" />
- <path
- android:pathData="M 0 0 H 26 V 17 H 0 V 0 Z" />
+ android:fillColor="#FF000000"
+ android:pathData="M28.72 5.24 28.72 2.89 27.42 2.89 27.42 5.24 25.07 5.24 25.07 6.54 27.42 6.54 27.42 8.89 28.72 8.89 28.72 6.54 31.07 6.54 31.07 5.24Z"/>
</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
index 840c936..9c0c9f5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
@@ -231,6 +233,10 @@
config.hspaDataDistinguishable =
res.getBoolean(R.bool.config_hspa_data_distinguishable);
+ config.show4gForLte = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.SHOW_FOURG_ICON, 0,
+ UserHandle.USER_CURRENT) == 1;
+
CarrierConfigManager configMgr = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
// Handle specific carrier config values for the default data SIM
@@ -240,10 +246,6 @@
if (b != null) {
config.alwaysShowDataRatIcon = b.getBoolean(
CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
- config.show4gForLte = b.getBoolean(
- CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
- config.show4glteForLte = b.getBoolean(
- CarrierConfigManager.KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL);
config.show4gFor3g = b.getBoolean(
CarrierConfigManager.KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL);
config.hideLtePlus = b.getBoolean(
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index d088c3b..0c4e9c6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -26,6 +26,7 @@
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
+import android.net.wifi.WifiManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
@@ -39,6 +40,7 @@
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.Locale;
+import java.util.Set;
public class DataUsageController {
@@ -52,6 +54,7 @@
private final Context mContext;
private final NetworkPolicyManager mPolicyManager;
private final NetworkStatsManager mNetworkStatsManager;
+ private final WifiManager mWifiManager;
private Callback mCallback;
private NetworkNameProvider mNetworkController;
@@ -61,6 +64,7 @@
mContext = context;
mPolicyManager = NetworkPolicyManager.from(mContext);
mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
@@ -94,6 +98,42 @@
return null;
}
+ public DataUsageInfo getDataUsageInfo() {
+ NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
+
+ return getDataUsageInfo(template);
+ }
+
+ public DataUsageInfo getDailyDataUsageInfo() {
+ NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
+
+ return getDailyDataUsageInfo(template);
+ }
+
+ public DataUsageInfo getWifiDataUsageInfo() {
+ return getWifiDataUsageInfo(false);
+ }
+
+ public DataUsageInfo getWifiDataUsageInfo(boolean currentNetwork) {
+ return getDataUsageInfo(getWifiNetworkTemplate(currentNetwork));
+ }
+
+ public DataUsageInfo getWifiDailyDataUsageInfo(boolean currentNetwork) {
+ return getDailyDataUsageInfo(getWifiNetworkTemplate(currentNetwork));
+ }
+
+ public NetworkTemplate getWifiNetworkTemplate(boolean currentNetwork) {
+ final NetworkTemplate.Builder builder =
+ new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI);
+ if (currentNetwork) {
+ final String networkKey = mWifiManager.getConnectionInfo().getNetworkKey();
+ if (networkKey != null) {
+ builder.setWifiNetworkKeys(Set.of(networkKey));
+ }
+ }
+ return builder.build();
+ }
+
public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
final NetworkPolicy policy = findNetworkPolicy(template);
final long now = System.currentTimeMillis();
@@ -131,6 +171,34 @@
return usage;
}
+ public DataUsageInfo getDailyDataUsageInfo(NetworkTemplate template) {
+ final NetworkPolicy policy = findNetworkPolicy(template);
+ final long end = System.currentTimeMillis();
+ long start = end - DataUsageUtils.getTodayMillis();
+
+ final long totalBytes = getUsageLevel(template, start, end);
+ if (totalBytes < 0L) {
+ return warn("no entry data");
+ }
+ final DataUsageInfo usage = new DataUsageInfo();
+ usage.startDate = start;
+ usage.usageLevel = totalBytes;
+ usage.period = formatDateRange(start, end);
+ usage.cycleStart = start;
+ usage.cycleEnd = end;
+
+ if (policy != null) {
+ usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
+ usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
+ } else {
+ usage.warningLevel = getDefaultWarningLevel();
+ }
+ if (usage != null && mNetworkController != null) {
+ usage.carrier = mNetworkController.getMobileDataNetworkName();
+ }
+ return usage;
+ }
+
/**
* Get the total usage level recorded in the network history
* @param template the network template to retrieve the network history
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
new file mode 100644
index 0000000..bfb31e0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.content.Context;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.format.Time;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utils class for data usage
+ */
+public class DataUsageUtils {
+ private static final String TAG = "DataUsageUtils";
+
+ /**
+ * Return mobile NetworkTemplate based on {@code subId}
+ */
+ public static NetworkTemplate getMobileTemplate(Context context, int subId) {
+ final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ final int mobileDefaultSubId = telephonyManager.getSubscriptionId();
+
+ final SubscriptionManager subscriptionManager =
+ context.getSystemService(SubscriptionManager.class);
+ final List<SubscriptionInfo> subInfoList =
+ subscriptionManager.getAvailableSubscriptionInfoList();
+ if (subInfoList == null) {
+ Log.i(TAG, "Subscription is not inited: " + subId);
+ return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId);
+ }
+
+ for (SubscriptionInfo subInfo : subInfoList) {
+ if ((subInfo != null) && (subInfo.getSubscriptionId() == subId)) {
+ return getNormalizedMobileTemplate(telephonyManager, subId);
+ }
+ }
+ Log.i(TAG, "Subscription is not active: " + subId);
+ return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId);
+ }
+
+ private static NetworkTemplate getNormalizedMobileTemplate(
+ TelephonyManager telephonyManager, int subId) {
+ final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId);
+ final String[] mergedSubscriberIds = telephonyManager
+ .createForSubscriptionId(subId).getMergedImsisFromGroup();
+ if (ArrayUtils.isEmpty(mergedSubscriberIds)) {
+ Log.i(TAG, "mergedSubscriberIds is null.");
+ return mobileTemplate;
+ }
+
+ return normalizeMobileTemplate(mobileTemplate, Set.of(mergedSubscriberIds));
+ }
+
+ private static NetworkTemplate normalizeMobileTemplate(
+ NetworkTemplate template, Set<String> mergedSet) {
+ if (template.getSubscriberIds().isEmpty()) return template;
+ // The input template should have at most 1 subscriberId.
+ final String subscriberId = template.getSubscriberIds().iterator().next();
+
+ if (mergedSet.contains(subscriberId)) {
+ // Requested template subscriber is part of the merge group; return
+ // a template that matches all merged subscribers.
+ return new NetworkTemplate.Builder(template.getMatchRule())
+ .setSubscriberIds(mergedSet)
+ .setWifiNetworkKeys(template.getWifiNetworkKeys())
+ .setMeteredness(NetworkStats.METERED_YES).build();
+ }
+
+ return template;
+ }
+
+ private static NetworkTemplate getMobileTemplateForSubId(
+ TelephonyManager telephonyManager, int subId) {
+ // Create template that matches any mobile network when the subscriberId is null.
+ String subscriberId = telephonyManager.getSubscriberId(subId);
+ return subscriberId != null
+ ? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setSubscriberIds(Set.of(subscriberId))
+ .setMeteredness(NetworkStats.METERED_YES)
+ .build()
+ : new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(NetworkStats.METERED_YES)
+ .build();
+ }
+
+ /**
+ * Returns today's passed time in Millisecond
+ */
+ public static long getTodayMillis() {
+ final long passedMillis;
+ Time time = new Time();
+ time.set(System.currentTimeMillis());
+ passedMillis = ((time.hour * 60 * 60) + (time.minute * 60) + time.second) * 1000;
+ return passedMillis;
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2fa1c6e..e0c0f74 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -267,6 +267,7 @@
Settings.Secure.EVEN_DIMMER_MIN_NITS,
Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
Settings.Secure.CAMERA_EXTENSIONS_FALLBACK,
- Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED,
+ Settings.Secure.TETHERING_ALLOW_VPN_UPSTREAMS
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 4c255a5..e748ba8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -114,7 +114,9 @@
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
Settings.System.NOTIFICATION_COOLDOWN_ALL,
- Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ Settings.System.NETWORK_TRAFFIC_STATE,
+ Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD
));
if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2535fdb..219c751 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -422,5 +422,6 @@
VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
VALIDATORS.put(Secure.STYLUS_POINTER_ICON_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.CAMERA_EXTENSIONS_FALLBACK, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.TETHERING_ALLOW_VPN_UPSTREAMS, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 011b42f..cc8eeca 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -246,5 +246,8 @@
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NETWORK_TRAFFIC_STATE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.ACCELEROMETER_ROTATION_ANGLES, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ec50323..3e49840 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -707,6 +707,9 @@
Secure.LOCK_PATTERN_ENABLED,
Secure.LOCK_PATTERN_VISIBLE,
Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED,
+ Secure.LOCK_PATTERN_SIZE,
+ Secure.LOCK_DOTS_VISIBLE,
+ Secure.LOCK_SHOW_ERROR_PATH,
"lockscreen.password_type",
"lockscreen.lockoutattemptdeadline",
"lockscreen.patterneverchosen",
@@ -1951,10 +1954,11 @@
// Convert lock pattern
try {
LockPatternUtils lpu = new LockPatternUtils(mContext);
+ byte size = lpu.getLockPatternSize(mUserHandle);
List<LockPatternView.Cell> cellPattern =
- LockPatternUtils.byteArrayToPattern(lockPattern.getBytes());
+ LockPatternUtils.byteArrayToPattern(lockPattern.getBytes(), size);
lpu.setLockCredential(
- LockscreenCredential.createPattern(cellPattern),
+ LockscreenCredential.createPattern(cellPattern, size),
LockscreenCredential.createNone(),
UserHandle.USER_SYSTEM);
} catch (IllegalArgumentException e) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cc2e84c..92c5ad0 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -164,6 +164,7 @@
"androidx.compose.material_material-icons-extended",
"androidx.activity_activity-compose",
"androidx.compose.animation_animation-graphics",
+ "vendor.lineage.powershare-V1.0-java",
],
libs: [
"keepanno-annotations",
@@ -331,6 +332,7 @@
"kotlin-test",
"SystemUICustomizationTestUtils",
"androidx.compose.runtime_runtime",
+ "vendor.lineage.powershare-V1.0-java",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 12e8f57..5c83275 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -369,6 +369,9 @@
<protected-broadcast android:name="com.android.systemui.action.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
<protected-broadcast android:name="com.android.systemui.STARTED" />
+ <!-- DataSwitch tile -->
+ <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
@@ -498,7 +501,7 @@
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"
- android:theme="@style/TunerSettings"
+ android:theme="@style/Theme.SubSettingsBase"
android:label="@string/system_ui_tuner"
android:process=":tuner"
android:exported="true">
@@ -512,6 +515,20 @@
android:resource="@string/summary_empty"/>
</activity>
+ <activity-alias
+ android:name=".tuner.StatusBarTuner"
+ android:targetActivity=".tuner.TunerActivity"
+ android:icon="@drawable/tuner"
+ android:theme="@style/Theme.SubSettingsBase"
+ android:label="@string/status_bar_icons_title"
+ android:process=":tuner"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.settings.action.STATUS_BAR_TUNER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity-alias>
+
<activity-alias android:name=".DemoMode"
android:targetActivity=".tuner.TunerActivity"
android:icon="@drawable/tuner"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index ea1cb34..ca52bd1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -391,7 +391,12 @@
)
}
- if (view.parent !is ViewGroup) {
+ var animatedView = view;
+ if (view.getAnimatedView() is View) {
+ view.getAnimatedView() as View
+ }
+
+ if (animatedView.parent !is ViewGroup) {
Log.e(
TAG,
"Skipping animation as view $view is not attached to a ViewGroup",
@@ -400,7 +405,7 @@
return null
}
- return GhostedViewTransitionAnimatorController(view, cujType)
+ return GhostedViewTransitionAnimatorController(animatedView, cujType)
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index dbdf970..1960cb4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -214,7 +214,12 @@
cuj: DialogCuj? = null,
animateBackgroundBoundsChange: Boolean = false
) {
- val controller = Controller.fromView(view, cuj)
+ var animatedView = view;
+ if (animatedView is LaunchableView && animatedView.getAnimatedView() is View) {
+ animatedView = animatedView.getAnimatedView() as View
+ }
+
+ val controller = Controller.fromView(animatedView, cuj)
if (controller == null) {
dialog.show()
} else {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index ed8e705..081a242 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -38,6 +38,15 @@
* @param block whether we should block/postpone all calls to `setVisibility`.
*/
fun setShouldBlockVisibilityChanges(block: Boolean)
+
+ /**
+ * Allows to override the animated view based on certain conditions
+ *
+ * @return The view that should actually be animated
+ */
+ fun getAnimatedView(): LaunchableView {
+ return this
+ }
}
/** A delegate that can be used by views to make the implementation of [LaunchableView] easier. */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 0a5f5d2..3fb9200 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -424,7 +424,7 @@
private fun offset(
availableSize: Int,
spacingPerDot: Float,
- dotCount: Int,
+ dotCount: Byte,
isCentered: Boolean = false,
): Float {
val default = availableSize - spacingPerDot * dotCount
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
index 95ff13b..7775551 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
@@ -26,7 +26,7 @@
int VERSION = 1;
void showGlobalActions(GlobalActionsManager manager);
- default void showShutdownUi(boolean isReboot, String reason) {
+ default void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
}
default void destroy() {
@@ -40,6 +40,6 @@
void onGlobalActionsHidden();
void shutdown();
- void reboot(boolean safeMode);
+ void reboot(boolean safeMode, String reason);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c9e2989..83fd07d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -154,7 +154,7 @@
@ProvidesInterface(version = State.VERSION)
public static class State {
public static final int VERSION = 1;
- public static final int DEFAULT_STATE = Tile.STATE_ACTIVE;
+ public static final int DEFAULT_STATE = Tile.STATE_INACTIVE;
public Icon icon;
public Supplier<Icon> iconSupplier;
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
index 7c5dbc2..6465754 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
@@ -38,7 +38,7 @@
android:id="@+id/row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
+ android:orientation="vertical"
android:gravity="start"
/>
</com.android.keyguard.KeyguardSliceView>
diff --git a/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png b/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png
new file mode 100644
index 0000000..4102e28
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/brightness_bg.xml b/packages/SystemUI/res/drawable/brightness_bg.xml
new file mode 100644
index 0000000..7e1a9a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/brightness_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="24dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ <size android:width="48dp" android:height="48dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_full.xml b/packages/SystemUI/res/drawable/ic_brightness_full.xml
index 1dc7cef..f443332 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_full.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_full.xml
@@ -1,45 +1,29 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_1_G_N_1_T_0"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <group android:name="_R_G_L_1_G">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M66.67 -27.59 C66.67,-27.59 66.67,-66.67 66.67,-66.67 C66.67,-66.67 27.59,-66.67 27.59,-66.67 C27.59,-66.67 0,-94.25 0,-94.25 C0,-94.25 -27.58,-66.67 -27.58,-66.67 C-27.58,-66.67 -66.66,-66.67 -66.66,-66.67 C-66.66,-66.67 -66.66,-27.59 -66.66,-27.59 C-66.66,-27.59 -94.25,0 -94.25,0 C-94.25,0 -66.66,27.58 -66.66,27.58 C-66.66,27.58 -66.66,66.66 -66.66,66.66 C-66.66,66.66 -27.58,66.66 -27.58,66.66 C-27.58,66.66 0,94.25 0,94.25 C0,94.25 27.59,66.66 27.59,66.66 C27.59,66.66 66.67,66.66 66.67,66.66 C66.67,66.66 66.67,27.58 66.67,27.58 C66.67,27.58 94.25,0 94.25,0 C94.25,0 66.67,-27.59 66.67,-27.59c M50 20.66 C50,20.66 50,50 50,50 C50,50 20.67,50 20.67,50 C20.67,50 0,70.66 0,70.66 C0,70.66 -20.66,50 -20.66,50 C-20.66,50 -50,50 -50,50 C-50,50 -50,20.66 -50,20.66 C-50,20.66 -70.66,0 -70.66,0 C-70.66,0 -50,-20.67 -50,-20.67 C-50,-20.67 -50,-50 -50,-50 C-50,-50 -20.66,-50 -20.66,-50 C-20.66,-50 0,-70.67 0,-70.67 C0,-70.67 20.67,-50 20.67,-50 C20.67,-50 50,-50 50,-50 C50,-50 50,-20.67 50,-20.67 C50,-20.67 70.67,0 70.67,0 C70.67,0 50,20.66 50,20.66c " />
- </group>
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c " />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c "
- android:strokeAlpha="1"
- android:strokeColor="#ffffff"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="17" />
- </group>
- </group>
- <group android:name="time_group" />
-</vector>
\ No newline at end of file
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:pathData="M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z"
+ />
+
+ <path
+ android:pathData=" M20,8.69 V4h-4.69L12,0.69L8.69,4H4v4.69L0.69,12L4,15.31V20h4.69L12,23.31L15.31,20H20v-4.69L23.31,12L20,8.69z M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7z"
+ android:fillColor="#FFFFFF" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_low.xml b/packages/SystemUI/res/drawable/ic_brightness_low.xml
index ded7567..b463556 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_low.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_low.xml
@@ -1,52 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+ android:viewportWidth="24"
android:viewportHeight="24"
- android:viewportWidth="24">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_1_G_N_1_T_0"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <group android:name="_R_G_L_1_G">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M66.67 -27.59 C66.67,-27.59 66.67,-66.67 66.67,-66.67 C66.67,-66.67 27.59,-66.67 27.59,-66.67 C27.59,-66.67 0,-94.25 0,-94.25 C0,-94.25 -27.58,-66.67 -27.58,-66.67 C-27.58,-66.67 -66.66,-66.67 -66.66,-66.67 C-66.66,-66.67 -66.66,-27.59 -66.66,-27.59 C-66.66,-27.59 -94.25,0 -94.25,0 C-94.25,0 -66.66,27.58 -66.66,27.58 C-66.66,27.58 -66.66,66.66 -66.66,66.66 C-66.66,66.66 -27.58,66.66 -27.58,66.66 C-27.58,66.66 0,94.25 0,94.25 C0,94.25 27.59,66.66 27.59,66.66 C27.59,66.66 66.67,66.66 66.67,66.66 C66.67,66.66 66.67,27.58 66.67,27.58 C66.67,27.58 94.25,0 94.25,0 C94.25,0 66.67,-27.59 66.67,-27.59c M50 20.66 C50,20.66 50,50 50,50 C50,50 20.67,50 20.67,50 C20.67,50 0,70.66 0,70.66 C0,70.66 -20.66,50 -20.66,50 C-20.66,50 -50,50 -50,50 C-50,50 -50,20.66 -50,20.66 C-50,20.66 -70.66,0 -70.66,0 C-70.66,0 -50,-20.67 -50,-20.67 C-50,-20.67 -50,-50 -50,-50 C-50,-50 -20.66,-50 -20.66,-50 C-20.66,-50 0,-70.67 0,-70.67 C0,-70.67 20.67,-50 20.67,-50 C20.67,-50 50,-50 50,-50 C50,-50 50,-20.67 50,-20.67 C50,-20.67 70.67,0 70.67,0 C70.67,0 50,20.66 50,20.66c " />
- </group>
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <group android:name="_R_G_L_0_C_0_G">
- <clip-path
- android:name="_R_G_L_0_C_0"
- android:pathData=" M-63 -50.5 C-63,-50.5 -63,48 -63,48 C-63,48 42.5,48 42.5,48 C42.5,48 42.5,-50.5 42.5,-50.5 C42.5,-50.5 -63,-50.5 -63,-50.5c " />
- <group android:name="_R_G_L_0_C_0_G_G">
- <path
- android:name="_R_G_L_0_G_G_0_D_0_P_0"
- android:pathData=" M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c "
- android:strokeAlpha="1"
- android:strokeColor="#ffffff"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="17" />
- <path
- android:name="_R_G_L_0_G_G_0_D_1_P_0"
- android:fillAlpha="0"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c " />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
-</vector>
\ No newline at end of file
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_medium.xml b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
index 606c6da..80acc4d 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_medium.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
@@ -1,52 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+ android:viewportWidth="24"
android:viewportHeight="24"
- android:viewportWidth="24">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_1_G_N_1_T_0"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <group android:name="_R_G_L_1_G">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData="M66.67 -27.59 C66.67,-27.59 66.67,-66.67 66.67,-66.67 C66.67,-66.67 27.59,-66.67 27.59,-66.67 C27.59,-66.67 0,-94.25 0,-94.25 C0,-94.25 -27.58,-66.67 -27.58,-66.67 C-27.58,-66.67 -66.66,-66.67 -66.66,-66.67 C-66.66,-66.67 -66.66,-27.59 -66.66,-27.59 C-66.66,-27.59 -94.25,0 -94.25,0 C-94.25,0 -66.66,27.58 -66.66,27.58 C-66.66,27.58 -66.66,66.66 -66.66,66.66 C-66.66,66.66 -27.58,66.66 -27.58,66.66 C-27.58,66.66 0,94.25 0,94.25 C0,94.25 27.59,66.66 27.59,66.66 C27.59,66.66 66.67,66.66 66.67,66.66 C66.67,66.66 66.67,27.58 66.67,27.58 C66.67,27.58 94.25,0 94.25,0 C94.25,0 66.67,-27.59 66.67,-27.59c M50 20.66 C50,20.66 50,50 50,50 C50,50 20.67,50 20.67,50 C20.67,50 0,70.66 0,70.66 C0,70.66 -20.66,50 -20.66,50 C-20.66,50 -50,50 -50,50 C-50,50 -50,20.66 -50,20.66 C-50,20.66 -70.66,0 -70.66,0 C-70.66,0 -50,-20.67 -50,-20.67 C-50,-20.67 -50,-50 -50,-50 C-50,-50 -20.66,-50 -20.66,-50 C-20.66,-50 0,-70.67 0,-70.67 C0,-70.67 20.67,-50 20.67,-50 C20.67,-50 50,-50 50,-50 C50,-50 50,-20.67 50,-20.67 C50,-20.67 70.67,0 70.67,0 C70.67,0 50,20.66 50,20.66c " />
- </group>
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:scaleX="0.1"
- android:scaleY="0.1"
- android:translateX="12"
- android:translateY="12">
- <group android:name="_R_G_L_0_C_0_G">
- <clip-path
- android:name="_R_G_L_0_C_0"
- android:pathData="M-105.5 -50.5 C-105.5,-50.5 -105.5,48 -105.5,48 C-105.5,48 0,48 0,48 C0,48 0,-50.5 0,-50.5 C0,-50.5 -105.5,-50.5 -105.5,-50.5c " />
- <group android:name="_R_G_L_0_C_0_G_G">
- <path
- android:name="_R_G_L_0_G_G_0_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData="M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c " />
- <path
- android:name="_R_G_L_0_G_G_0_D_1_P_0"
- android:pathData="M0 -32.5 C17.95,-32.5 32.5,-17.95 32.5,0 C32.5,17.95 17.95,32.5 0,32.5 C-17.95,32.5 -32.5,17.95 -32.5,0 C-32.5,-17.95 -17.95,-32.5 0,-32.5c "
- android:strokeAlpha="1"
- android:strokeColor="#ffffff"
- android:strokeLineCap="round"
- android:strokeLineJoin="round"
- android:strokeWidth="17" />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
-</vector>
\ No newline at end of file
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,17c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5v10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_internet_hotspot.xml b/packages/SystemUI/res/drawable/ic_internet_hotspot.xml
new file mode 100644
index 0000000..efe34a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_internet_hotspot.xml
@@ -0,0 +1,11 @@
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M 10 8.7 C 8.9 8.7 8 9.6 8 10.7 C 8 11.8 8.9 12.7 10 12.7 C 11.1 12.7 12 11.8 12 10.7 C 12 9.6 11.1 8.7 10 8.7 Z M 16 10.7 C 16 7.39 13.31 4.7 10 4.7 C 6.69 4.7 4 7.39 4 10.7 C 4 12.92 5.21 14.85 7 15.89 L 8 14.15 C 6.81 13.45 6 12.18 6 10.7 C 6 8.49 7.79 6.7 10 6.7 C 12.21 6.7 14 8.49 14 10.7 C 14 12.18 13.19 13.45 12 14.15 L 13 15.89 C 14.79 14.85 16 12.92 16 10.7 Z M 10 0.7 C 4.48 0.7 0 5.18 0 10.7 C 0 14.4 2.01 17.62 4.99 19.35 L 5.99 17.62 C 3.61 16.23 2 13.66 2 10.7 C 2 6.28 5.58 2.7 10 2.7 C 14.42 2.7 18 6.28 18 10.7 C 18 13.66 16.39 16.23 14 17.62 L 15 19.35 C 17.99 17.62 20 14.4 20 10.7 C 20 5.18 15.52 0.7 10 0.7 Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml b/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml
new file mode 100644
index 0000000..2d30019
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_internet_hotspot_disabled.xml
@@ -0,0 +1,11 @@
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="21dp"
+ android:height="21dp"
+ android:viewportWidth="21"
+ android:viewportHeight="21"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M 1.61 0.81 L 0.19 2.22 L 2.88 4.91 C 1.58 6.6 0.8 8.71 0.8 11 C 0.8 13.76 1.92 16.26 3.73 18.07 L 5.15 16.65 C 3.7 15.21 2.8 13.21 2.8 11 C 2.8 9.25 3.37 7.65 4.31 6.34 L 5.74 7.77 C 5.15 8.7 4.8 9.81 4.8 11 C 4.8 12.66 5.48 14.15 6.56 15.24 L 7.98 13.82 C 7.25 13.1 6.8 12.11 6.8 11 C 6.8 10.37 6.95 9.77 7.21 9.24 L 8.82 10.85 C 8.82 10.9 8.8 10.95 8.8 11 C 8.8 11.55 9.03 12.05 9.39 12.41 C 9.75 12.77 10.25 13 10.8 13 C 10.85 13 10.9 12.99 10.96 12.98 L 18.58 20.6 L 19.99 19.19 L 1.61 0.81 Z M 16.5 12.87 C 16.69 12.28 16.8 11.65 16.8 11 C 16.8 7.69 14.11 5 10.8 5 C 10.15 5 9.52 5.1 8.93 5.3 L 10.64 7.01 C 10.69 7 10.75 7 10.8 7 C 13.01 7 14.8 8.79 14.8 11 C 14.8 11.05 14.8 11.11 14.79 11.16 L 16.5 12.87 Z M 10.8 3 C 15.22 3 18.8 6.58 18.8 11 C 18.8 12.22 18.53 13.37 18.03 14.4 L 19.52 15.89 C 20.33 14.45 20.8 12.78 20.8 11 C 20.8 5.48 16.32 1 10.8 1 C 9.02 1 7.36 1.46 5.91 2.28 L 7.39 3.76 C 8.43 3.27 9.58 3 10.8 3 Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_restart_bootloader.xml b/packages/SystemUI/res/drawable/ic_lock_restart_bootloader.xml
new file mode 100644
index 0000000..ea625b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_restart_bootloader.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,8 L3,8 C2,8,2,9,2,9 L2,21 C2,22,3,22,3,22 L21,22 C22,22,22,21,22,21 L22,9 C22,8,21,8,21,8 Z M17,9 C17.55,9,18,9.45,18,10 S17.55,11,17,11 S16,10.55,16,10 S16.45,9,17,9 Z M14,9 C14.55,9,15,9.45,15,10 S14.55,11,14,11 S13,10.55,13,10 S13.45,9,14,9 Z M20,20 L4,20 L4,12 L20,12 L20,20 Z M20,11 C19.45,11,19,10.55,19,10 S19.45,9,20,9 S21,9.45,21,10 S20.55,11,20,11 Z M21,2 L3,2 C2,2,2,3,2,3 L2,6 L22,6 L22,3 C22,2,21,2,21,2 Z M14,5 C13.45,5,13,4.55,13,4 S13.45,3,14,3 S15,3.45,15,4 S14.55,5,14,5 Z M17,5 C16.45,5,16,4.55,16,4 S16.45,3,17,3 S18,3.45,18,4 S17.55,5,17,5 Z M20,5 C19.45,5,19,4.55,19,4 S19.45,3,20,3 S21,3.45,21,4 S20.55,5,20,5 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,19.414 L8.293,16.707 L9.707,15.293 L11,16.586 L14.293,13.293 L15.707,14.707 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_restart_fastboot.xml b/packages/SystemUI/res/drawable/ic_lock_restart_fastboot.xml
new file mode 100644
index 0000000..560e625
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_restart_fastboot.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M 22 9 L 22 7 L 20 7 L 20 5 C 20 4.47 19.789 3.961 19.414 3.586 C 19.039 3.211 18.53 3 18 3 L 4 3 C 3.47 3 2.961 3.211 2.586 3.586 C 2.211 3.961 2 4.47 2 5 L 2 19 C 2 19.53 2.211 20.039 2.586 20.414 C 2.961 20.789 3.47 21 4 21 L 18 21 C 18.53 21 19.039 20.789 19.414 20.414 C 19.789 20.039 20 19.53 20 19 L 20 17 L 22 17 L 22 15 L 20 15 L 20 13 L 22 13 L 22 11 L 20 11 L 20 9 L 22 9 M 18 19 L 4 19 L 4 5 L 18 5 L 18 19 M 6 13 L 11 13 L 11 17 L 6 17 L 6 13 M 12 7 L 16 7 L 16 10 L 12 10 L 12 7 M 6 7 L 11 7 L 11 12 L 6 12 L 6 7 M 12 11 L 16 11 L 16 17 L 12 17 L 12 11 Z"
+ android:fillColor="#000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_restart_recovery.xml b/packages/SystemUI/res/drawable/ic_lock_restart_recovery.xml
new file mode 100644
index 0000000..6328026
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_restart_recovery.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.43,12.98 C19.47,12.66,19.5,12.34,19.5,12 S19.47,11.34,19.43,11.02 L21.54,9.37 C21.73,9.22,21.78,8.95,21.66,8.73 L19.66,5.27 C19.54,5.05,19.269,4.97,19.05,5.05 L16.56,6.05 C16.04,5.65,15.481,5.32,14.871,5.07 L14.491,2.42 C14.46,2.18,14.25,2,14,2 L10,2 C9.75,2,9.54,2.18,9.51,2.42 L9.13,5.07 C8.52,5.32,7.96,5.66,7.44,6.05 L4.95,5.05 C4.72,4.96,4.46,5.05,4.34,5.27 L2.34,8.73 C2.21,8.95,2.27,9.22,2.46,9.37 L4.57,11.02 C4.53,11.34,4.5,11.67,4.5,12 S4.53,12.66,4.57,12.98 L2.46,14.63 C2.27,14.78,2.22,15.05,2.34,15.27 L4.34,18.731 C4.46,18.951,4.73,19.031,4.95,18.951 L7.44,17.951 C7.96,18.35,8.52,18.68,9.13,18.93 L9.51,21.58 C9.54,21.82,9.75,22,10,22 L14,22 C14.25,22,14.46,21.82,14.49,21.58 L14.87,18.93 C15.48,18.68,16.04,18.34,16.559,17.951 L19.049,18.951 C19.279,19.041,19.539,18.951,19.659,18.731 L21.659,15.27 C21.779,15.05,21.729,14.781,21.539,14.63 L19.43,12.98 Z M9.71,15.71 L8.29,14.29 L14.29,8.29 L15.71,9.71 L9.71,15.71 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml
new file mode 100644
index 0000000..3f9abb9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36.0dip"
+ android:height="48.0dip"
+ android:viewportWidth="36.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 26,20.69 V 16 H 21.31 L 18,12.69 14.69,16 H 10 v 4.69 L 6.69,24 10,27.31 V 32 h 4.69 L 18,35.31 21.31,32 H 26 V 27.31 L 29.31,24 Z m -2,5.79 V 30 H 20.48 L 18,32.48 15.52,30 H 12 V 26.48 L 9.52,24 12,21.52 V 18 h 3.52 L 18,15.52 20.48,18 H 24 v 3.52 L 26.48,24 Z M 18,18 v 12 c 3.31,0 6,-2.69 6,-6 0,-3.31 -2.69,-6 -6,-6 z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml
new file mode 100644
index 0000000..4de4a0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36.0dip"
+ android:height="48.0dip"
+ android:viewportWidth="36.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="m 17,19 -3.2,9 h 1.9 l 0.7,-2 h 3.2 l 0.7,2 h 1.9 L 19,19 Z M 16.85,24.65 18,21 19.15,24.65 Z M 26,20.69 V 16 H 21.31 L 18,12.69 14.69,16 H 10 v 4.69 L 6.69,24 10,27.31 V 32 h 4.69 L 18,35.31 21.31,32 H 26 V 27.31 L 29.31,24 Z m -2,5.79 V 30 H 20.48 L 18,32.48 15.52,30 H 12 V 26.48 L 9.52,24 12,21.52 V 18 h 3.52 L 18,15.52 20.48,18 H 24 v 3.52 L 26.48,24 Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_caffeine.xml b/packages/SystemUI/res/drawable/ic_qs_caffeine.xml
new file mode 100644
index 0000000..6313ae9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_caffeine.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2015 The CyanogenMod Project
+ Copyright (c) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M2,21H20V19H2M20,8H18V5H20M20,3H4V13A4,4 0 0,0 8,17H14A4,4 0 0,0 18,13V10H20A2,2 0 0,0 22,8V5C22,3.89 21.1,3 20,3Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_compass.xml b/packages/SystemUI/res/drawable/ic_qs_compass.xml
new file mode 100644
index 0000000..27023cd7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_compass.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019-2024 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M25.9,3.3l7.8,19.1c0.4,1,0.4,2.2,0,3.3l-7.8,19.1c-0.7,1.7-3.1,1.7-3.8,0l-7.8-19.1c-0.4-1-0.4-2.2,0-3.3
+l7.8-19.1C22.8,1.6,25.2,1.6,25.9,3.3Z
+M24,20c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S26.2,20,24,20z
+M26,14v-4c0-1.1-0.9-2-2-2 s-2,0.9-2,2v4c0,1.1,0.9,2,2,2S26,15.1,26,14z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml
new file mode 100644
index 0000000..1747678
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt" android:height="48dp" android:width="48dp" android:viewportWidth="72" android:viewportHeight="72">
+ <path android:fillColor="#ffffffff" android:pathData="m36,66 l-18,0c-3.3,0 -6,-2.7 -6,-6L12,24 30,6 54,6c3.3,0 5.97,2.7 5.97,6l0.02,33 -20.99,0C37.35,45 36,46.34 36,47.99zM42,28 L48,35 54,28zM54,19 L48,12 42,19zM51.32,66 L47.36,66 47.36,53.39 43.47,54.53 43.47,51.53 50.96,48.94 51.32,48.94z" android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml
new file mode 100644
index 0000000..438fc94
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt" android:height="48dp" android:width="48dp" android:viewportWidth="72" android:viewportHeight="72">
+ <path android:fillColor="#ffffffff" android:pathData="m36,66 l-18,0c-3.3,0 -6,-2.7 -6,-6L12,24 30,6 54,6c3.3,0 5.97,2.7 5.97,6l0.02,33 -20.99,0C37.35,45 36,46.34 36,47.99zM42,28 L48,35 54,28zM48,12 L42,19 54,19zM54.48,66 L42.57,66 42.57,63.42 48.06,57.66c0.73,-0.83 1.25,-1.54 1.56,-2.13 0.31,-0.59 0.47,-1.1 0.47,-1.54 0,-0.73 -0.16,-1.29 -0.48,-1.68 -0.32,-0.38 -0.79,-0.57 -1.39,-0.57 -0.3,0 -0.58,0.07 -0.83,0.21 -0.25,0.14 -0.46,0.33 -0.64,0.57 -0.18,0.24 -0.31,0.53 -0.41,0.85 -0.1,0.32 -0.15,0.68 -0.15,1.06l-3.96,0c0,-0.78 0.15,-1.52 0.44,-2.21 0.29,-0.7 0.71,-1.3 1.24,-1.83 0.54,-0.52 1.18,-0.94 1.92,-1.24 0.75,-0.3 1.57,-0.45 2.48,-0.45 0.95,0 1.78,0.11 2.49,0.33 0.71,0.22 1.31,0.54 1.8,0.97 0.48,0.42 0.85,0.94 1.1,1.55 0.25,0.61 0.37,1.31 0.37,2.09 0,0.59 -0.09,1.16 -0.28,1.71 -0.19,0.54 -0.46,1.08 -0.81,1.62 -0.35,0.54 -0.78,1.09 -1.29,1.65 -0.51,0.56 -1.08,1.16 -1.71,1.79l-2.2,2.54 6.71,0z" android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_powershare.xml b/packages/SystemUI/res/drawable/ic_qs_powershare.xml
new file mode 100644
index 0000000..a9798bf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_powershare.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M19.77,7.23l0.01,-0.01 -3.72,-3.72L15,4.56l2.11,2.11c-0.94,0.36 -1.61,1.26 -1.61,2.33 0,1.38 1.12,2.5 2.5,2.5 0.36,0 0.69,-0.08 1,-0.21v7.21c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V14c0,-1.1 -0.9,-2 -2,-2h-1V5c0,-1.1 -0.9,-2 -2,-2H6c-1.1,0 -2,0.9 -2,2v16h10v-7.5h1.5v5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V9c0,-0.69 -0.28,-1.32 -0.73,-1.77zM18,10c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM8,18v-4.5H6L10,6v5h2l-4,7z" />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_24dp.xml
new file mode 100644
index 0000000..28e163d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group_24dp.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2020-2022 The Android Open Source Project
+
+ SPDX-License-Identifier: Apache-2.0
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorBackgroundFloating">
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="@android:color/white"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="@android:color/white"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="@android:color/white"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_4g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_statusbar_4g_mobiledata.xml
new file mode 100644
index 0000000..060c52d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_4g_mobiledata.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M284,680L284,550L120,550L120,280L180,280L180,490L284,490L284,280L344,280L344,490L429,490L429,550L344,550L344,680L284,680ZM840,448L840,620Q840,644.75 823,662.38Q806,680 781,680L550,680Q525.25,680 507.63,662.38Q490,644.75 490,620L490,340Q490,315.25 507.63,297.63Q525.25,280 550,280L781,280Q804.1,280 820.55,298Q837,316 840,340L550,340Q550,340 550,340Q550,340 550,340L550,620Q550,620 550,620Q550,620 550,620L781,620Q781,620 781,620Q781,620 781,620L781,508L678,508L678,448L840,448Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml b/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml
new file mode 100644
index 0000000..7ade8b2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M21,16v-2l-8,-5V3.5C13,2.67 12.33,2 11.5,2S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1l3.5,1v-1.5L13,19v-5.5L21,16z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml b/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml
new file mode 100644
index 0000000..4d0a5f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M44.0,11.44l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-28.24,-4.66l-2.57,-3.06 -9.19,7.71 2.57,3.06 9.19,-7.71zm9.24,9.22l-3.0,0.0l0.0,12.0l9.49,5.71 1.51,-2.47 -8.0,-4.74l0.0,-10.5zm-1.01,-8.0c-9.95,0.0 -17.99,8.06 -17.99,18.0s8.04,18.0 17.99,18.0 18.01,-8.06 18.01,-18.0 -8.06,-18.0 -18.01,-18.0zm0.01,32.0c-7.73,0.0 -14.0,-6.27 -14.0,-14.0s6.27,-14.0 14.0,-14.0 14.0,6.27 14.0,14.0 -6.26,14.0 -14.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml
new file mode 100644
index 0000000..9a2cb49
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7.4,10.94H2.45V5.99l2,0.01v1.53l4.61,-4.61c0.64,-0.64 1.67,-0.66 2.29,-0.04l8.18,8.18h-2.83l-6.48,-6.48L5.86,8.95H7.4V10.94zM16.6,13.06l0.01,2h1.53l-4.36,4.36l-6.48,-6.48H4.46l8.19,8.19c0.62,0.62 1.65,0.6 2.29,-0.04l4.61,-4.61l0.01,0.01v1.53h1.99v-4.95H16.6z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_camera.xml b/packages/SystemUI/res/drawable/ic_statusbar_camera.xml
new file mode 100644
index 0000000..32d6b53
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_camera.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_cast.xml b/packages/SystemUI/res/drawable/ic_statusbar_cast.xml
new file mode 100644
index 0000000..5413c77
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_cast.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_clock.xml b/packages/SystemUI/res/drawable/ic_statusbar_clock.xml
new file mode 100644
index 0000000..4b91508
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_clock.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.99,2.0C6.47,2.0 2.0,6.48 2.0,12.0s4.47,10.0 9.99,10.0C17.52,22.0 22.0,17.52 22.0,12.0S17.52,2.0 11.99,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.58 -8.0,-8.0s3.58,-8.0 8.0,-8.0 8.0,3.58 8.0,8.0 -3.58,8.0 -8.0,8.0z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.5,7.0L11.0,7.0l0.0,6.0l5.25,3.1 0.75,-1.23 -4.5,-2.67z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml b/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml
new file mode 100644
index 0000000..f2bc5ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11,8v3L8,11v2h3v3h2v-3h3v-2h-3L13,8zM13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92L11,2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml b/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml
new file mode 100644
index 0000000..cace8d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,16.41 16.41,20 12,20z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7,11h10v2h-10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml b/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml
new file mode 100644
index 0000000..46e753f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml
@@ -0,0 +1,34 @@
+ <!--
+ ~ 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.
+ -->
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17,6l-1.41,1.41L20.17,12l-4.58,4.59L17,18l6,-6zM8.41,7.41L7,6l-6,6 6,6 1.41,-1.41L3.83,12z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8,12m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,12m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M16,12m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+ </vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_headset.xml b/packages/SystemUI/res/drawable/ic_statusbar_headset.xml
new file mode 100644
index 0000000..ad9a55f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_headset.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19,15v3c0,0.55 -0.45,1 -1,1h-1v-4h2M7,15v4H6c-0.55,0 -1,-0.45 -1,-1v-3h2m5,-13c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml b/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml
new file mode 100644
index 0000000..37f5ea9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,7c-3.31,0-6,2.69-6,6c0,1.66,0.68,3.15,1.76,4.24l1.42-1.42C8.45,15.1,8,14.11,8,13c0-2.21,1.79-4,4-4s4,1.79,4,4 c0,1.11-0.45,2.1-1.18,2.82l1.42,1.42C17.32,16.15,18,14.66,18,13C18,9.69,15.31,7,12,7z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,3C6.48,3,2,7.48,2,13c0,2.76,1.12,5.26,2.93,7.07l1.41-1.41C4.9,17.21,4,15.21,4,13c0-4.42,3.58-8,8-8s8,3.58,8,8 c0,2.21-0.9,4.2-2.35,5.65l1.41,1.41C20.88,18.26,22,15.76,22,13C22,7.48,17.52,3,12,3z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,11c-1.1,0-2,0.9-2,2c0,0.55,0.23,1.05,0.59,1.41C10.95,14.77,11.45,15,12,15s1.05-0.23,1.41-0.59 C13.77,14.05,14,13.55,14,13C14,11.9,13.1,11,12,11z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml b/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml
new file mode 100644
index 0000000..1f24717
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="m3.8,22l17.19,0c0.55,0 1.01,-0.45 1.01,-1.01l0,-17.19c0,-0.71 -0.87,-1.08 -1.38,-0.57l-17.38,17.39c-0.51,0.51 -0.15,1.38 0.56,1.38z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_mute.xml b/packages/SystemUI/res/drawable/ic_statusbar_mute.xml
new file mode 100644
index 0000000..c63aeea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_mute.xml
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_network_monitor.xml b/packages/SystemUI/res/drawable/ic_statusbar_network_monitor.xml
new file mode 100644
index 0000000..3bba2d8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_network_monitor.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M63,423L0,360Q93,265 218,212.5Q343,160 480,160Q504,160 534,161.5Q564,163 596,167L554,253Q536,251 518,250.5Q500,250 480,250Q361,250 254.5,296Q148,342 63,423ZM186,541L123,478Q192,411 288.5,376Q385,341 508,349L469,430Q392,428 314.5,459Q237,490 186,541ZM444,796Q417,785 404.5,755Q392,725 406,696L643,206Q646,199 653.5,196Q661,193 669,195Q677,197 682,204Q687,211 685,219L547,754Q539,786 506,796.5Q473,807 444,796ZM774,541Q758,524 729.5,504Q701,484 681,475L702,389Q737,401 775,427.5Q813,454 837,478L774,541ZM897,423Q858,385 812,354Q766,323 724,304L746,214Q811,241 862,277.5Q913,314 960,360L897,423Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml b/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml
new file mode 100644
index 0000000..b8d2be6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="16.0"
+ android:viewportHeight="16.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M4.5,13.02V3h3.93c0.97,0 1.75,0.27 2.34,0.81c0.6,0.53 0.9,1.28 0.9,2.25c0,0.63 -0.19,1.2 -0.56,1.74c-0.37,0.53 -0.95,0.91 -1.74,1.12l2.45,4.02v0.08h-1.69L7.75,9.09H6.03v3.93H4.5zM8.46,4.44H6.03v3.23h2.44c0.49,0 0.9,-0.14 1.2,-0.41c0.31,-0.28 0.46,-0.69 0.46,-1.22s-0.15,-0.93 -0.45,-1.2C9.38,4.58 8.98,4.44 8.46,4.44z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml b/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml
new file mode 100644
index 0000000..04c38ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml b/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml
new file mode 100644
index 0000000..e56e951
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M24,7.39L12,22L0,7.39C2.97,4.08,7.25,2,12,2S21.03,4.08,24,7.39z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_statusbar_work.xml b/packages/SystemUI/res/drawable/ic_statusbar_work.xml
new file mode 100644
index 0000000..ea8918b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_statusbar_work.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+~ Copyright (C) 2019 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
index ef3c61b..af976e76 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -17,8 +17,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/qs_tile_ripple_color">
<item android:id="@android:id/mask"
- android:drawable="@drawable/qs_tile_background_shape"
- />
+ android:drawable="@drawable/qs_tile_background_shape" />
<item android:id="@id/background">
<layer-list>
<item
diff --git a/packages/SystemUI/res/drawable/qs_tile_background_no_mask.xml b/packages/SystemUI/res/drawable/qs_tile_background_no_mask.xml
new file mode 100644
index 0000000..487082f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background_no_mask.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@id/background"
+ android:drawable="@drawable/qs_tile_background_shape_round"/>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_tile_background_shape_round.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape_round.xml
new file mode 100644
index 0000000..66c4727
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background_shape_round.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="100dp" />
+ <solid android:color="#FFFFFF" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml
new file mode 100644
index 0000000..3559e09
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,15.3V15.94h3V15.3Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml
new file mode 100644
index 0000000..50c9438
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,13.6V15.94h3V13.6Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml
new file mode 100644
index 0000000..0c82aa5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,11.9V15.94h3V11.9Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml
new file mode 100644
index 0000000..0f39487
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,10.2V15.94h3V10.2Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml
new file mode 100644
index 0000000..6a613d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,8.5V15.94h3V8.5Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml
new file mode 100644
index 0000000..df0f93d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,6.8V15.94h3V6.8Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml
new file mode 100644
index 0000000..4924dea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,5.1V15.94h3V5.1Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml
new file mode 100644
index 0000000..4c4ae03
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,3.4V15.94h3V3.4Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml
new file mode 100644
index 0000000..6ba978a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#4DFFFFFF"/>
+ <path
+ android:pathData="M15.77,1.7V15.94h3V1.7Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml
new file mode 100644
index 0000000..26ff151
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="17dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="18.0">
+ <group
+ android:scaleX="0.75"
+ android:scaleY="0.75">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </group>
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M15.77,1.064V15.94h3V1.064Z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/statusbar_chip_bg.xml b/packages/SystemUI/res/drawable/statusbar_chip_bg.xml
index d7de16d..af0d595 100644
--- a/packages/SystemUI/res/drawable/statusbar_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/statusbar_chip_bg.xml
@@ -18,6 +18,6 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <solid android:color="?androidprv:attr/colorAccent" />
<corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
-</shape>
\ No newline at end of file
+</shape>
diff --git a/packages/SystemUI/res/drawable/volume_background.xml b/packages/SystemUI/res/drawable/volume_background.xml
new file mode 100644
index 0000000..671d45c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The LineageOS Project
+
+ SPDX-License-Identifier: Apache-2.0
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <size android:width="@dimen/volume_dialog_panel_width" />
+ <solid android:color="?androidprv:attr/colorSurface" />
+</shape>
diff --git a/packages/SystemUI/res/layout-land/global_actions_power_dialog.xml b/packages/SystemUI/res/layout-land/global_actions_power_dialog.xml
new file mode 100644
index 0000000..ba99564
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_power_dialog.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:divider="@drawable/controls_list_divider"
+ android:showDividers="middle"
+/>
+
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 5ce2601..da9891f 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -25,14 +25,14 @@
android:background="@android:color/transparent"
android:theme="@style/volume_dialog_theme">
- <!-- right-aligned to be physically near volume button -->
- <LinearLayout
+ <com.android.systemui.animation.view.LaunchableLinearLayout
android:id="@+id/volume_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:layout_gravity="right"
- android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+ android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding_horizontal"
+ android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_horizontal"
android:orientation="vertical"
android:clipToPadding="false"
android:clipChildren="false">
@@ -93,13 +93,12 @@
android:id="@+id/settings_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/volume_background_bottom"
+ android:background="@drawable/volume_background"
android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/settings"
- android:src="@drawable/horizontal_ellipsis"
+ android:src="@drawable/ic_speaker_group_24dp"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:layout_gravity="center"
@@ -108,6 +107,38 @@
android:tint="?androidprv:attr/colorAccent"
android:soundEffectsEnabled="false" />
</FrameLayout>
+ <FrameLayout
+ android:id="@+id/expandable_indicator_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_background_bottom"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+ <com.android.systemui.statusbar.phone.ExpandableIndicator
+ android:id="@+id/expandable_indicator"
+ android:layout_width="@dimen/volume_dialog_tap_target_size"
+ android:layout_height="@dimen/volume_dialog_tap_target_size"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:tint="?androidprv:attr/colorAccent"
+ android:soundEffectsEnabled="false"
+ android:padding="10dp"
+ android:rotation="90" />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/rounded_border_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_background_bottom"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+ <View
+ android:layout_width="0dp"
+ android:layout_height="32dp"
+ android:background="@drawable/ripple_drawable_20dp"/>
+ </FrameLayout>
</LinearLayout>
</LinearLayout>
@@ -132,7 +163,7 @@
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
</FrameLayout>
- </LinearLayout>
+ </com.android.systemui.animation.view.LaunchableLinearLayout>
<ViewStub
android:id="@+id/odi_captions_tooltip_stub"
@@ -141,6 +172,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom | right"
- android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
+ android:layout_marginLeft="@dimen/volume_tool_tip_horizontal_margin"
+ android:layout_marginRight="@dimen/volume_tool_tip_horizontal_margin"/>
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index ac781ec..1063631 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -233,6 +233,7 @@
android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
android:layout_height="64dp"
+ android:layout_marginBottom="9dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -276,4 +277,4 @@
app:layout_constraintVertical_bias="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/contaminant_dialog.xml b/packages/SystemUI/res/layout/contaminant_dialog.xml
index 5f8c305..230df6c 100644
--- a/packages/SystemUI/res/layout/contaminant_dialog.xml
+++ b/packages/SystemUI/res/layout/contaminant_dialog.xml
@@ -32,7 +32,7 @@
android:paddingTop="18dp"
android:paddingBottom="18dp"
android:textAlignment="center"
- android:fontFamily="google-sans-medium"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="?android:attr/textColorPrimary"/>
diff --git a/packages/SystemUI/res/layout/global_actions_power_dialog_flow.xml b/packages/SystemUI/res/layout/global_actions_power_dialog_flow.xml
new file mode 100644
index 0000000..1b4c8f3
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_power_dialog_flow.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/power_actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:background="@drawable/global_actions_lite_background"
+ android:padding="@dimen/global_actions_lite_padding">
+
+ <androidx.constraintlayout.helper.widget.Flow
+ android:id="@+id/power_flow"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:flow_wrapMode="chain"
+ app:flow_maxElementsWrap="2"
+ app:flow_horizontalGap="@dimen/global_actions_lite_padding"
+ app:flow_verticalGap="@dimen/global_actions_lite_padding"
+ app:flow_horizontalStyle="packed"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_power_item.xml b/packages/SystemUI/res/layout/global_actions_power_item.xml
index 3bf5894..84153a8 100644
--- a/packages/SystemUI/res/layout/global_actions_power_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_power_item.xml
@@ -37,7 +37,7 @@
android:ellipsize="marquee"
android:layout_marginBottom="16dp"
android:maxLines="1"
- android:textSize="16sp"
+ android:textSize="12sp"
android:gravity="center"
android:textColor="@color/control_primary_text"
android:textAppearance="?android:attr/textAppearanceSmall" />
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 22d156d..29debeb 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -128,7 +128,8 @@
<LinearLayout
android:id="@+id/mobile_network_layout"
- style="@style/InternetDialog.Network">
+ style="@style/InternetDialog.Network"
+ android:layout_height="72dp">
<FrameLayout
android:layout_width="24dp"
@@ -196,10 +197,84 @@
android:layout="@layout/qs_dialog_secondary_mobile_network"
style="@style/InternetDialog.Network"/>
+ <Space
+ android:id="@+id/mobile_connected_space"
+ android:layout_height="8dp"
+ android:layout_width="match_parent"
+ android:visibility="gone"/>
+
+ <LinearLayout
+ android:id="@+id/hotspot_layout"
+ style="@style/InternetDialog.Network"
+ android:layout_height="72dp">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/hotspot_icon"
+ android:autoMirrored="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_internet_hotspot_disabled"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/hotspot_title"
+ android:maxLines="1"
+ style="@style/InternetDialog.NetworkTitle"/>
+ <TextView
+ android:id="@+id/hotspot_summary"
+ style="@style/InternetDialog.NetworkSummary"/>
+ </LinearLayout>
+
+ <View
+ android:id="@+id/hotspot_toggle_divider"
+ android:layout_width="1dp"
+ android:layout_height="28dp"
+ android:layout_marginStart="7dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/textColorSecondary"/>
+
+ <FrameLayout
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="48dp"
+ android:layout_gravity="end|center_vertical">
+ <Switch
+ android:id="@+id/hotspot_toggle"
+ android:contentDescription="@string/quick_settings_hotspot_label"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="match_parent"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/wifi_connected_space"
+ android:layout_height="8dp"
+ android:layout_width="match_parent"
+ android:visibility="gone"/>
+
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
style="@style/InternetDialog.Network"
- android:layout_height="@dimen/internet_dialog_wifi_network_height"
+ android:layout_height="@dimen/internet_dialog_wifi_toggle_height"
android:gravity="center"
android:clickable="false"
android:focusable="false">
@@ -251,6 +326,7 @@
android:id="@+id/wifi_connected_layout"
style="@style/InternetDialog.Network"
android:layout_height="@dimen/internet_dialog_wifi_network_height"
+ android:layout_marginTop="8dp"
android:paddingStart="20dp"
android:paddingEnd="24dp"
android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -315,7 +391,8 @@
<LinearLayout
android:id="@+id/see_all_layout"
style="@style/InternetDialog.Network"
- android:layout_height="64dp"
+ android:layout_height="56dp"
+ android:paddingTop="8dp"
android:paddingStart="20dp">
<FrameLayout
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 4f100f6..5598ca8 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="14sp"
- android:fontFamily="sans-serif-medium"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:importantForAccessibility="yes"
android:paddingTop="20dp"
android:paddingBottom="10dp"/>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1eb05bf..894ea46 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -38,6 +38,7 @@
android:background="@android:color/transparent"
android:focusable="true"
android:accessibilityTraversalBefore="@android:id/edit"
+ android:paddingTop="@dimen/custom_qs_panel_padding_top"
android:clipToPadding="false"
android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout/qs_tile_label_round.xml b/packages/SystemUI/res/layout/qs_tile_label_round.xml
new file mode 100644
index 0000000..376cef1
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_label_round.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:layout_gravity="center_horizontal">
+
+ <com.android.systemui.util.DelayableMarqueeTextView
+ android:id="@+id/tile_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textDirection="locale"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
+
+ <com.android.systemui.util.DelayableMarqueeTextView
+ android:id="@+id/app_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textDirection="locale"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:visibility="invisible"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+</com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon_round.xml b/packages/SystemUI/res/layout/qs_tile_side_icon_round.xml
new file mode 100644
index 0000000..d49df26
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon_round.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ android:layout_marginStart="@dimen/qs_label_container_margin"
+ android:layout_gravity="center_vertical | end"
+>
+ <ImageView
+ android:id="@+id/customDrawable"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/qs_side_view_size"
+ android:layout_marginEnd="@dimen/qs_drawable_end_margin"
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/chevron"
+ android:layout_width="@dimen/qs_icon_size"
+ android:layout_height="@dimen/qs_icon_size"
+ android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 91550b3..f0c39bb 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -20,13 +20,15 @@
android:layout_height="@dimen/brightness_mirror_height"
android:layout_gravity="center"
android:contentDescription="@string/accessibility_brightness"
+ android:orientation="horizontal"
android:importantForAccessibility="no" >
<com.android.systemui.settings.brightness.ToggleSeekBar
android:id="@+id/slider"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_weight="1"
android:minHeight="48dp"
android:thumb="@null"
android:background="@null"
@@ -36,4 +38,17 @@
android:splitTrack="false"
android:clickable="true"
/>
+
+ <ImageButton
+ android:id="@+id/brightness_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="20.0dip"
+ android:background="@drawable/brightness_bg"
+ android:src="@drawable/ic_qs_brightness_auto_off"
+ android:tint="?android:attr/textColorPrimaryInverse"
+ android:contentDescription="@null"
+ android:visibility="gone"
+ />
</com.android.systemui.settings.brightness.BrightnessSliderView>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 1749ed4..d7313de 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -41,8 +41,8 @@
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
+ android:paddingTop="@dimen/custom_qs_panel_padding_top"
android:paddingBottom="@dimen/qqs_layout_padding_bottom"
android:importantForAccessibility="no">
</com.android.systemui.qs.QuickQSPanel>
-
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 3b728a9..a654b79 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -57,6 +57,8 @@
<include layout="@layout/overlay_action_chip"
android:id="@+id/screenshot_edit_chip"/>
<include layout="@layout/overlay_action_chip"
+ android:id="@+id/screenshot_delete_chip"/>
+ <include layout="@layout/overlay_action_chip"
android:id="@+id/screenshot_scroll_chip"
android:visibility="gone" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 72424a13..054f1c4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -20,6 +20,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingStart="@dimen/custom_notification_row_padding"
+ android:paddingEnd="@dimen/custom_notification_row_padding"
android:visibility="gone">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/tuner_activity.xml b/packages/SystemUI/res/layout/tuner_activity.xml
index 83cbf14..1d76f67 100644
--- a/packages/SystemUI/res/layout/tuner_activity.xml
+++ b/packages/SystemUI/res/layout/tuner_activity.xml
@@ -20,16 +20,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
- <Toolbar
- android:id="@+id/action_bar"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/actionBarTheme"
- android:navigationContentDescription="@*android:string/action_bar_up_description" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:background="?android:attr/windowBackground" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 257d238..0fcbfa1 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -28,4 +28,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <com.android.systemui.biometrics.UdfpsSurfaceView
+ android:id="@+id/hbm_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"/>
+
</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 39a1f1f..e843f76 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -25,14 +25,14 @@
android:clipToPadding="false"
android:theme="@style/volume_dialog_theme">
- <!-- right-aligned to be physically near volume button -->
- <LinearLayout
+ <com.android.systemui.animation.view.LaunchableLinearLayout
android:id="@+id/volume_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:layout_gravity="right"
- android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+ android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding_horizontal"
+ android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_horizontal"
android:orientation="vertical"
android:clipToPadding="false"
android:clipChildren="false">
@@ -92,13 +92,12 @@
android:id="@+id/settings_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/volume_background_bottom"
+ android:background="@drawable/volume_background"
android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/settings"
- android:src="@drawable/horizontal_ellipsis"
+ android:src="@drawable/ic_speaker_group_24dp"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:layout_gravity="center"
@@ -107,6 +106,38 @@
android:tint="?androidprv:attr/colorAccent"
android:soundEffectsEnabled="false" />
</FrameLayout>
+ <FrameLayout
+ android:id="@+id/expandable_indicator_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_background_bottom"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+ <com.android.systemui.statusbar.phone.ExpandableIndicator
+ android:id="@+id/expandable_indicator"
+ android:layout_width="@dimen/volume_dialog_tap_target_size"
+ android:layout_height="@dimen/volume_dialog_tap_target_size"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:tint="?androidprv:attr/colorAccent"
+ android:soundEffectsEnabled="false"
+ android:padding="10dp"
+ android:rotation="90" />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/rounded_border_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_background_bottom"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+ <View
+ android:layout_width="0dp"
+ android:layout_height="32dp"
+ android:background="@drawable/ripple_drawable_20dp"/>
+ </FrameLayout>
</LinearLayout>
</LinearLayout>
@@ -131,7 +162,7 @@
android:layout_gravity="center"
android:soundEffectsEnabled="false"/>
</FrameLayout>
- </LinearLayout>
+ </com.android.systemui.animation.view.LaunchableLinearLayout>
<ViewStub
android:id="@+id/odi_captions_tooltip_stub"
@@ -140,6 +171,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom | right"
- android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
+ android:layout_marginLeft="@dimen/volume_tool_tip_horizontal_margin"
+ android:layout_marginRight="@dimen/volume_tool_tip_horizontal_margin"/>
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/packages/SystemUI/res/values-af/cm_strings.xml b/packages/SystemUI/res/values-af/cm_strings.xml
new file mode 100644
index 0000000..742f9eb
--- /dev/null
+++ b/packages/SystemUI/res/values-af/cm_strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Stelsel</string>
+ <string name="quick_settings_caffeine_label">Kafeïen</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafeïen af.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafeïen aan.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ar/cm_strings.xml b/packages/SystemUI/res/values-ar/cm_strings.xml
new file mode 100644
index 0000000..b761a0a
--- /dev/null
+++ b/packages/SystemUI/res/values-ar/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">إعادة تشغيل\u2026</string>
+ <string name="global_action_restart_system">النظام</string>
+ <string name="global_action_restart_recovery">الاسترداد</string>
+ <string name="global_action_restart_bootloader">محمل الاقلاع</string>
+ <string name="global_action_restart_download">التنزيل</string>
+ <string name="global_action_restart_fastboot">إعادة التشغيل إلى وضع الإقلاع السريع</string>
+ <string name="global_action_restart_progress">جارٍ إعادة التشغيل\u2026</string>
+ <string name="global_action_restart_recovery_progress">إعادة التشغيل في وضع الاسترداد\u2026</string>
+ <string name="global_action_restart_bootloader_progress">إعادة التشغيل في وضع مُحمّل الإقلاع\u2026</string>
+ <string name="global_action_restart_download_progress">إعادة التشغيل في وضع التنزيل\u2026</string>
+ <string name="global_action_restart_fastboot_progress">إعادة التشغيل إلى وضع الإقلاع السريع\u2026</string>
+ <string name="quick_settings_powershare_label">مشاركة الشحن لاسلكياً</string>
+ <string name="quick_settings_powershare_off_powersave_label">إيقاف\n مشاركة الشحن لاسلكياً عند تفعيل وضع توفير البطارية</string>
+ <string name="quick_settings_powershare_off_low_battery_label">إيقاف\n مشاركة الشحن لاسلكياً عندما يكون مستوى البطارية منخفضة جداً</string>
+ <string name="quick_settings_powershare_enabled_label">مشاركة الشحن لاسلكياً مفعلة</string>
+ <string name="quick_settings_caffeine_label">وضع الكافيين</string>
+ <string name="accessibility_quick_settings_caffeine_off">ايقاف وضع الكافيين.</string>
+ <string name="accessibility_quick_settings_caffeine_on">تشغيل وضع الكافيين.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-as/cm_strings.xml b/packages/SystemUI/res/values-as/cm_strings.xml
new file mode 100644
index 0000000..2373439
--- /dev/null
+++ b/packages/SystemUI/res/values-as/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">কেফেইন</string>
+ <string name="accessibility_quick_settings_caffeine_off">কেফেইন অফ.</string>
+ <string name="accessibility_quick_settings_caffeine_on">কেফেইন অন.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ast-rES/cm_strings.xml b/packages/SystemUI/res/values-ast-rES/cm_strings.xml
new file mode 100644
index 0000000..1bdcc66
--- /dev/null
+++ b/packages/SystemUI/res/values-ast-rES/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reaniciar\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Cargador d\'arrinque</string>
+ <string name="global_action_restart_download">Descarga</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reaniciando\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reaniciando al mou de recuperación\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reaniciando al mou del xestor d\'arrinque\u2026</string>
+ <string name="global_action_restart_download_progress">Reaniciando al mou de descarga\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reaniciando al mou fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare ensin filos</string>
+ <string name="quick_settings_powershare_off_powersave_label">PowerShare ensin filos desactiváu\nAforrador d\'enerxía</string>
+ <string name="quick_settings_powershare_off_low_battery_label">PowerShare ensin filos desactiváu\nQueda perpoca batería</string>
+ <string name="quick_settings_powershare_enabled_label">El PowerShare ensin filos ta activáu</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">«Cafeína» desactivada.</string>
+ <string name="accessibility_quick_settings_caffeine_on">«Cafeína» activada.</string>
+ <string name="status_bar_icons_title">Iconos de la barra d\'estáu</string>
+</resources>
diff --git a/packages/SystemUI/res/values-az/cm_strings.xml b/packages/SystemUI/res/values-az/cm_strings.xml
new file mode 100644
index 0000000..ab6506d
--- /dev/null
+++ b/packages/SystemUI/res/values-az/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Yenidən başlat\u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Geri qaytarma</string>
+ <string name="global_action_restart_bootloader">Önyükləyici</string>
+ <string name="global_action_restart_download">Endirmə</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Yenidən başladılır\u2026</string>
+ <string name="global_action_restart_recovery_progress">Geri qaytarma rejimində yenidən başladılır\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Önyükləyici rejimində yenidən başladılır\u2026</string>
+ <string name="global_action_restart_download_progress">Endirmə rejimində yenidən başladılır\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Fastbootd rejimində yenidən başladılır\u2026</string>
+ <string name="quick_settings_powershare_label">Naqilsiz Enerji Paylaşma</string>
+ <string name="quick_settings_powershare_off_powersave_label">Naqilsiz Enerji Paylaşma bağlıdır\nBatareyaya qənaət</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Naqilsiz Enerji Paylaşma bağlıdır\nBatareya səviyyəsi aşağıdır</string>
+ <string name="quick_settings_powershare_enabled_label">Naqilsiz Enerji Paylaşma fəaldır</string>
+ <string name="quick_settings_caffeine_label">Kofein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofein bağlıdır.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofein açıqdır.</string>
+ <string name="status_bar_icons_title">Status çubuğu ikonları</string>
+</resources>
diff --git a/packages/SystemUI/res/values-be/cm_strings.xml b/packages/SystemUI/res/values-be/cm_strings.xml
new file mode 100644
index 0000000..7fcfac5
--- /dev/null
+++ b/packages/SystemUI/res/values-be/cm_strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Сістэма</string>
+ <string name="global_action_restart_recovery">Аднаўленне</string>
+ <string name="global_action_restart_bootloader">Загрузчык</string>
+ <string name="global_action_restart_download">Спампоўка</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Перазапуск\u2026</string>
+ <string name="quick_settings_powershare_label">Бесправадны PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Бесправадны PowerShare выключаны\nЭканомія акумулятара</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Бесправадны PowerShare выключаны\nНізкі зарад акумулятара</string>
+ <string name="quick_settings_powershare_enabled_label">Бесправадны PowerShare уключаны</string>
+ <string name="quick_settings_caffeine_label">Кафеін</string>
+ <string name="accessibility_quick_settings_caffeine_off">Кафеін выключаны.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Кафеін уключаны.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bg/cm_strings.xml b/packages/SystemUI/res/values-bg/cm_strings.xml
new file mode 100644
index 0000000..e8ade1c
--- /dev/null
+++ b/packages/SystemUI/res/values-bg/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Рестартиране\u2026</string>
+ <string name="global_action_restart_system">Система</string>
+ <string name="global_action_restart_recovery">Режим на възстановяване</string>
+ <string name="global_action_restart_bootloader">Буутлоудър</string>
+ <string name="global_action_restart_download">Изтегли</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Рестартиране\u2026</string>
+ <string name="global_action_restart_recovery_progress">Рестартиране до режим за възстановяване\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Рестартиране за режим буутлоудър\u2026</string>
+ <string name="global_action_restart_download_progress">Рестартиране за режим изтегляне\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Рестартиране в режим fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nИкономия на батерия</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nБатерията е изтощена</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare е включен</string>
+ <string name="quick_settings_caffeine_label">Кофеин</string>
+ <string name="accessibility_quick_settings_caffeine_off">Кофеин изключен.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Кофеин включен.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bn/cm_strings.xml b/packages/SystemUI/res/values-bn/cm_strings.xml
new file mode 100644
index 0000000..c15db97
--- /dev/null
+++ b/packages/SystemUI/res/values-bn/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">রিস্টার্ট\u2026</string>
+ <string name="global_action_restart_system">সিস্টেম</string>
+ <string name="global_action_restart_recovery">রিকভারি</string>
+ <string name="global_action_restart_bootloader">বুটলোডার</string>
+ <string name="global_action_restart_download">ডাউনলোড</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">রিস্টার্টিং\u2026</string>
+ <string name="global_action_restart_recovery_progress">রিকভারি মোডে রিস্টার্ট করা হচ্ছে\u2026</string>
+ <string name="global_action_restart_bootloader_progress">বুটলোডার মোডে রিস্টার্ট করা হচ্ছে\u2026</string>
+ <string name="global_action_restart_download_progress">ডাউনলোড মোডে রিস্টার্ট করা হচ্ছে\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Fastbootd মোডে রিস্টার্ট করা হচ্ছে\u2026</string>
+ <string name="quick_settings_powershare_label">ওয়্যারলেস পাওয়ার শেয়ার</string>
+ <string name="quick_settings_powershare_off_powersave_label">ওয়্যারলেস পাওয়ারশেয়ার অক্ষম\nব্যাটারি সেভার</string>
+ <string name="quick_settings_powershare_off_low_battery_label">ওয়্যারলেস পাওয়ারশেয়ার অক্ষম\nব্যাটারি খুব কম</string>
+ <string name="quick_settings_powershare_enabled_label">ওয়্যারলেস পাওয়ার শেয়ার সক্ষম করা আছে</string>
+ <string name="quick_settings_caffeine_label">ক্যাফেইন</string>
+ <string name="accessibility_quick_settings_caffeine_off">ক্যাফেইন বন্ধ।</string>
+ <string name="accessibility_quick_settings_caffeine_on">ক্যাফেইন চালু।</string>
+</resources>
diff --git a/packages/SystemUI/res/values-br-rFR/cm_strings.xml b/packages/SystemUI/res/values-br-rFR/cm_strings.xml
new file mode 100644
index 0000000..b632fa4
--- /dev/null
+++ b/packages/SystemUI/res/values-br-rFR/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Reizhiad</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Pellgargañ</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Oc\'h Adlañsañ\u2026</string>
+ <string name="global_action_restart_recovery_progress">Oc\'h adlañsañ e mod recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Oc\'h adlañsañ e mod bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Oc\'h adlañsañ e mod download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Oc\'h adlañsañ e mod fastbootd\u2026</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafein gweredekaet.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bs/cm_strings.xml b/packages/SystemUI/res/values-bs/cm_strings.xml
new file mode 100644
index 0000000..34ba475
--- /dev/null
+++ b/packages/SystemUI/res/values-bs/cm_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Sistem</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ca/cm_strings.xml b/packages/SystemUI/res/values-ca/cm_strings.xml
new file mode 100644
index 0000000..b66789d
--- /dev/null
+++ b/packages/SystemUI/res/values-ca/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reinicia\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recuperació</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Descàrrega</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">S\'està reiniciant\u2026</string>
+ <string name="global_action_restart_recovery_progress">S\'està reiniciant al mode de recuperació\u2026</string>
+ <string name="global_action_restart_bootloader_progress">S\'està reiniciant al mode bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">S\'està reiniciant al mode de descàrrega\u2026</string>
+ <string name="global_action_restart_fastboot_progress">S\'està reiniciant al mode fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare sense fils</string>
+ <string name="quick_settings_powershare_off_powersave_label">PowerShare sense fils desactivat\nEstalvi de bateria</string>
+ <string name="quick_settings_powershare_off_low_battery_label">PowerShare sense fils desactivat\nBateria massa baixa</string>
+ <string name="quick_settings_powershare_enabled_label">PowerShare sense fils està activat</string>
+ <string name="quick_settings_caffeine_label">Cafeïna</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeïna desactivada.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeïna activada.</string>
+ <string name="status_bar_icons_title">Icones de la barra d\'estat</string>
+</resources>
diff --git a/packages/SystemUI/res/values-cs/cm_strings.xml b/packages/SystemUI/res/values-cs/cm_strings.xml
new file mode 100644
index 0000000..6c40e25
--- /dev/null
+++ b/packages/SystemUI/res/values-cs/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Restartovat\u2026</string>
+ <string name="global_action_restart_system">Systém</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Stahování</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Restartování\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restartování do režimu recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Restartování do režimu bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Restartování do režimu stahování\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Restartování do režimu fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Bezdrátové sdílení energie</string>
+ <string name="quick_settings_powershare_off_powersave_label">Bezdrátové sdílení energie je vypnuto\nSpořič baterie</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Bezdrátové sdílení energie je vypnuto\nNízký stav baterie</string>
+ <string name="quick_settings_powershare_enabled_label">Bezdrátové sdílení energie je zapnuto</string>
+ <string name="quick_settings_caffeine_label">Kofein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofein vyp.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofein zap.</string>
+ <string name="status_bar_icons_title">Ikony stavového řádku</string>
+</resources>
diff --git a/packages/SystemUI/res/values-cy/cm_strings.xml b/packages/SystemUI/res/values-cy/cm_strings.xml
new file mode 100644
index 0000000..f03059f
--- /dev/null
+++ b/packages/SystemUI/res/values-cy/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Ailgychwyn\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Modd adfer</string>
+ <string name="global_action_restart_bootloader">Cychwynnydd</string>
+ <string name="global_action_restart_download">Modd lawrlwytho</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Yn ailgychwyn\u2026</string>
+ <string name="global_action_restart_recovery_progress">Yn ailgychwyn i\'r modd adfer\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Yn ailgychwyn i\'r modd llwytho\'r system\u2026</string>
+ <string name="global_action_restart_download_progress">Yn ailgychwyn i\'r modd lawrlwytho\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Yn ailgychwyn in fodd fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Rhannu Pŵer Di-wifr</string>
+ <string name="quick_settings_powershare_off_powersave_label">Rhannu Pŵer Di-wifr i ffwrdd\nArbedwr batri</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Rhannu Pŵer Di-wifr i ffwrdd\nBatri rhy isel</string>
+ <string name="quick_settings_powershare_enabled_label">Mae Rhannu Pŵer Di-wifr ymlaen</string>
+ <string name="quick_settings_caffeine_label">Caffîn</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffîn i ffwrdd.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffîn ymlaen.</string>
+ <string name="status_bar_icons_title">Eiconau\'r bar statws</string>
+</resources>
diff --git a/packages/SystemUI/res/values-da/cm_strings.xml b/packages/SystemUI/res/values-da/cm_strings.xml
new file mode 100644
index 0000000..e8d80b9
--- /dev/null
+++ b/packages/SystemUI/res/values-da/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Genstart\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Gendannelse</string>
+ <string name="global_action_restart_bootloader">Opstartslæser</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Genstarter\u2026</string>
+ <string name="global_action_restart_recovery_progress">Genstarter i gendannelses tilstand\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Genstarter i opstartslæser tilstand\u2026</string>
+ <string name="global_action_restart_download_progress">Genstarter i download tilstand\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Genstarter i fastbootd mode\u2026</string>
+ <string name="quick_settings_powershare_label">Trådløs strømdeling</string>
+ <string name="quick_settings_powershare_off_powersave_label">Slå trådløs strøm deling fra\nBatterisparefunktion</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Trådløs PowerShare fra\nBatteri for lavt</string>
+ <string name="quick_settings_powershare_enabled_label">Trådløs Strømdeling er aktiveret</string>
+ <string name="quick_settings_caffeine_label">Koffein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Koffein fra.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Koffein til.</string>
+ <string name="status_bar_icons_title">Statuslinje ikoner</string>
+</resources>
diff --git a/packages/SystemUI/res/values-de/cm_strings.xml b/packages/SystemUI/res/values-de/cm_strings.xml
new file mode 100644
index 0000000..eed8b83
--- /dev/null
+++ b/packages/SystemUI/res/values-de/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Neustarten\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download-Modus</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Neustarten\u2026</string>
+ <string name="global_action_restart_recovery_progress">In Recovery-Modus neustarten\u2026</string>
+ <string name="global_action_restart_bootloader_progress">In Bootloader-Modus neustarten\u2026</string>
+ <string name="global_action_restart_download_progress">In Download-Modus neustarten\u2026</string>
+ <string name="global_action_restart_fastboot_progress">In Fastbootd-Modus neustarten\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare aus\nEnergiesparmodus</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare aus\nAkkustand zu niedrig</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare ist aktiviert</string>
+ <string name="quick_settings_caffeine_label">Koffein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Koffein aus.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Koffein an.</string>
+ <string name="status_bar_icons_title">Statusleistensymbole</string>
+</resources>
diff --git a/packages/SystemUI/res/values-de/leaf_strings.xml b/packages/SystemUI/res/values-de/leaf_strings.xml
new file mode 100644
index 0000000..a6b3dae
--- /dev/null
+++ b/packages/SystemUI/res/values-de/leaf_strings.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources></resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-el/cm_strings.xml b/packages/SystemUI/res/values-el/cm_strings.xml
new file mode 100644
index 0000000..178c294
--- /dev/null
+++ b/packages/SystemUI/res/values-el/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Επανεκκίνηση\u2026</string>
+ <string name="global_action_restart_system">Σύστημα</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Επανεκκίνηση\u2026</string>
+ <string name="global_action_restart_recovery_progress">Επανεκκίνηση σε λειτουργία recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Επανεκκίνηση σε λειτουργία bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Επανεκκίνηση σε λειτουργία download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Επανεκκίνηση σε λειτουργία fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Ασύρματος διαμοιρασμός ενέργειας</string>
+ <string name="quick_settings_powershare_off_powersave_label">Ασύρματος διαμοιρασμός ενέργειας απενεργοποιημένος\Εξοικονόμηση ενέργειας</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Ασύρματος διαμοιρασμός ενέργειας απενεργοποιημένος\nΜπαταρία πολύ χαμηλή</string>
+ <string name="quick_settings_powershare_enabled_label">Ο ασύρματος διαμοιρασμός ενέργειας είναι ενεργοποιημένος</string>
+ <string name="quick_settings_caffeine_label">Καφεΐνη</string>
+ <string name="accessibility_quick_settings_caffeine_off">Καφεΐνη ανενεργή.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Καφεΐνη ενεργή.</string>
+ <string name="status_bar_icons_title">Εικονίδια γραμμής κατάστασης</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/cm_strings.xml b/packages/SystemUI/res/values-en-rAU/cm_strings.xml
new file mode 100644
index 0000000..2c7522e
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rAU/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Restart\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Restarting\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restarting to recovery mode\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Restarting to bootloader mode\u2026</string>
+ <string name="global_action_restart_download_progress">Restarting to download mode\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Restarting to fastbootd mode\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on.</string>
+ <string name="status_bar_icons_title">Status bar icons</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/cm_strings.xml b/packages/SystemUI/res/values-en-rCA/cm_strings.xml
new file mode 100644
index 0000000..2c7522e
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rCA/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Restart\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Restarting\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restarting to recovery mode\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Restarting to bootloader mode\u2026</string>
+ <string name="global_action_restart_download_progress">Restarting to download mode\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Restarting to fastbootd mode\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on.</string>
+ <string name="status_bar_icons_title">Status bar icons</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/cm_strings.xml b/packages/SystemUI/res/values-en-rGB/cm_strings.xml
new file mode 100644
index 0000000..2c7522e
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rGB/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Restart\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Restarting\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restarting to recovery mode\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Restarting to bootloader mode\u2026</string>
+ <string name="global_action_restart_download_progress">Restarting to download mode\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Restarting to fastbootd mode\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on.</string>
+ <string name="status_bar_icons_title">Status bar icons</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/cm_strings.xml b/packages/SystemUI/res/values-en-rIN/cm_strings.xml
new file mode 100644
index 0000000..2c7522e
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rIN/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Restart\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Restarting\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restarting to recovery mode\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Restarting to bootloader mode\u2026</string>
+ <string name="global_action_restart_download_progress">Restarting to download mode\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Restarting to fastbootd mode\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on.</string>
+ <string name="status_bar_icons_title">Status bar icons</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rMX/cm_strings.xml b/packages/SystemUI/res/values-es-rMX/cm_strings.xml
new file mode 100644
index 0000000..e060ed3
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rMX/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Desactivar Cafeína.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Activar Cafeína.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/cm_strings.xml b/packages/SystemUI/res/values-es-rUS/cm_strings.xml
new file mode 100644
index 0000000..63d609b
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS/cm_strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Modo de recuperación (Recovery)</string>
+ <string name="global_action_restart_bootloader">Gestor de arranque (Bootloader)</string>
+ <string name="global_action_restart_download">Descarga</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reiniciando\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reiniciando en modo de recuperación\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reiniciando en modo del gestor de arranque\u2026</string>
+ <string name="global_action_restart_download_progress">Reiniciando en modo «descarga»\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reiniciando en modo fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Transferencia de energía inalámbrica</string>
+ <string name="quick_settings_powershare_off_powersave_label">Transferencia de energía inalámbrica apagada\nAhorro de batería</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Transferencia de energía inalámbrica apagada\nBatería demasiado baja</string>
+ <string name="quick_settings_powershare_enabled_label">Transferencia de energía inalámbrica activada</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Desactivar Cafeína.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Activar Cafeína.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es/cm_strings.xml b/packages/SystemUI/res/values-es/cm_strings.xml
new file mode 100644
index 0000000..ba1cf05
--- /dev/null
+++ b/packages/SystemUI/res/values-es/cm_strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recuperación</string>
+ <string name="global_action_restart_bootloader">Gestor de arranque</string>
+ <string name="global_action_restart_download">Descargar</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reiniciando\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reiniciando en modo recuperación\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reiniciando en gestor de arranque\u2026</string>
+ <string name="global_action_restart_download_progress">Reiniciando en modo descarga\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reiniciando en modo fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Transferencia de energía inalámbrica</string>
+ <string name="quick_settings_powershare_off_powersave_label">Transferencia de energía inalámbrica apagada\nAhorro de energía</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Transferencia de energía inalámbrica apagada\nBatería demasiado baja</string>
+ <string name="quick_settings_powershare_enabled_label">Transferencia de energía inalámbrica activada</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Desactivar Cafeína.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Activar Cafeína.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-et/cm_strings.xml b/packages/SystemUI/res/values-et/cm_strings.xml
new file mode 100644
index 0000000..83b54c3
--- /dev/null
+++ b/packages/SystemUI/res/values-et/cm_strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Taaskäivita\u2026</string>
+ <string name="global_action_restart_recovery">Taastus</string>
+ <string name="global_action_restart_bootloader">Käivituslaadur</string>
+ <string name="global_action_restart_download">Allalaadimine</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Taaskäivitamine\u2026</string>
+ <string name="global_action_restart_recovery_progress">Taaskäivitamine taastusrežiimi\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Taaskäivitamine käivituslaadurisse\u2026</string>
+ <string name="global_action_restart_download_progress">Taaskäivitamine allalaadimisrežiimi\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Taaskäivitamine fastbootd-režiimi\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare väljas\nAkusäästja</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare väljas\nAku on liiga tühi</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare sees</string>
+ <string name="quick_settings_caffeine_label">Kofeiin</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofeiin välja.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofeiin sisse.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-eu/cm_strings.xml b/packages/SystemUI/res/values-eu/cm_strings.xml
new file mode 100644
index 0000000..9869e43
--- /dev/null
+++ b/packages/SystemUI/res/values-eu/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Berreskuratzea</string>
+ <string name="global_action_restart_bootloader">Abiarazlea</string>
+ <string name="global_action_restart_download">Deskarga</string>
+ <string name="global_action_restart_progress">Berrabiarazten\u2026</string>
+ <string name="global_action_restart_recovery_progress">Berreskuratze modura berrabiarazten\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Abiarazle modura berrabiarazten\u2026</string>
+ <string name="global_action_restart_download_progress">Deskarga modura berrabiarazten\u2026</string>
+ <string name="quick_settings_caffeine_label">Kafeina</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafeina ez.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafeina bai.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fa/cm_strings.xml b/packages/SystemUI/res/values-fa/cm_strings.xml
new file mode 100644
index 0000000..484c120
--- /dev/null
+++ b/packages/SystemUI/res/values-fa/cm_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">سامانه</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fi/cm_strings.xml b/packages/SystemUI/res/values-fi/cm_strings.xml
new file mode 100644
index 0000000..58eb886a
--- /dev/null
+++ b/packages/SystemUI/res/values-fi/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Käynnistä uudelleen\u2026</string>
+ <string name="global_action_restart_system">Järjestelmä</string>
+ <string name="global_action_restart_recovery">Recovery-tila</string>
+ <string name="global_action_restart_bootloader">Bootloader-tila</string>
+ <string name="global_action_restart_download">Lataa</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Käynnistetään uudelleen\u2026</string>
+ <string name="global_action_restart_recovery_progress">Uudelleenkäynnistetään Recovery-tilaan\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Uudelleenkäynnistetään Bootloader-tilaan\u2026</string>
+ <string name="global_action_restart_download_progress">Uudelleenkäynnistetään Download-tilaan\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Uudelleenkäynnistetään fastbootd-tilaan\u2026</string>
+ <string name="quick_settings_powershare_label">Langaton virranjako</string>
+ <string name="quick_settings_powershare_off_powersave_label">Langaton virranjako pois\nVirransäästö</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Langaton virranjako pois\nAkun varaus alhainen</string>
+ <string name="quick_settings_powershare_enabled_label">Langaton virranjako käytössä</string>
+ <string name="quick_settings_caffeine_label">Kofeiini</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofeiini pois.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofeiini päällä.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/cm_strings.xml b/packages/SystemUI/res/values-fr-rCA/cm_strings.xml
new file mode 100644
index 0000000..adec89a
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA/cm_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_fastboot_progress">Redémarrage en mode fastbootd\u2026</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr/cm_strings.xml b/packages/SystemUI/res/values-fr/cm_strings.xml
new file mode 100644
index 0000000..dcd931d
--- /dev/null
+++ b/packages/SystemUI/res/values-fr/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Redémarrer\u2026 </string>
+ <string name="global_action_restart_system">Système</string>
+ <string name="global_action_restart_recovery">Récupération</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Télécharger</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Redémarrage\u2026</string>
+ <string name="global_action_restart_recovery_progress">Redémarrage en mode recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Redémarrage en mode bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Redémarrage en mode download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Redémarrage en mode fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Recharge sans-fil inversée</string>
+ <string name="quick_settings_powershare_off_powersave_label">Recharge sans-fil inversée désactivée\nÉconomiseur de batterie</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Recharge sans-fil inversée désactivée\nBatterie trop faible</string>
+ <string name="quick_settings_powershare_enabled_label">La recharge sans-fil inversée est activée</string>
+ <string name="quick_settings_caffeine_label">Caféine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caféine éteinte.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caféine allumée.</string>
+ <string name="status_bar_icons_title">Icônes de la barre d\'état</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fur-rIT/cm_strings.xml b/packages/SystemUI/res/values-fur-rIT/cm_strings.xml
new file mode 100644
index 0000000..d2f70f9
--- /dev/null
+++ b/packages/SystemUI/res/values-fur-rIT/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Gnûf inviament\u2026</string>
+ <string name="global_action_restart_system">Sisteme</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Gnûf inviament\u2026</string>
+ <string name="global_action_restart_recovery_progress">Gnûf inviament in modalitât recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Gnûf inviament in modalitât bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Gnûf inviament in modalitât download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Gnûf inviament in modalitât fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare distudât\nSparagn energjetic</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare distudât\nCjarie de batarie masse basse</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare atîf</string>
+ <string name="quick_settings_caffeine_label">Cafeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Disative cafeine.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Ative cafeine.</string>
+ <string name="status_bar_icons_title">Iconis de sbare di stât</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fy-rNL/cm_strings.xml b/packages/SystemUI/res/values-fy-rNL/cm_strings.xml
new file mode 100644
index 0000000..c7dca81
--- /dev/null
+++ b/packages/SystemUI/res/values-fy-rNL/cm_strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Systeem</string>
+ <string name="global_action_restart_recovery">Werstel</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Downloade</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Opnij opstarte\u2026</string>
+ <string name="global_action_restart_recovery_progress">Opnij opstarte nei werstelmodus\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Opnij opstarte nei bootloadermodus\u2026</string>
+ <string name="global_action_restart_download_progress">Opnij opstarte nei downloadmodus\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Opnij opstarte nei fastbootddmodus\u2026</string>
+ <string name="quick_settings_powershare_label">Triedleas PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Triedleaze PowerShare út\nBatterijbesparring</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Triedleaze PowerShare út\nBatterij te leech</string>
+ <string name="quick_settings_powershare_enabled_label">Triedleaze PowerShare is ynskeakele</string>
+ <string name="quick_settings_caffeine_label">Kafeïne</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafeïne út.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafeïne oan.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-gd/cm_strings.xml b/packages/SystemUI/res/values-gd/cm_strings.xml
new file mode 100644
index 0000000..e9c1e4d
--- /dev/null
+++ b/packages/SystemUI/res/values-gd/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Ath-thòisich\u2026</string>
+ <string name="global_action_restart_system">An siostam</string>
+ <string name="global_action_restart_recovery">Aiseag</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Luchdadh a-nuas</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">’Ga ath-thòiseachadh\u2026</string>
+ <string name="global_action_restart_recovery_progress">’Ga ath-thòiseachadh gun mhodh aisig\u2026</string>
+ <string name="global_action_restart_bootloader_progress">’Ga ath-thòiseachadh gun mhodh bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">’Ga ath-thòiseachadh gun mhodh luchdaidh a-nuas\u2026</string>
+ <string name="global_action_restart_fastboot_progress">’Ga ath-thòiseachadh gun mhodh fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Co-roinneadh cumhachd uèirleas</string>
+ <string name="quick_settings_powershare_off_powersave_label">Tha co-roinneadh cumhachd uèirleas dheth\nCaomhnaiche a’ bhataraidh</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Tha co-roinneadh cumhachd uèirleas dheth\nBataraidh ro fhann</string>
+ <string name="quick_settings_powershare_enabled_label">Tha co-roinneadh cumhachd uèirleas an comas</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Tha Caffeine dheth.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Tha Caffeine air.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-gl/cm_strings.xml b/packages/SystemUI/res/values-gl/cm_strings.xml
new file mode 100644
index 0000000..d59c04f
--- /dev/null
+++ b/packages/SystemUI/res/values-gl/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reiniciar\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recuperación</string>
+ <string name="global_action_restart_bootloader">Cargador de arranque</string>
+ <string name="global_action_restart_download">Descarga</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reiniciando\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reiniciando en modo recuperación\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reiniciando en modo bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Reiniciando en modo descarga\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reiniciando no modo fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare sen fíos</string>
+ <string name="quick_settings_powershare_off_powersave_label">PowerShare sen fíos desactivado\nAforro de batería</string>
+ <string name="quick_settings_powershare_off_low_battery_label">PowerShare sen fíos desactivado\nBatería baixa de máis</string>
+ <string name="quick_settings_powershare_enabled_label">PowerShare sen fíos está activado</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Deshabilitar Cafeína.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Habilitar Cafeína.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-gu/cm_strings.xml b/packages/SystemUI/res/values-gu/cm_strings.xml
new file mode 100644
index 0000000..85cbba0
--- /dev/null
+++ b/packages/SystemUI/res/values-gu/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">કેફીન</string>
+ <string name="accessibility_quick_settings_caffeine_off">કેફીન બંધ.</string>
+ <string name="accessibility_quick_settings_caffeine_on">કેફીન ચાલુ.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hi/cm_strings.xml b/packages/SystemUI/res/values-hi/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-hi/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-hr/cm_strings.xml b/packages/SystemUI/res/values-hr/cm_strings.xml
new file mode 100644
index 0000000..c99d63a
--- /dev/null
+++ b/packages/SystemUI/res/values-hr/cm_strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Ponovno pokretanje\u2026</string>
+ <string name="global_action_restart_system">Sustav</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Preuzmi</string>
+ <string name="global_action_restart_progress">Ponovno pokretanje\u2026</string>
+ <string name="global_action_restart_recovery_progress">Ponovno pokretanje u Recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Ponovno pokretanje u bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Ponovno pokretanje u način preuzimanja\u2026</string>
+ <string name="quick_settings_caffeine_label">Kofein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofein isključen.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofein uključen.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hu/cm_strings.xml b/packages/SystemUI/res/values-hu/cm_strings.xml
new file mode 100644
index 0000000..516b660
--- /dev/null
+++ b/packages/SystemUI/res/values-hu/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Újraindítás\u2026</string>
+ <string name="global_action_restart_system">Rendszer</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download-mód</string>
+ <string name="global_action_restart_fastboot">Fastbootd-mód</string>
+ <string name="global_action_restart_progress">Újraindítás\u2026</string>
+ <string name="global_action_restart_recovery_progress">Újraindítás recovery-módba\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Újraindítás bootloader-módba\u2026</string>
+ <string name="global_action_restart_download_progress">Újraindítás download-módba\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Újraindítás Fastbootd-módba\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">PowerShare ki\nAkkukímélő</string>
+ <string name="quick_settings_powershare_off_low_battery_label">PowerShare ki\nAlacsony akkuszint</string>
+ <string name="quick_settings_powershare_enabled_label">A PowerShare bekapcsolva</string>
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine ki.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine be.</string>
+ <string name="status_bar_icons_title">Az állapotsor ikonjai</string>
+</resources>
diff --git a/packages/SystemUI/res/values-in/cm_strings.xml b/packages/SystemUI/res/values-in/cm_strings.xml
new file mode 100644
index 0000000..e3182d6
--- /dev/null
+++ b/packages/SystemUI/res/values-in/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Mulai ulang\u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Memulai ulang\u2026</string>
+ <string name="global_action_restart_recovery_progress">Memulai ulang ke mode recovery</string>
+ <string name="global_action_restart_bootloader_progress">Memulai ulang ke mode bootloader</string>
+ <string name="global_action_restart_download_progress">Memulai ulang ke mode download</string>
+ <string name="global_action_restart_fastboot_progress">Memulai ulang ke mode fastbootd</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare nonaktif\nPenghemat baterai</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare nonaktif\nBaterai terlalu lemah</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare diaktifkan</string>
+ <string name="quick_settings_caffeine_label">Kafein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafein mati.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafein hidup.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-is/cm_strings.xml b/packages/SystemUI/res/values-is/cm_strings.xml
new file mode 100644
index 0000000..46aa2c2
--- /dev/null
+++ b/packages/SystemUI/res/values-is/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Endurræsa\u2026</string>
+ <string name="global_action_restart_system">Kerfi</string>
+ <string name="global_action_restart_recovery">Endurheimting</string>
+ <string name="global_action_restart_bootloader">Ræsistjóri</string>
+ <string name="global_action_restart_download">Niðurhal</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Endurræsi…\u2026</string>
+ <string name="global_action_restart_recovery_progress">Endurræsi í endurheimtuham\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Endurræsi í ræsistjóraham\u2026</string>
+ <string name="global_action_restart_download_progress">Endurræsi í niðurhalsham\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Endurræsi í fastbootd-ham\u2026</string>
+ <string name="quick_settings_powershare_label">Þráðlaust PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Þráðlaust PowerShare slökkt\nRafhlöðusparnaður</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Þráðlaust PowerShare slökkt\nOf lítið á rafhlöðu</string>
+ <string name="quick_settings_powershare_enabled_label">Þráðlaust PowerShare er virkt</string>
+ <string name="quick_settings_caffeine_label">Lengri skjátími (Caffeine)</string>
+ <string name="accessibility_quick_settings_caffeine_off">Slökkt á Caffeine.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kveikt á Caffeine.</string>
+ <string name="status_bar_icons_title">Táknmyndir á stöðustiku</string>
+</resources>
diff --git a/packages/SystemUI/res/values-it/cm_strings.xml b/packages/SystemUI/res/values-it/cm_strings.xml
new file mode 100644
index 0000000..48c101c
--- /dev/null
+++ b/packages/SystemUI/res/values-it/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Riavvio\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Riavvio\u2026</string>
+ <string name="global_action_restart_recovery_progress">Riavvio in modalità recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Riavvio in modalità bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Riavvio in modalità download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Riavvio in modalità fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare spento\nRisparmio energetico</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare spento\nBatteria quasi scarica</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare attivo</string>
+ <string name="quick_settings_caffeine_label">Caffeina</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeina off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeina on.</string>
+ <string name="status_bar_icons_title">Icone della barra di stato</string>
+</resources>
diff --git a/packages/SystemUI/res/values-iw/cm_strings.xml b/packages/SystemUI/res/values-iw/cm_strings.xml
new file mode 100644
index 0000000..afcf890
--- /dev/null
+++ b/packages/SystemUI/res/values-iw/cm_strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">מערכת</string>
+ <string name="global_action_restart_recovery">שחזור (Recovery)</string>
+ <string name="global_action_restart_bootloader">מנהל אתחול (bootloader)</string>
+ <string name="global_action_restart_download">מצב הורדה</string>
+ <string name="global_action_restart_progress">מפעיל מחדש\u2026</string>
+ <string name="global_action_restart_recovery_progress">מפעיל מחדש למצב שחזור (Recovery)\u2026</string>
+ <string name="global_action_restart_bootloader_progress">מפעיל מחדש למנהל אתחול (bootloader)\u2026</string>
+ <string name="global_action_restart_download_progress">מפעיל מחדש למצב הורדה\u2026</string>
+ <string name="global_action_restart_fastboot_progress">מפעיל מחדש למצב fastboot\u2026</string>
+ <string name="quick_settings_powershare_enabled_label">טעינה אלחוטית הפוכה מופעלת</string>
+ <string name="quick_settings_caffeine_label">קפאין</string>
+ <string name="accessibility_quick_settings_caffeine_off">קפאין כבוי.</string>
+ <string name="accessibility_quick_settings_caffeine_on">קפאין פועל.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ja/cm_strings.xml b/packages/SystemUI/res/values-ja/cm_strings.xml
new file mode 100644
index 0000000..2a8f71b
--- /dev/null
+++ b/packages/SystemUI/res/values-ja/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">再起動\u2026</string>
+ <string name="global_action_restart_system">システム</string>
+ <string name="global_action_restart_recovery">リカバリー</string>
+ <string name="global_action_restart_bootloader">ブートローダー</string>
+ <string name="global_action_restart_download">ダウンロード</string>
+ <string name="global_action_restart_fastboot">fastbootd</string>
+ <string name="global_action_restart_progress">再起動中\u2026</string>
+ <string name="global_action_restart_recovery_progress">リカバリーモードで再起動中\u2026</string>
+ <string name="global_action_restart_bootloader_progress">ブートローダーモードで再起動中\u2026</string>
+ <string name="global_action_restart_download_progress">ダウンロードモードで再起動中\u2026</string>
+ <string name="global_action_restart_fastboot_progress">fastbootd モードで再起動中\u2026</string>
+ <string name="quick_settings_powershare_label">ワイヤレス PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">ワイヤレス PowerShare OFF\nバッテリー節約</string>
+ <string name="quick_settings_powershare_off_low_battery_label">ワイヤレス PowerShare OFF\nバッテリー残量低下</string>
+ <string name="quick_settings_powershare_enabled_label">ワイヤレス PowerShare は有効です</string>
+ <string name="quick_settings_caffeine_label">カフェイン</string>
+ <string name="accessibility_quick_settings_caffeine_off">カフェインはOFFです。</string>
+ <string name="accessibility_quick_settings_caffeine_on">カフェインはONです。</string>
+ <string name="status_bar_icons_title">ステータスバーのアイコン</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ka/cm_strings.xml b/packages/SystemUI/res/values-ka/cm_strings.xml
new file mode 100644
index 0000000..8152175
--- /dev/null
+++ b/packages/SystemUI/res/values-ka/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">გადატვირთვა\u2026</string>
+ <string name="global_action_restart_system">სისტემა</string>
+ <string name="global_action_restart_recovery">აღმდგენი</string>
+ <string name="global_action_restart_bootloader">ჩამტვირთავი</string>
+ <string name="global_action_restart_download">ჩატვირთვა</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">გადაიტვირთება\u2026</string>
+ <string name="global_action_restart_recovery_progress">გადაიტვირთება აღმდგენ რეჟიმში\u2026</string>
+ <string name="global_action_restart_bootloader_progress">გადაიტვირთება ჩამტვირთავში\u2026</string>
+ <string name="global_action_restart_download_progress">გადაიტვირთება ჩატვირთვის რეჟიმში\u2026</string>
+ <string name="global_action_restart_fastboot_progress">გადაიტვირთება Fastbootd-რეჟიმში\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare გამორთ.\nბატარეის დამზოგი</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare გამორთ.\nბატარეის მუხტი დაბალია</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare ჩართულია</string>
+ <string name="quick_settings_caffeine_label">სიფხიზლე</string>
+ <string name="accessibility_quick_settings_caffeine_off">სიფხიზლე გამორთ.</string>
+ <string name="accessibility_quick_settings_caffeine_on">სიფხიზლე ჩართ.</string>
+ <string name="status_bar_icons_title">ხატულები მდგომარეობის ზოლზე</string>
+</resources>
diff --git a/packages/SystemUI/res/values-kab-rDZ/cm_strings.xml b/packages/SystemUI/res/values-kab-rDZ/cm_strings.xml
new file mode 100644
index 0000000..890c917
--- /dev/null
+++ b/packages/SystemUI/res/values-kab-rDZ/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Tulsa tanekra\u2026</string>
+ <string name="global_action_restart_system">Anagraw</string>
+ <string name="global_action_restart_recovery">Tiririt</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Sider</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Tulsa n tnekra\u2026</string>
+ <string name="quick_settings_caffeine_label">Kafiyin</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafiyin insa.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafiyin irmed.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-kn/cm_strings.xml b/packages/SystemUI/res/values-kn/cm_strings.xml
new file mode 100644
index 0000000..859f1c2
--- /dev/null
+++ b/packages/SystemUI/res/values-kn/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">ವ್ಯವಸ್ಥೆ</string>
+ <string name="global_action_restart_recovery">ಮರುಚೇತರಿಕೆ</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">ಇಳಿಸು</string>
+ <string name="global_action_restart_progress">ಪುನರಾರಂಭವಾಗುತ್ತಿದೆ\u2026</string>
+ <string name="global_action_restart_recovery_progress">Restarting ರೀತಿಗೆ ಪುನರಾರಂಭವಾಗುತ್ತಿದೆ</string>
+ <string name="global_action_restart_bootloader_progress">Bootloader ರೀತಿಗೆ ಪುನರಾರಂಭವಾಗುತ್ತಿದೆ\u2026</string>
+ <string name="global_action_restart_download_progress">Download ರೀತಿಗೆ ಪುನರಾರಂಭವಾಗುತ್ತಿದೆ\u2026</string>
+ <string name="quick_settings_caffeine_label">ಕ್ಯಾಫೀನ್</string>
+ <string name="accessibility_quick_settings_caffeine_off">ಕ್ಯಾಫೀನ್ ಆಫ್.</string>
+ <string name="accessibility_quick_settings_caffeine_on">ಕ್ಯಾಫೀನ್ ಒನ್.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ko/cm_strings.xml b/packages/SystemUI/res/values-ko/cm_strings.xml
new file mode 100644
index 0000000..eb37203
--- /dev/null
+++ b/packages/SystemUI/res/values-ko/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">다시 시작\u2026</string>
+ <string name="global_action_restart_system">시스템</string>
+ <string name="global_action_restart_recovery">복구 모드</string>
+ <string name="global_action_restart_bootloader">부트로더</string>
+ <string name="global_action_restart_download">다운로드</string>
+ <string name="global_action_restart_fastboot">Fastbootd 모드</string>
+ <string name="global_action_restart_progress">다시 시작하는 중\u2026</string>
+ <string name="global_action_restart_recovery_progress">복구 모드로 다시 시작하는 중\u2026</string>
+ <string name="global_action_restart_bootloader_progress">부트로더 모드로 다시 시작하는 중\u2026</string>
+ <string name="global_action_restart_download_progress">다운로드 모드로 다시 시작하는 중\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Fastbootd 모드로 다시 시작하는 중\u2026</string>
+ <string name="quick_settings_powershare_label">무선 PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">무선 PowerShare 꺼짐 \n배터리 절약 모드</string>
+ <string name="quick_settings_powershare_off_low_battery_label">무선 PowerShare 꺼짐\n 배터리 부족</string>
+ <string name="quick_settings_powershare_enabled_label">무선 PowerShare 켜짐</string>
+ <string name="quick_settings_caffeine_label">카페인</string>
+ <string name="accessibility_quick_settings_caffeine_off">카페인 꺼짐.</string>
+ <string name="accessibility_quick_settings_caffeine_on">카페인 켜짐.</string>
+ <string name="status_bar_icons_title">상태 표시줄 아이콘</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ku/cm_strings.xml b/packages/SystemUI/res/values-ku/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-ku/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 55606aa..efae1f5 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -47,9 +47,6 @@
<dimen name="biometric_auth_pattern_view_size">248dp</dimen>
<dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
- <dimen name="global_actions_power_dialog_item_height">130dp</dimen>
- <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen>
-
<dimen name="controls_management_top_padding">12dp</dimen>
<dimen name="controls_management_titles_margin">8dp</dimen>
<dimen name="controls_management_indicator_top_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/values-land/leaf_config.xml b/packages/SystemUI/res/values-land/leaf_config.xml
new file mode 100644
index 0000000..752de57
--- /dev/null
+++ b/packages/SystemUI/res/values-land/leaf_config.xml
@@ -0,0 +1,4 @@
+<resources>
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">4</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-lt/cm_strings.xml b/packages/SystemUI/res/values-lt/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-lt/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-lu/cm_strings.xml b/packages/SystemUI/res/values-lu/cm_strings.xml
new file mode 100644
index 0000000..d1b107f
--- /dev/null
+++ b/packages/SystemUI/res/values-lu/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">Koffein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Koffein aus.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Koffein un.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-lv/cm_strings.xml b/packages/SystemUI/res/values-lv/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-lv/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-mk/cm_strings.xml b/packages/SystemUI/res/values-mk/cm_strings.xml
new file mode 100644
index 0000000..022daea
--- /dev/null
+++ b/packages/SystemUI/res/values-mk/cm_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Систем</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ml/cm_strings.xml b/packages/SystemUI/res/values-ml/cm_strings.xml
new file mode 100644
index 0000000..8a14660
--- /dev/null
+++ b/packages/SystemUI/res/values-ml/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">കഫീൻ</string>
+ <string name="accessibility_quick_settings_caffeine_off">കഫീൻ ഓഫ്.</string>
+ <string name="accessibility_quick_settings_caffeine_on">കഫീൻ ഓണ്.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mr/cm_strings.xml b/packages/SystemUI/res/values-mr/cm_strings.xml
new file mode 100644
index 0000000..d5e0bd8
--- /dev/null
+++ b/packages/SystemUI/res/values-mr/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">कॅफीन</string>
+ <string name="accessibility_quick_settings_caffeine_off">कॅफीन बंद.</string>
+ <string name="accessibility_quick_settings_caffeine_on">कॅफिन चालू.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-my/cm_strings.xml b/packages/SystemUI/res/values-my/cm_strings.xml
new file mode 100644
index 0000000..6265fac
--- /dev/null
+++ b/packages/SystemUI/res/values-my/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">အစမှ ပြန်စတင်မည် \u2026</string>
+ <string name="global_action_restart_system">စနစ်</string>
+ <string name="global_action_restart_recovery">ပြန်လည်ရယူခြင်း</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">ဒေါင်းလုပ်မုဒ်</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">အစမှ ပြန်စတင်နေသည် \u2026</string>
+ <string name="global_action_restart_recovery_progress">ပြန်လည်ဆယ်ယူခြင်းမုဒ်ကို ပြန်စတင်နေသည် \u2026</string>
+ <string name="global_action_restart_bootloader_progress">Bootloader မုဒ်ကို ပြန်စတင်နေသည် \u2026</string>
+ <string name="global_action_restart_download_progress">ဒေါင်းလုပ်မုဒ်ကို ပြန်စတင်နေသည် \u2026</string>
+ <string name="global_action_restart_fastboot_progress">Fastbootd မုဒ်ကို ပြန်စတင်နေသည် \u2026</string>
+ <string name="quick_settings_powershare_label">ကြိုးမဲ့အားသွင်းစနစ်မျှဝေသုံးခြင်း</string>
+ <string name="quick_settings_powershare_off_powersave_label">ဘက်ထရီချွေတာချိန်တွင်\n ကြိုးမဲ့အားသွင်းစနစ်မျှဝေသုံးခြင်းကိုပိတ်မည်</string>
+ <string name="quick_settings_powershare_off_low_battery_label">ဘက်ထရီအားနည်းချိန်တွင်\n ကြိုးမဲ့အားသွင်းစနစ်မျှဝေသုံးခြင်းကိုပိတ်မည်</string>
+ <string name="quick_settings_powershare_enabled_label">ကြိုးမဲ့အားသွင်းစနစ်မျှဝေသုံးခြင်းအားပိတ်မည်</string>
+ <string name="quick_settings_caffeine_label">ကဖင်းမုဒ်</string>
+ <string name="accessibility_quick_settings_caffeine_off">ကဖင်းစနစ်ပိတ်မည်</string>
+ <string name="accessibility_quick_settings_caffeine_on">ကဖင်းစနစ်ဖွင့်မည်။</string>
+</resources>
diff --git a/packages/SystemUI/res/values-nb/cm_strings.xml b/packages/SystemUI/res/values-nb/cm_strings.xml
new file mode 100644
index 0000000..48f2a68
--- /dev/null
+++ b/packages/SystemUI/res/values-nb/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Gjenoppretting</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download-modus</string>
+ <string name="global_action_restart_progress">Starter på nytt\u2026</string>
+ <string name="global_action_restart_recovery_progress">Starter på nytt til gjenoppretting-modus\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Starter på nytt til bootloader-modus\u2026</string>
+ <string name="global_action_restart_download_progress">Starter på nytt til download-modus\u2026</string>
+ <string name="quick_settings_caffeine_label">Koffein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Koffein av.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Koffein på.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index b6971d3..670af5f 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Dialog"/>
@@ -24,6 +25,36 @@
<item name="android:windowIsFloating">true</item>
</style>
+ <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault.SystemUI">
+ <item name="isQsTheme">true</item>
+ <item name="lightIconTheme">@style/QSIconTheme</item>
+ <item name="darkIconTheme">@style/QSIconTheme</item>
+ <item name="android:colorError">@*android:color/error_color_material_dark</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
+
+ <item name="surfaceBright">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="android:colorBackground">?attr/surfaceBright</item>
+ <item name="scHigh">?androidprv:attr/materialColorSurfaceContainerHigh</item>
+ <item name="primary">?androidprv:attr/materialColorPrimary</item>
+ <item name="tertiary">?androidprv:attr/materialColorTertiary</item>
+ <item name="onSurface">?androidprv:attr/materialColorOnSurface</item>
+ <item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="outline">?androidprv:attr/materialColorOutline</item>
+
+ <item name="shadeActive">?android:attr/colorAccent</item>
+ <item name="onShadeActive">?android:attr/textColorPrimaryInverse</item>
+ <item name="onShadeActiveVariant">?android:attr/textColorSecondaryInverse</item>
+ <item name="shadeInactive">@android:color/system_neutral1_800</item>
+ <item name="onShadeInactive">?android:attr/textColorPrimary</item>
+ <item name="onShadeInactiveVariant">?android:attr/textColorSecondary</item>
+ <item name="shadeDisabled">@color/shade_disabled</item>
+ <item name="underSurface">@android:color/system_neutral1_900</item>
+ <item name="android:itemTextAppearance">@style/Control.MenuItem</item>
+ <item name="android:colorPrimary">@*android:color/system_neutral1_0</item>
+ <item name="android:colorSecondary">@*android:color/system_neutral1_50</item>
+ </style>
+
<!-- Screenshots -->
<style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:windowNoTitle">true</item>
diff --git a/packages/SystemUI/res/values-nl/cm_strings.xml b/packages/SystemUI/res/values-nl/cm_strings.xml
new file mode 100644
index 0000000..3817ccf
--- /dev/null
+++ b/packages/SystemUI/res/values-nl/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Herstarten\u2026</string>
+ <string name="global_action_restart_system">Systeem</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Opnieuw opstarten\u2026</string>
+ <string name="global_action_restart_recovery_progress">Opnieuw opstarten naar recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Opnieuw opstarten naar bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Opnieuw opstarten naar download-modus\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Herstarten naar fastbootd modus\u2026</string>
+ <string name="quick_settings_powershare_label">Draadloze PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Draadloze PowerShare uit\nBatterijbesparing</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Draadloze PowerShare uit\nBatterij te ver leeg</string>
+ <string name="quick_settings_powershare_enabled_label">Draadloze PowerShare is ingeschakeld</string>
+ <string name="quick_settings_caffeine_label">Cafeïne</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeïne uit.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeïne aan.</string>
+ <string name="status_bar_icons_title">Statusbalk iconen</string>
+</resources>
diff --git a/packages/SystemUI/res/values-or/cm_strings.xml b/packages/SystemUI/res/values-or/cm_strings.xml
new file mode 100644
index 0000000..4b48e7a
--- /dev/null
+++ b/packages/SystemUI/res/values-or/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pl/cm_strings.xml b/packages/SystemUI/res/values-pl/cm_strings.xml
new file mode 100644
index 0000000..d07ddcd
--- /dev/null
+++ b/packages/SystemUI/res/values-pl/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Uruchom ponownie\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Ponowne uruchamianie\u2026</string>
+ <string name="global_action_restart_recovery_progress">Uruchamianie w trybie Recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Uruchamianie w trybie Bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Uruchamianie w trybie Download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Uruchamianie w trybie Fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Ładowanie bezprzewodowe PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wyłączono ładowanie bezprzewodowe PowerShare\nOszczędzanie baterii</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wyłączono ładowanie bezprzewodowe PowerShare\nZbyt niski poziom baterii</string>
+ <string name="quick_settings_powershare_enabled_label">Włączono ładowanie bezprzewodowe PowerShare</string>
+ <string name="quick_settings_caffeine_label">Kofeina</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofeina wył.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofeina wł.</string>
+ <string name="status_bar_icons_title">Ikony paska stanu</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/cm_strings.xml b/packages/SystemUI/res/values-pt-rBR/cm_strings.xml
new file mode 100644
index 0000000..309d236
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rBR/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reiniciar\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recuperação</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reiniciando\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reiniciando no modo de recuperação\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reiniciando no modo bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Reiniciando no modo de download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reiniciando no modo fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare desligado\nEconomia de bateria</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare desligado\nBateria muito fraca</string>
+ <string name="quick_settings_powershare_enabled_label">O Wireless PowerShare está ativado</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeína desligada.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeína ligada.</string>
+ <string name="status_bar_icons_title">Ícones da barra de status</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/leaf_strings.xml b/packages/SystemUI/res/values-pt-rBR/leaf_strings.xml
new file mode 100644
index 0000000..bf2574f
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rBR/leaf_strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_compass_label">Bússola</string>
+ <string name="quick_settings_compass_value"><xliff:g id="degrees">%1$.0f</xliff:g>° <xliff:g id="direction">%2$s</xliff:g></string>
+ <string name="quick_settings_compass_init">Inicializando…</string>
+ <string name="accessibility_quick_settings_compass_off">Bússola desligada</string>
+ <string name="accessibility_quick_settings_compass_on">Bússola ligada</string>
+ <string name="accessibility_quick_settings_compass_changed_off">Bússola desligada</string>
+ <string name="accessibility_quick_settings_compass_changed_on">Bússola ativada</string>
+ <string name="quick_settings_compass_N">N</string>
+ <string name="quick_settings_compass_NE">NE</string>
+ <string name="quick_settings_compass_E">L</string>
+ <string name="quick_settings_compass_SE">SE</string>
+ <string name="quick_settings_compass_S">S</string>
+ <string name="quick_settings_compass_SW">SO</string>
+ <string name="quick_settings_compass_W">O</string>
+ <string name="quick_settings_compass_NW">NO</string>
+ <string name="qs_data_switch_label">Mudar cartão de dados</string>
+ <string name="qs_data_switch_toast_0">Atualmente nenhum cartão SIM está inserido</string>
+ <string name="qs_data_switch_toast_1">Atualmente apenas um cartão SIM está inserido</string>
+ <string name="qs_data_switch_text_1">SIM 1</string>
+ <string name="qs_data_switch_text_2">SIM 2</string>
+ <string name="qs_data_switch_changed_1">Cartão de dados alterado para o SIM 1.</string>
+ <string name="qs_data_switch_changed_2">Cartão de dados alterado para o SIM 2.</string>
+ <string name="usage_data">usado hoje</string>
+ <string name="usage_data_default_suffix">Dados móveis</string>
+ <string name="usage_wifi_default_suffix">Wi-Fi</string>
+ <plurals name="quick_settings_internet_hotspot_summary_num_devices">
+ <item quantity="one">%d dispositivo conectado</item>
+ <item quantity="many">%d dispositivos conectados</item>
+ <item quantity="other">%d dispositivos conectados</item>
+ </plurals>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-pt-rPT/cm_strings.xml b/packages/SystemUI/res/values-pt-rPT/cm_strings.xml
new file mode 100644
index 0000000..db63764
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rPT/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reiniciar\u2026</string>
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_recovery">Recuperação</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Transferência</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">A reiniciar\u2026</string>
+ <string name="global_action_restart_recovery_progress">A reiniciar para o modo de recuperação\u2026</string>
+ <string name="global_action_restart_bootloader_progress">A reiniciar para o modo de bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">A reiniciar para o modo de transferência\u2026</string>
+ <string name="global_action_restart_fastboot_progress">A reiniciar para o modo de fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare desligado\nPoupança de energia</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare\nBateria demasiado fraca</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare está ativado</string>
+ <string name="quick_settings_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeína desligada.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeína ligada.</string>
+ <string name="status_bar_icons_title">Ícones da barra de estado</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ro/cm_strings.xml b/packages/SystemUI/res/values-ro/cm_strings.xml
new file mode 100644
index 0000000..afa8d8f
--- /dev/null
+++ b/packages/SystemUI/res/values-ro/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Repornire\u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Download</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Se repornește\u2026</string>
+ <string name="global_action_restart_recovery_progress">Repornire în modul recuperare\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Repornire în modul bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Repornire în modul descărcare\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Repornire în modul fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare oprit\nEconomisire baterie</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare oprit\nBaterie prea slabă</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare este activat</string>
+ <string name="quick_settings_caffeine_label">Cofeină</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cofeină oprit.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cofeină pornit.</string>
+ <string name="status_bar_icons_title">Pictograme bară de stare</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ru/cm_strings.xml b/packages/SystemUI/res/values-ru/cm_strings.xml
new file mode 100644
index 0000000..60ec292
--- /dev/null
+++ b/packages/SystemUI/res/values-ru/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Перезапуск\u2026</string>
+ <string name="global_action_restart_system">Система</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Загрузчик</string>
+ <string name="global_action_restart_download">Загрузка</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Перезапуск\u2026</string>
+ <string name="global_action_restart_recovery_progress">Перезапуск в Recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Перезапуск в режим загрузчика\u2026</string>
+ <string name="global_action_restart_download_progress">Перезапуск в режим загрузки\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Перезапуск в режим fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare выключен\nЭкономия энергии</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare выключен\nНизкий заряд батареи</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare включен</string>
+ <string name="quick_settings_caffeine_label">Кофеин</string>
+ <string name="accessibility_quick_settings_caffeine_off">Кофеин выкл.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Кофеин вкл.</string>
+ <string name="status_bar_icons_title">Значки строки состояния</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ru/leaf_strings.xml b/packages/SystemUI/res/values-ru/leaf_strings.xml
new file mode 100644
index 0000000..85cfc3a
--- /dev/null
+++ b/packages/SystemUI/res/values-ru/leaf_strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="qs_data_switch_label">Сменить SIM для передачи данных</string>
+ <string name="qs_data_switch_changed_2">Выбрана SIM 2 для передачи данных.</string>
+ <string name="screenshot_delete_description">Удалить скриншот</string>
+ <string name="network_traffic_summary">Включить/выключить индикатор скорости сети в строке состояния</string>
+ <plurals name="quick_settings_internet_hotspot_summary_num_devices">
+ <item quantity="one">Подключено %d устройство</item>
+ <item quantity="few">Подключено %d устройства</item>
+ <item quantity="many">Подключено %d устройств</item>
+ <item quantity="other">Подключено %d устройств</item>
+ </plurals>
+ <string name="qs_data_switch_text_2">SIM 2</string>
+ <string name="screenshot_delete_label">Удалить</string>
+ <string name="usage_data_default_suffix">Данные</string>
+ <string name="show_fourg_icon_title">Иконка 4G</string>
+ <string name="usage_wifi_default_suffix">Wi-Fi</string>
+ <string name="qs_data_switch_toast_0">Нет SIM карты</string>
+ <string name="network_traffic_title">Индикатор трафика</string>
+ <string name="usage_data">исп. сегодня</string>
+ <string name="qs_data_switch_text_1">SIM 1</string>
+ <string name="show_fourg_icon_summary">Показывать 4G вместо LTE в строке состояния</string>
+ <string name="qs_data_switch_changed_1">Выбрана SIM 1 для передачи данных.</string>
+ <string name="qs_data_switch_toast_1">Присутствует одна SIM карта</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sc-rIT/cm_strings.xml b/packages/SystemUI/res/values-sc-rIT/cm_strings.xml
new file mode 100644
index 0000000..e11289b
--- /dev/null
+++ b/packages/SystemUI/res/values-sc-rIT/cm_strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Sistema</string>
+ <string name="global_action_restart_download">Iscàrriga</string>
+ <string name="global_action_restart_progress">Torrende a allùghere\u2026</string>
+ <string name="quick_settings_caffeine_label">Cafeina</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeina istudada.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeina alluta.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-si/cm_strings.xml b/packages/SystemUI/res/values-si/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-si/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-sk/cm_strings.xml b/packages/SystemUI/res/values-sk/cm_strings.xml
new file mode 100644
index 0000000..c649b11
--- /dev/null
+++ b/packages/SystemUI/res/values-sk/cm_strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Reštartovať\u2026</string>
+ <string name="global_action_restart_system">Systém</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Sťahovanie</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Reštartovanie\u2026</string>
+ <string name="global_action_restart_recovery_progress">Reštartovanie do režimu Recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Reštartovanie do režimu Bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Reštartovanie do režimu sťahovania\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Reštartovanie do režimu fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">Bezdrôtové reverzné nabíjanie PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Bezdrôtové reverzné nabíjanie PowerShare vypnuté\nŠetrenie batérie</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Bezdrôtové reverzné nabíjanie PowerShare vypnuté\nSlabá batéria</string>
+ <string name="quick_settings_powershare_enabled_label">Bezdrôtové reverzné nabíjanie PowerShare je zapnuté</string>
+ <string name="quick_settings_caffeine_label">Kofeín</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofeín vypnutý.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofeín zapnutý.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sl/cm_strings.xml b/packages/SystemUI/res/values-sl/cm_strings.xml
new file mode 100644
index 0000000..f8ba688
--- /dev/null
+++ b/packages/SystemUI/res/values-sl/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Vnovičen zagon \u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Obnovitev</string>
+ <string name="global_action_restart_bootloader">Zagonski nalagalnik</string>
+ <string name="global_action_restart_download">Prenos</string>
+ <string name="global_action_restart_fastboot">Namen. zagon. nalagalnik</string>
+ <string name="global_action_restart_progress">Vnovično zaganjanje \u2026</string>
+ <string name="global_action_restart_recovery_progress">Vnovično zaganjanje v obnovitveni način \u2026</string>
+ <string name="global_action_restart_bootloader_progress">Vnovično zaganjanje v način zagonskega nalagalnika \u2026</string>
+ <string name="global_action_restart_download_progress">Vnovično zaganjanje v način prenosa \u2026</string>
+ <string name="global_action_restart_fastboot_progress">Vnovično zaganjanje v način namen. zagon. nalagalnika \u2026</string>
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare izklopljen\nVarčevanje z energijo baterije</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare izklopljen\nRaven napolnjenosti baterije prenizka</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare je omogočen</string>
+ <string name="quick_settings_caffeine_label">Kofein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kofein izklopljen.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kofein vklopljen.</string>
+ <string name="status_bar_icons_title">Ikone vrstice stanja</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sq/cm_strings.xml b/packages/SystemUI/res/values-sq/cm_strings.xml
new file mode 100644
index 0000000..3b5c910
--- /dev/null
+++ b/packages/SystemUI/res/values-sq/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Rinisni\u2026</string>
+ <string name="global_action_restart_system">Sistemi</string>
+ <string name="global_action_restart_recovery">Rikuperimi</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Shkarko</string>
+ <string name="global_action_restart_fastboot">Fastboot</string>
+ <string name="global_action_restart_progress">Duke u rindezur\u2026</string>
+ <string name="global_action_restart_recovery_progress">Duke u rindezur në metodën e rikuperimit\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Duke u rindezur në metodën e Bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Duke u rindezur në metodën e shkarkimit\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Duke u ndezur në modalitetin fastboot\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare me valë</string>
+ <string name="quick_settings_powershare_off_powersave_label">Fikja e energjisë elektrike pa tel\nKursyesi i baterisë</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Ndarja e energjisë elektrike me valë\nBateria është shumë e ulët</string>
+ <string name="quick_settings_powershare_enabled_label">Eshtë aktivizuar PowerShare me valë</string>
+ <string name="quick_settings_caffeine_label">Kafeina</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafeina fikur.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafeina ndezur.</string>
+ <string name="status_bar_icons_title">Ikonat e shiritit të statusit</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sr/cm_strings.xml b/packages/SystemUI/res/values-sr/cm_strings.xml
new file mode 100644
index 0000000..4a8eebf
--- /dev/null
+++ b/packages/SystemUI/res/values-sr/cm_strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_system">Систем</string>
+ <string name="global_action_restart_recovery">Режим опоравка</string>
+ <string name="global_action_restart_bootloader">Покретач система</string>
+ <string name="global_action_restart_download">Преузимање</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Поново покрећем\u2026</string>
+ <string name="global_action_restart_recovery_progress">Поново покретање у режим за опоравак\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Поновно покретање система\u2026</string>
+ <string name="global_action_restart_download_progress">Поновно покретање у режим преузимања\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Поново покретање у режим fastbootd\u2026</string>
+ <string name="quick_settings_caffeine_label">Кофеин</string>
+ <string name="accessibility_quick_settings_caffeine_off">Кофеин је искључен.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Кофеин је укључен.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-su/cm_strings.xml b/packages/SystemUI/res/values-su/cm_strings.xml
new file mode 100644
index 0000000..a66f1a6
--- /dev/null
+++ b/packages/SystemUI/res/values-su/cm_strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_fastboot_progress">Ngahurungkeun deui ka mode fastbootd</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sv/cm_strings.xml b/packages/SystemUI/res/values-sv/cm_strings.xml
new file mode 100644
index 0000000..c9ed26e
--- /dev/null
+++ b/packages/SystemUI/res/values-sv/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Starta om\u2026</string>
+ <string name="global_action_restart_system">System</string>
+ <string name="global_action_restart_recovery">Återställning</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">Nedladdning</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Startar om\u2026</string>
+ <string name="global_action_restart_recovery_progress">Startar om i återställningsläge\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Startar om i bootloader-läge\u2026</string>
+ <string name="global_action_restart_download_progress">Startar om i nedladdningsläge\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Startar om i fastbootd-läge\u2026</string>
+ <string name="quick_settings_powershare_label">Trådlös PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Trådlös PowerShare av\nStrömsparläge</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Trådlös PowerShare avstängd\nLåg batterinivå</string>
+ <string name="quick_settings_powershare_enabled_label">Trådlös PowerShare är aktiverad</string>
+ <string name="quick_settings_caffeine_label">Koffein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Koffein av.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Koffein på.</string>
+ <string name="status_bar_icons_title">Ikoner för statusfältet</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ta/cm_strings.xml b/packages/SystemUI/res/values-ta/cm_strings.xml
new file mode 100644
index 0000000..8b81724
--- /dev/null
+++ b/packages/SystemUI/res/values-ta/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">கேஃபைன்</string>
+ <string name="accessibility_quick_settings_caffeine_off">கேஃபைன் ஆஃப்.</string>
+ <string name="accessibility_quick_settings_caffeine_on">கேஃபைன் ஆன்.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-te/cm_strings.xml b/packages/SystemUI/res/values-te/cm_strings.xml
new file mode 100644
index 0000000..c0b5d69
--- /dev/null
+++ b/packages/SystemUI/res/values-te/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">కాఫిన్</string>
+ <string name="accessibility_quick_settings_caffeine_off">కాఫిన్ ఆఫ్</string>
+ <string name="accessibility_quick_settings_caffeine_on">కాఫిన్ ఆన్</string>
+</resources>
diff --git a/packages/SystemUI/res/values-th/cm_strings.xml b/packages/SystemUI/res/values-th/cm_strings.xml
new file mode 100644
index 0000000..8075e1d
--- /dev/null
+++ b/packages/SystemUI/res/values-th/cm_strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">รีสตาร์ท\u2026</string>
+ <string name="global_action_restart_system">ระบบ</string>
+ <string name="global_action_restart_recovery">กู้คืนระบบ</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">ดาวน์โหลด</string>
+ <string name="global_action_restart_progress">กำลังเริ่มระบบใหม่\u2026</string>
+ <string name="global_action_restart_recovery_progress">กำลังเริ่มต้นใหม่ในโหมดการกู้คืน\u2026</string>
+ <string name="global_action_restart_bootloader_progress">กำลังเริ่มต้นใหม่ในโหมด bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">กำลังเริ่มต้นใหม่ในโหมด bootloader\u2026</string>
+ <string name="global_action_restart_fastboot_progress">กำลังเริ่มต้นใหม่ในโหมด fastbootd\u2026</string>
+ <string name="quick_settings_powershare_label">แบ่งปันพลังงานไร้สาย</string>
+ <string name="quick_settings_powershare_off_powersave_label">แบ่งปันพลังงานไร้สายถูกปิด\nประหยัดพลังงาน</string>
+ <string name="quick_settings_powershare_off_low_battery_label">แบ่งปันพลังงานไร้สายถูกปิด\nแบตเตอรี่ต่ำเกินไป</string>
+ <string name="quick_settings_powershare_enabled_label">แบ่งปันพลังงานไร้สายถูกเปิดใช้งาน</string>
+ <string name="quick_settings_caffeine_label">คาเฟอีน</string>
+ <string name="accessibility_quick_settings_caffeine_off">คาเฟอีนปิดอยู่</string>
+ <string name="accessibility_quick_settings_caffeine_on">คาเฟอีนเปิดอยู่</string>
+</resources>
diff --git a/packages/SystemUI/res/values-tr/cm_strings.xml b/packages/SystemUI/res/values-tr/cm_strings.xml
new file mode 100644
index 0000000..f4e38c8
--- /dev/null
+++ b/packages/SystemUI/res/values-tr/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Yeniden başlat\u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Kurtarma</string>
+ <string name="global_action_restart_bootloader">Ön yükleyici</string>
+ <string name="global_action_restart_download">İndirme</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Yeniden başlatılıyor\u2026</string>
+ <string name="global_action_restart_recovery_progress">Kurtarma modunda başlatılıyor\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Ön yükleyici modunda başlatılıyor\u2026</string>
+ <string name="global_action_restart_download_progress">İndirme modunda başlatılıyor\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Fastboot moduna yeniden başlatılıyor\u2026</string>
+ <string name="quick_settings_powershare_label">Kablosuz Güç Paylaşımı</string>
+ <string name="quick_settings_powershare_off_powersave_label">Kablosuz Güç Paylaşımı kapalı\nPil tasarrufu</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Kablosuz Güç Paylaşımı\nPil seviyesi çok düşük</string>
+ <string name="quick_settings_powershare_enabled_label">Kablosuz Güç Paylaşımı etkin</string>
+ <string name="quick_settings_caffeine_label">Kafein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Kafein kapalı.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Kafein açık.</string>
+ <string name="status_bar_icons_title">Durum çubuğu simgeleri</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ug/cm_strings.xml b/packages/SystemUI/res/values-ug/cm_strings.xml
new file mode 100644
index 0000000..b621e10
--- /dev/null
+++ b/packages/SystemUI/res/values-ug/cm_strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="quick_settings_caffeine_label">قەھۋەخانە ھالىتى</string>
+ <string name="accessibility_quick_settings_caffeine_off">قەھۋەخانە ھالىتىنى تاقاش.</string>
+ <string name="accessibility_quick_settings_caffeine_on">قەھۋەخانە ھالىتىنى ئېچىش.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-uk/cm_strings.xml b/packages/SystemUI/res/values-uk/cm_strings.xml
new file mode 100644
index 0000000..03481b6
--- /dev/null
+++ b/packages/SystemUI/res/values-uk/cm_strings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Перезапуск\u2026</string>
+ <string name="global_action_restart_system">Система</string>
+ <string name="global_action_restart_recovery">Відновлення</string>
+ <string name="global_action_restart_bootloader">Завантажувач</string>
+ <string name="global_action_restart_download">Режим оновлення</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Перезапуск\u2026</string>
+ <string name="global_action_restart_recovery_progress">Перезапуск до режиму відновлення\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Перезапуск до завантажувача\u2026</string>
+ <string name="global_action_restart_download_progress">Перезапуск у режим оновлення\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Перезапуск у режим fastbootd\u2026</string>
+ <string name="quick_settings_caffeine_label">Кофеїн</string>
+ <string name="accessibility_quick_settings_caffeine_off">Кофеїн вимкнено.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Кофеїн увімкнено.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-vi/cm_strings.xml b/packages/SystemUI/res/values-vi/cm_strings.xml
new file mode 100644
index 0000000..9bae962
--- /dev/null
+++ b/packages/SystemUI/res/values-vi/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">Khởi động lại\u2026</string>
+ <string name="global_action_restart_system">Hệ thống</string>
+ <string name="global_action_restart_recovery">Chế độ phục hồi</string>
+ <string name="global_action_restart_bootloader">Trình nạp khởi động</string>
+ <string name="global_action_restart_download">Tải xuống</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">Đang khởi động lại\u2026</string>
+ <string name="global_action_restart_recovery_progress">Đang khởi động lại vào chế độ phục hồi\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Đang khởi động lại vào chế độ trình nạp khởi động\u2026</string>
+ <string name="global_action_restart_download_progress">Đang khởi động lại vào chế độ tải về\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Đang khởi động lại vào chế độ fastboot\u2026</string>
+ <string name="quick_settings_powershare_label">PowerShare không dây</string>
+ <string name="quick_settings_powershare_off_powersave_label">Tắt PowerShare không dây\nTiết kiệm pin</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Tắt PowerShare không dây\nPin Yếu</string>
+ <string name="quick_settings_powershare_enabled_label">PowerShare không dây đã bật</string>
+ <string name="quick_settings_caffeine_label">Cafein</string>
+ <string name="accessibility_quick_settings_caffeine_off">Đã tắt Cafein.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Đã bật Cafein.</string>
+ <string name="status_bar_icons_title">Biểu tượng thanh trạng thái</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/cm_strings.xml b/packages/SystemUI/res/values-zh-rCN/cm_strings.xml
new file mode 100644
index 0000000..f0471ab
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rCN/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">重新启动\u2026</string>
+ <string name="global_action_restart_system">系统</string>
+ <string name="global_action_restart_recovery">恢复模式</string>
+ <string name="global_action_restart_bootloader">引导程序</string>
+ <string name="global_action_restart_download">下载</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">正在重启\u2026</string>
+ <string name="global_action_restart_recovery_progress">重新启动到恢复模式\u2026</string>
+ <string name="global_action_restart_bootloader_progress">重新启动到引导模式\u2026</string>
+ <string name="global_action_restart_download_progress">重新启动到下载模式\u2026</string>
+ <string name="global_action_restart_fastboot_progress">正在重启到 fastbootd 模式\u2026</string>
+ <string name="quick_settings_powershare_label">无线反充</string>
+ <string name="quick_settings_powershare_off_powersave_label">无线反充 关闭\n省电模式</string>
+ <string name="quick_settings_powershare_off_low_battery_label">无线反充 关闭\n电量过低</string>
+ <string name="quick_settings_powershare_enabled_label">无线反充已启用</string>
+ <string name="quick_settings_caffeine_label">保持亮屏</string>
+ <string name="accessibility_quick_settings_caffeine_off">保持亮屏关闭。</string>
+ <string name="accessibility_quick_settings_caffeine_on">保持亮屏开启。</string>
+ <string name="status_bar_icons_title">状态栏图标</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/cm_strings.xml b/packages/SystemUI/res/values-zh-rHK/cm_strings.xml
new file mode 100644
index 0000000..64ba385
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rHK/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/cm_strings.xml b/packages/SystemUI/res/values-zh-rTW/cm_strings.xml
new file mode 100644
index 0000000..bdf8e78
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rTW/cm_strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="global_action_restart_more">重新啟動\u2026</string>
+ <string name="global_action_restart_system">系統</string>
+ <string name="global_action_restart_recovery">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">下載</string>
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+ <string name="global_action_restart_progress">重新啟動中\u2026</string>
+ <string name="global_action_restart_recovery_progress">正在重新啟動至 Recovery 模式\u2026</string>
+ <string name="global_action_restart_bootloader_progress">正在重新啟動至 Bootloader 模式\u2026</string>
+ <string name="global_action_restart_download_progress">正在重新啟動至下載模式\u2026</string>
+ <string name="global_action_restart_fastboot_progress">正在重新啟動至 Fastbootd 模式\u2026</string>
+ <string name="quick_settings_powershare_label">無線反向充電</string>
+ <string name="quick_settings_powershare_off_powersave_label">無線反向充電關閉\n省電模式</string>
+ <string name="quick_settings_powershare_off_low_battery_label">無線反向充電關閉\n電量太低</string>
+ <string name="quick_settings_powershare_enabled_label">無線反向充電已開啟</string>
+ <string name="quick_settings_caffeine_label">暫停休眠</string>
+ <string name="accessibility_quick_settings_caffeine_off">暫停休眠關閉</string>
+ <string name="accessibility_quick_settings_caffeine_on">暫停休眠開啟</string>
+ <string name="status_bar_icons_title">狀態列圖示</string>
+</resources>
diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml
new file mode 100644
index 0000000..2ce037b
--- /dev/null
+++ b/packages/SystemUI/res/values/cm_strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ * Copyright (c) 2017-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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Advanced restart menu -->
+ <!-- Button to indicate more options -->
+ <string name="global_action_restart_more">Restart\u2026</string>
+ <!-- Button to restart the device, within the Restart Options dialog -->
+ <string name="global_action_restart_system">System</string>
+ <!-- Button to restart the device into recovery mode, within the Restart Options dialog -->
+ <string name="global_action_restart_recovery">Recovery</string>
+ <!-- Button to restart the device into bootloader mode, within the Restart Options dialog -->
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <!-- Button to restart the device into download mode, within the Restart Options dialog -->
+ <string name="global_action_restart_download">Download</string>
+ <!-- Button to restart the device into fastboot mode, within the Restart Options dialog -->
+ <string name="global_action_restart_fastboot">Fastbootd</string>
+
+ <!-- Restart progress dialog. This is shown if the user chooses to restart the device. -->
+ <string name="global_action_restart_progress">Restarting\u2026</string>
+ <!-- Restart to recovery mode progress dialog. This is shown if the user chooses to restart the device. -->
+ <string name="global_action_restart_recovery_progress">Restarting to recovery mode\u2026</string>
+ <!-- Restart to bootloader mode progress dialog. This is shown if the user chooses to restart the device. -->
+ <string name="global_action_restart_bootloader_progress">Restarting to bootloader mode\u2026</string>
+ <!-- Restart to download mode progress dialog. This is shown if the user chooses to restart the device. -->
+ <string name="global_action_restart_download_progress">Restarting to download mode\u2026</string>
+ <!-- Restart to fastboot mode progress dialog. This is shown if the user chooses to restart the device. -->
+ <string name="global_action_restart_fastboot_progress">Restarting to fastbootd mode\u2026</string>
+
+ <!-- PowerShare QS tile -->
+ <string name="quick_settings_powershare_label">Wireless PowerShare</string>
+ <string name="quick_settings_powershare_off_powersave_label">Wireless PowerShare off\nBattery saver</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Wireless PowerShare off\nBattery too low</string>
+ <string name="quick_settings_powershare_enabled_label">Wireless PowerShare is enabled</string>
+
+ <!-- Allow devices override audio panel location to the left side -->
+ <bool name="config_audioPanelOnLeftSide">false</bool>
+
+ <!-- Caffeine tile -->
+ <string name="quick_settings_caffeine_label">Caffeine</string>
+ <string name="accessibility_quick_settings_caffeine_off">Caffeine off</string>
+ <string name="accessibility_quick_settings_caffeine_on">Caffeine on</string>
+
+ <!-- Status bar -->
+ <string name="status_bar_icons_title">Status bar icons</string>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 307a619..e9ea948 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -223,7 +223,7 @@
<color name="screenrecord_status_color">#E94235</color>
<color name="screenrecord_icon_color">#D93025</color><!-- red 600 -->
- <color name="privacy_chip_background">#3ddc84</color>
+ <color name="privacy_chip_background">?android:attr/colorAccent</color>
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#CCFFFFFF</color> <!-- 80% -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e181d07..7efd7ce 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -101,7 +101,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue
+ internet,wifi,cell,bt,flashlight,dnd,alarm,airplane,nfc,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,powershare,caffeine,dataswitch,compass
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b7eff38..c2b2659 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -564,7 +564,7 @@
<dimen name="brightness_mirror_height">48dp</dimen>
- <dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding_horizontal">8dp</dimen>
<dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
@@ -603,7 +603,7 @@
<dimen name="volume_dialog_background_blur_radius">0dp</dimen>
- <dimen name="volume_tool_tip_right_margin">76dp</dimen>
+ <dimen name="volume_tool_tip_horizontal_margin">76dp</dimen>
<dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen>
@@ -1018,9 +1018,9 @@
<dimen name="global_actions_controls_y_translation">20dp</dimen>
<!-- Shutdown and restart actions are larger in power options dialog -->
- <dimen name="global_actions_power_dialog_item_height">190dp</dimen>
- <dimen name="global_actions_power_dialog_item_width">255dp</dimen>
- <dimen name="global_actions_power_dialog_item_bottom_margin">45dp</dimen>
+ <dimen name="global_actions_power_dialog_item_height">128dp</dimen>
+ <dimen name="global_actions_power_dialog_item_width">128dp</dimen>
+ <dimen name="global_actions_power_dialog_item_bottom_margin">22dp</dimen>
<!-- Power Menu Lite -->
<dimen name="global_actions_button_size">96dp</dimen>
@@ -1697,6 +1697,8 @@
<dimen name="internet_dialog_list_max_height">662dp</dimen>
<!-- The height of the WiFi network in Internet panel. -->
<dimen name="internet_dialog_wifi_network_height">72dp</dimen>
+ <!-- The height of the WiFi toggle layout in Internet panel -->
+ <dimen name="internet_dialog_wifi_toggle_height">56dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">@dimen/match_parent</dimen>
diff --git a/packages/SystemUI/res/values/leaf_arrays.xml b/packages/SystemUI/res/values/leaf_arrays.xml
new file mode 100644
index 0000000..714feb8
--- /dev/null
+++ b/packages/SystemUI/res/values/leaf_arrays.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 LeafOS Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Compass directions for the compass tile -->
+ <string-array name="cardinal_directions">
+ <item>@string/quick_settings_compass_N</item> <!-- North -->
+ <item>@string/quick_settings_compass_NE</item> <!-- North east -->
+ <item>@string/quick_settings_compass_E</item> <!-- East -->
+ <item>@string/quick_settings_compass_SE</item> <!-- South east -->
+ <item>@string/quick_settings_compass_S</item> <!-- South -->
+ <item>@string/quick_settings_compass_SW</item> <!-- South west -->
+ <item>@string/quick_settings_compass_W</item> <!-- West -->
+ <item>@string/quick_settings_compass_NW</item> <!-- North west -->
+ </string-array>
+</resources>
diff --git a/packages/SystemUI/res/values/leaf_config.xml b/packages/SystemUI/res/values/leaf_config.xml
new file mode 100644
index 0000000..4bba121
--- /dev/null
+++ b/packages/SystemUI/res/values/leaf_config.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- The maximum number of tiles in a row -->
+ <integer name="quick_qs_panel_max_tiles">4</integer>
+
+ <!-- The number of columns in the QuickQSPanel -->
+ <integer name="quick_qs_panel_num_columns">2</integer>
+ <integer name="quick_qs_panel_num_columns_landscape">4</integer>
+ <integer name="quick_qs_panel_num_columns_media">2</integer>
+
+ <!--The number of columns in the QSPanel -->
+ <integer name="qs_panel_num_columns">2</integer>
+ <integer name="qs_panel_num_columns_landscape">4</integer>
+ <integer name="qs_panel_num_columns_media">2</integer>
+
+ <!-- Color of the UDFPS pressed view -->
+ <color name="config_udfpsColor">#ffffffff</color>
+
+ <!-- Whether to enable framework dimming for UDFPS -->
+ <bool name="config_udfpsFrameworkDimming">false</bool>
+
+ <!-- Array of brightness-alpha LUT for framework dimming -->
+ <string-array name="config_udfpsDimmingBrightnessAlphaArray" translatable="false">
+ <!-- Example:
+ <item>0,255</item>
+ <item>1,234</item>
+ <item>3,227</item>
+ <item>8,208</item>
+ <item>16,192</item>
+ <item>27,176</item>
+ <item>41,160</item>
+ <item>61,144</item>
+ <item>80,128</item>
+ <item>104,112</item>
+ <item>130,96</item>
+ <item>158,80</item>
+ <item>188,64</item>
+ <item>221,48</item>
+ <item>250,36</item>
+ <item>255,33</item>
+ -->
+ </string-array>
+
+ <!-- Brightness range min for UDFPS dimming -->
+ <integer name="config_udfpsDimmingBrightnessMin">0</integer>
+
+ <!-- Brightness range max for UDFPS dimming -->
+ <integer name="config_udfpsDimmingBrightnessMax">0</integer>
+
+ <!-- The amount of delay to add when disabling the dimming.
+ This is used to prevent flickers due to the dimming being disabled
+ before the screen has had chance to switch out of HBM mode -->
+ <integer name="config_udfpsDimmingDisableDelay">0</integer>
+</resources>
diff --git a/packages/SystemUI/res/values/leaf_dimens.xml b/packages/SystemUI/res/values/leaf_dimens.xml
new file mode 100644
index 0000000..f4713ee
--- /dev/null
+++ b/packages/SystemUI/res/values/leaf_dimens.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The LeafOS Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <!-- QQS Brightness slider -->
+ <dimen name="qqs_top_brightness_margin_top">4dp</dimen>
+ <dimen name="qqs_top_brightness_margin_bottom">12dp</dimen>
+ <dimen name="qqs_bottom_brightness_margin_top">12dp</dimen>
+
+ <!-- QS Brightness slider -->
+ <dimen name="qs_top_brightness_margin_top">8dp</dimen>
+ <dimen name="qs_top_brightness_margin_bottom">10dp</dimen>
+ <dimen name="qs_bottom_brightness_margin_top">10dp</dimen>
+
+ <!-- Network traffic -->
+ <dimen name="network_traffic_width">25sp</dimen>
+
+ <!-- QS Paddings -->
+ <dimen name="custom_qs_panel_padding_top">0dp</dimen>
+ <dimen name="custom_notification_row_padding">16dp</dimen>
+ <dimen name="custom_qs_tile_label_line_height">20sp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/leaf_strings.xml b/packages/SystemUI/res/values/leaf_strings.xml
new file mode 100644
index 0000000..9e02790
--- /dev/null
+++ b/packages/SystemUI/res/values/leaf_strings.xml
@@ -0,0 +1,72 @@
+<!--
+ Copyright (C) 2022 The LeafOS Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Compass QS Tile -->
+ <string name="quick_settings_compass_label">Compass</string>
+ <string name="quick_settings_compass_value"><xliff:g id="degrees">%1$.0f</xliff:g>\u00b0 <xliff:g id="direction">%2$s</xliff:g></string>
+ <string name="quick_settings_compass_init">Initializing\u2026</string>
+ <string name="accessibility_quick_settings_compass_off">Compass off</string>
+ <string name="accessibility_quick_settings_compass_on">Compass on</string>
+ <string name="accessibility_quick_settings_compass_changed_off">Compass turned off</string>
+ <string name="accessibility_quick_settings_compass_changed_on">Compass turned on</string>
+ <string name="quick_settings_compass_N">N</string>
+ <string name="quick_settings_compass_NE">NE</string>
+ <string name="quick_settings_compass_E">E</string>
+ <string name="quick_settings_compass_SE">SE</string>
+ <string name="quick_settings_compass_S">S</string>
+ <string name="quick_settings_compass_SW">SW</string>
+ <string name="quick_settings_compass_W">W</string>
+ <string name="quick_settings_compass_NW">NW</string>
+
+ <!-- DataSwitch Tile -->
+ <string name="qs_data_switch_label">Switch data card</string>
+ <string name="qs_data_switch_toast_0">Currently no SIM card is inserted</string>
+ <string name="qs_data_switch_toast_1">Currently only one SIM card is inserted</string>
+ <string name="qs_data_switch_text_1">SIM 1</string>
+ <string name="qs_data_switch_text_2">SIM 2</string>
+ <string name="qs_data_switch_changed_1">Switched data card to SIM 1.</string>
+ <string name="qs_data_switch_changed_2">Switched data card to SIM 2.</string>
+
+ <!-- 4G icon -->
+ <string name="show_fourg_icon_title">4G icon</string>
+ <string name="show_fourg_icon_summary">Show 4G instead of LTE icon in statusbar</string>
+
+ <!-- QuickSettings: InternetDialog: Summary for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
+ <plurals name="quick_settings_internet_hotspot_summary_num_devices">
+ <item quantity="one">%d device connected</item>
+ <item quantity="other">%d devices connected</item>
+ </plurals>
+
+ <!-- Network traffic -->
+ <string name="network_traffic_title">Network traffic indicator</string>
+ <string name="network_traffic_summary">Enable or disable network speed indicator in status bar</string>
+
+ <!-- Data usage info in QS footer -->
+ <string name="usage_data">used today</string>
+ <string name="usage_data_default_suffix">Data</string>
+ <string name="usage_wifi_default_suffix">Wi-Fi</string>
+
+ <!-- Label for UI element which allows deleting the screenshot [CHAR LIMIT=30] -->
+ <string name="screenshot_delete_label">Delete</string>
+ <!-- Content description indicating that tapping the element will allow deleting the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_delete_description">Delete screenshot</string>
+
+ <!-- Name of the VPN status bar icon. -->
+ <string name="status_bar_vpn">VPN</string>
+
+ <!-- Name of the roaming status bar icon. -->
+ <string name="status_bar_roaming">Roaming indicator</string>
+
+ <!-- Name of the data saver status bar icon. -->
+ <string name="status_bar_data_saver">Data saver</string>
+</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ce08ca3..6cfd00f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -127,7 +127,7 @@
<style name="TextAppearance.QS.TileLabel">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
<item name="android:letterSpacing">0.01</item>
- <item name="android:lineHeight">20sp</item>
+ <item name="android:lineHeight">@dimen/custom_qs_tile_label_line_height</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
</style>
@@ -144,14 +144,14 @@
<style name="TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?attr/onSurface</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:letterSpacing">0.01</item>
</style>
<style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?attr/onSurface</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.QS.Status.Carriers" />
@@ -175,21 +175,21 @@
</style>
<style name="TextAppearance.AuthCredential.Title">
- <item name="android:fontFamily">google-sans</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">24sp</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
- <item name="android:fontFamily">google-sans</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
- <item name="android:fontFamily">google-sans</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
@@ -412,11 +412,11 @@
<item name="containerStyle">@style/AuthCredentialPinPasswordContainerStyle</item>
</style>
- <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
+ <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault.SystemUI">
<item name="isQsTheme">true</item>
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
- <item name="android:colorError">@*android:color/error_color_material_dark</item>
+ <item name="android:colorError">@*android:color/error_color_material_light</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
@@ -429,15 +429,17 @@
<item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
<item name="outline">?androidprv:attr/materialColorOutline</item>
- <item name="shadeActive">@color/material_dynamic_primary90</item>
- <item name="onShadeActive">@color/material_dynamic_primary10</item>
- <item name="onShadeActiveVariant">@color/material_dynamic_primary30</item>
- <item name="shadeInactive">@color/material_dynamic_neutral20</item>
- <item name="onShadeInactive">@color/material_dynamic_neutral90</item>
- <item name="onShadeInactiveVariant">@color/material_dynamic_neutral_variant80</item>
- <item name="shadeDisabled">@color/shade_disabled</item>
- <item name="underSurface">@color/material_dynamic_neutral0</item>
+ <item name="shadeActive">?android:attr/colorAccent</item>
+ <item name="onShadeActive">?android:attr/textColorPrimaryInverse</item>
+ <item name="onShadeActiveVariant">?android:attr/textColorSecondaryInverse</item>
+ <item name="shadeInactive">@*android:color/surface_light</item>
+ <item name="onShadeInactive">?android:attr/textColorPrimary</item>
+ <item name="onShadeInactiveVariant">?android:attr/textColorSecondary</item>
+ <item name="shadeDisabled">@*android:color/surface_light</item>
+ <item name="underSurface">@android:color/system_neutral1_100</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
+ <item name="android:colorPrimary">@*android:color/system_neutral1_900</item>
+ <item name="android:colorSecondary">@*android:color/system_neutral1_800</item>
</style>
<!-- Cannot double inherit. Use Theme.SystemUI.QuickSettings in code to match -->
@@ -704,7 +706,7 @@
<style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
<item name="android:textColor">?attr/onSurface</item>
- <item name="android:elevation">10dp</item>
+ <item name="android:elevation">0dp</item>
</style>
<!-- Media controls always have light background -->
diff --git a/packages/SystemUI/res/xml/status_bar_prefs.xml b/packages/SystemUI/res/xml/status_bar_prefs.xml
new file mode 100644
index 0000000..50a0933
--- /dev/null
+++ b/packages/SystemUI/res/xml/status_bar_prefs.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:key="status_bar"
+ android:title="@string/status_bar_icons_title">
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="rotate"
+ android:title="@string/status_bar_settings_auto_rotation"
+ android:icon="@drawable/ic_statusbar_auto_rotate" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="headset"
+ android:title="@string/headset"
+ android:icon="@drawable/ic_statusbar_headset" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="managed_profile"
+ android:title="@string/status_bar_work"
+ android:icon="@drawable/ic_statusbar_work" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="cast"
+ android:title="@string/quick_settings_cast_title"
+ android:icon="@drawable/ic_statusbar_cast" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="hotspot"
+ android:title="@string/quick_settings_hotspot_label"
+ android:icon="@drawable/ic_statusbar_hotspot" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="bluetooth"
+ android:title="@string/quick_settings_bluetooth_label"
+ android:icon="@*android:drawable/ic_qs_bluetooth" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="zen"
+ android:title="@string/quick_settings_dnd_label"
+ android:icon="@drawable/ic_statusbar_do_not_disturb" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="volume"
+ android:title="@*android:string/volume_unknown"
+ android:icon="@drawable/ic_statusbar_mute" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="wifi"
+ android:title="@string/quick_settings_wifi_label"
+ android:icon="@drawable/ic_statusbar_wifi" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="ethernet"
+ android:title="@string/status_bar_ethernet"
+ android:icon="@drawable/ic_statusbar_ethernet" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="mobile"
+ android:title="@string/quick_settings_cellular_detail_title"
+ android:icon="@drawable/ic_statusbar_mobile_network" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="airplane"
+ android:title="@string/status_bar_airplane"
+ android:icon="@drawable/ic_statusbar_airplane" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="roaming"
+ android:title="@string/status_bar_roaming"
+ android:icon="@drawable/ic_statusbar_roaming" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="vpn"
+ android:title="@string/status_bar_vpn"
+ android:icon="@drawable/ic_statusbar_vpn" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="data_saver"
+ android:title="@string/status_bar_data_saver"
+ android:icon="@drawable/ic_statusbar_data_saver" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="alarm_clock"
+ android:title="@string/status_bar_alarm"
+ android:icon="@drawable/ic_statusbar_alarm" />
+
+ <com.android.systemui.tuner.StatusBarSwitch
+ android:key="cameratoggle"
+ android:title="@string/quick_settings_camera_label"
+ android:icon="@drawable/ic_statusbar_camera" />
+
+ <SwitchPreference
+ android:key="show_fourg"
+ android:title="@string/show_fourg_icon_title"
+ android:summary="@string/show_fourg_icon_summary"
+ android:icon="@drawable/ic_statusbar_4g_mobiledata"
+ android:dependency="mobile"
+ android:defaultValue="false" />
+
+ <SwitchPreference
+ android:key="network_traffic_settings"
+ android:title="@string/network_traffic_title"
+ android:summary="@string/network_traffic_summary"
+ android:icon="@drawable/ic_statusbar_network_monitor"
+ android:defaultValue="false" />
+
+ <com.android.systemui.tuner.BatteryPreference
+ android:title="@string/battery"
+ android:icon="@*android:drawable/ic_battery"
+ android:summary="%s"
+ android:entries="@array/battery_options" />
+
+ <com.android.systemui.tuner.ClockPreference
+ android:title="@string/tuner_time"
+ android:icon="@drawable/ic_statusbar_clock"
+ android:summary="%s"
+ android:entries="@array/clock_options" />
+
+</PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 902de23..9bf102f 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -18,96 +18,10 @@
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:title="@string/system_ui_tuner">
- <PreferenceScreen
+ <Preference
android:key="status_bar"
- android:title="@string/status_bar" >
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="rotate"
- android:title="@string/status_bar_settings_auto_rotation" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="headset"
- android:title="@string/headset" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="managed_profile"
- android:title="@string/status_bar_work" />
-
- <!-- ime -->
- <!-- sync_failing -->
- <!-- sync_active -->
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="cast"
- android:title="@string/quick_settings_cast_title" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="hotspot"
- android:title="@string/quick_settings_hotspot_label" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="bluetooth"
- android:title="@string/quick_settings_bluetooth_label" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="cameratoggle"
- android:title="@string/quick_settings_camera_label" />
-
- <!-- nfc -->
- <!-- tty -->
- <!-- speakerphone -->
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="zen"
- android:title="@string/quick_settings_dnd_label" />
-
- <!-- mute -->
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="volume"
- android:title="@*android:string/volume_unknown" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="wifi"
- android:title="@string/quick_settings_wifi_label" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="ethernet"
- android:title="@string/status_bar_ethernet" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="mobile"
- android:title="@string/quick_settings_cellular_detail_title" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="airplane"
- android:title="@string/status_bar_airplane" />
-
- <!-- other weird signal stuff -->
-
- <com.android.systemui.tuner.BatteryPreference
- android:title="@string/battery"
- android:summary="%s"
- android:entries="@array/battery_options" />
-
- <com.android.systemui.tuner.StatusBarSwitch
- android:key="alarm_clock"
- android:title="@string/status_bar_alarm" />
-
- <!-- secure -->
-
- <com.android.systemui.tuner.ClockPreference
- android:title="@string/tuner_time"
- android:summary="%s"
- android:entries="@array/clock_options" />
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="low_priority"
- android:title="@string/tuner_low_priority"
- sysui:defValue="false" />
-
- </PreferenceScreen>
+ android:title="@string/status_bar"
+ android:fragment="com.android.systemui.tuner.StatusBarTuner" />
<PreferenceScreen
android:key="volume_and_do_not_disturb"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 4632914..d28d227 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -109,6 +109,9 @@
/** Sets home rotation enabled. */
oneway void setHomeRotationEnabled(boolean enabled) = 45;
+ /** Notifies when taskbar is enabled or disabled */
+ oneway void setTaskbarEnabled(boolean enabled) = 500;
+
/** Notifies when taskbar status updated */
oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 400f652..52a728e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -20,7 +20,6 @@
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
import android.animation.Animator;
@@ -637,7 +636,7 @@
}
// Only override user prefs when returning to the natural rotation (normally portrait).
// Don't let apps that force landscape or 180 alter user lock.
- return rotation == NATURAL_ROTATION;
+ return rotation == RotationPolicy.getNaturalRotation();
}
private void rescheduleRotationTimeout(final boolean reasonHover) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index bf8900d..4a08a87 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -104,7 +104,7 @@
}
@Override
- public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+ public void onPatternDetected(final List<LockPatternView.Cell> pattern, byte patternSize) {
mKeyguardUpdateMonitor.setCredentialAttempted();
mLockPatternView.disableInput();
if (mPendingLockCheck != null) {
@@ -127,7 +127,7 @@
mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
- LockscreenCredential.createPattern(pattern),
+ LockscreenCredential.createPattern(pattern, patternSize),
userId,
new LockPatternChecker.OnCheckCallback() {
@@ -222,10 +222,15 @@
@Override
protected void onViewAttached() {
super.onViewAttached();
+ int userId = KeyguardUpdateMonitor.getCurrentUser();
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
mSelectedUserInteractor.getSelectedUserId()));
+ mLockPatternView.setLockPatternUtils(mLockPatternUtils);
+ mLockPatternView.setLockPatternSize(mLockPatternUtils.getLockPatternSize(userId));
+ mLockPatternView.setVisibleDots(mLockPatternUtils.isVisibleDotsEnabled(userId));
+ mLockPatternView.setShowErrorPath(mLockPatternUtils.isShowErrorPath(userId));
mLockPatternView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mFalsingCollector.avoidGesture();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index f4cda02..62f9f3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -18,10 +18,15 @@
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.view.View;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -33,6 +38,11 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -51,6 +61,11 @@
private boolean mDisabledAutoConfirmation;
+ private boolean mScramblePin;
+
+ private List<Integer> mNumbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ private final List<Integer> mDefaultNumbers = List.of(mNumbers.toArray(new Integer[0]));
+
protected KeyguardPinViewController(KeyguardPINView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode, LockPatternUtils lockPatternUtils,
@@ -96,6 +111,38 @@
}
}
+ private void updatePinScrambling() {
+ boolean scramblePin = Settings.System.getIntForUser(getContext().getContentResolver(),
+ Settings.System.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, 0,
+ UserHandle.USER_CURRENT) == 1;
+
+ if (scramblePin || scramblePin != mScramblePin) {
+ mScramblePin = scramblePin;
+ if (scramblePin) {
+ Collections.shuffle(mNumbers);
+ } else {
+ mNumbers = new ArrayList<>(mDefaultNumbers);
+ }
+
+ // get all children who are NumPadKey's
+ ConstraintLayout container = (ConstraintLayout) mView.findViewById(R.id.pin_container);
+
+ List<NumPadKey> views = new ArrayList<NumPadKey>();
+ for (int i = 0; i < container.getChildCount(); i++) {
+ View view = container.getChildAt(i);
+ if (view.getClass() == NumPadKey.class) {
+ views.add((NumPadKey) view);
+ }
+ }
+
+ // reset the digits in the views
+ for (int i = 0; i < mNumbers.size(); i++) {
+ NumPadKey view = views.get(i);
+ view.setDigit(mNumbers.get(i));
+ }
+ }
+ }
+
protected void onUserInput() {
super.onUserInput();
if (isAutoPinConfirmEnabledInSettings()) {
@@ -116,6 +163,7 @@
@Override
public void startAppearAnimation() {
+ updatePinScrambling();
super.startAppearAnimation();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8c51a4e..c6350fb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2854,9 +2854,14 @@
&& !strongerAuthRequired
&& userDoesNotHaveTrust);
+ final boolean shouldListenFpsState = isUdfps
+ || mFingerprintInteractiveToAuthProvider == null
+ || !mFingerprintInteractiveToAuthProvider.isEnabled(user)
+ || (isDeviceInteractive() && !mGoingToSleep);
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState && !mBiometricPromptShowing;
+ && shouldListenBouncerState && shouldListenUdfpsState && !mBiometricPromptShowing
+ && shouldListenFpsState;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index dcfa775..1321945 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -24,6 +24,8 @@
import android.graphics.drawable.GradientDrawable;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
@@ -115,21 +117,7 @@
mDigitText.setText(Integer.toString(mDigit));
mKlondikeText = (TextView) findViewById(R.id.klondike_text);
- if (mDigit >= 0) {
- if (sKlondike == null) {
- sKlondike = getResources().getStringArray(R.array.lockscreen_num_pad_klondike);
- }
- if (sKlondike != null && sKlondike.length > mDigit) {
- String klondike = sKlondike[mDigit];
- final int len = klondike.length();
- if (len > 0) {
- mKlondikeText.setText(klondike);
- } else if (mKlondikeText.getVisibility() != View.GONE) {
- mKlondikeText.setVisibility(View.INVISIBLE);
- }
- }
- }
-
+ updateText();
setContentDescription(mDigitText.getText().toString());
Drawable background = getBackground();
@@ -141,6 +129,32 @@
}
}
+ public void setDigit(int digit) {
+ mDigit = digit;
+ updateText();
+ }
+
+ private void updateText() {
+ boolean scramblePin = Settings.System.getIntForUser(getContext().getContentResolver(),
+ Settings.System.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, 0,
+ UserHandle.USER_CURRENT) == 1;
+ if (mDigit >= 0) {
+ mDigitText.setText(Integer.toString(mDigit));
+ if (sKlondike == null) {
+ sKlondike = getResources().getStringArray(R.array.lockscreen_num_pad_klondike);
+ }
+ if (sKlondike != null && sKlondike.length > mDigit) {
+ String klondike = sKlondike[mDigit];
+ final int len = klondike.length();
+ if (len > 0 || scramblePin) {
+ mKlondikeText.setText(klondike);
+ } else if (mKlondikeText.getVisibility() != View.GONE) {
+ mKlondikeText.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
mOrientation = newConfig.orientation;
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 454ed27..8bcef37 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -84,6 +84,9 @@
*/
val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
+ /** The current pattern size. */
+ val patternSize: StateFlow<Byte>
+
/**
* The number of failed authentication attempts for the selected user since their last
* successful authentication.
@@ -198,6 +201,12 @@
override val hintedPinLength: Int = 6
+ override val patternSize: StateFlow<Byte> =
+ refreshingFlow(
+ initialValue = LockPatternUtils.PATTERN_SIZE_DEFAULT,
+ getFreshValue = lockPatternUtils::getLockPatternSize,
+ )
+
override val isPatternVisible: StateFlow<Boolean> =
refreshingFlow(
initialValue = true,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 8ca083f..c9f490f 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -123,6 +123,9 @@
initialValue = null,
)
+ /** The current pattern size. */
+ val patternSize: StateFlow<Byte> = repository.patternSize
+
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
@@ -318,7 +321,8 @@
LockscreenCredential.createPattern(
input
.map { it as AuthenticationPatternCoordinate }
- .map { LockPatternView.Cell.of(it.y, it.x) }
+ .map { LockPatternView.Cell.of(it.y, it.x, patternSize.value) },
+ patternSize.value
)
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 31698a3..55c30b0 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -152,12 +152,18 @@
updateShowPercent();
mDualToneHandler = new DualToneHandler(context);
// Init to not dark at all.
- onDarkChanged(new ArrayList<Rect>(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+ if (isNightMode()) {
+ onDarkChanged(new ArrayList<Rect>(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+ }
setClipChildren(false);
setClipToPadding(false);
}
+ private boolean isNightMode() {
+ return (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
private void setBatteryDrawableState(BatteryDrawableState newState) {
if (!newStatusBarIcons()) return;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9de71c1..955aa68 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -70,6 +70,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
@@ -490,6 +491,9 @@
case Utils.CREDENTIAL_PATTERN:
mCredentialView = factory.inflate(
R.layout.auth_credential_pattern_view, null, false);
+ LockPatternView lockPatternView = mCredentialView.findViewById(R.id.lockPattern);
+ lockPatternView.setLockPatternSize(
+ mLockPatternUtils.getLockPatternSize(mConfig.mUserId));
break;
case Utils.CREDENTIAL_PIN:
mCredentialView = factory.inflate(R.layout.auth_credential_pin_view, null, false);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4c2dc41..42090ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -29,6 +29,7 @@
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.app.animation.Interpolators
+import com.android.settingslib.Utils
import com.android.systemui.surfaceeffects.ripple.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
@@ -89,7 +90,8 @@
rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
updateRippleFadeParams()
ripplePaint.shader = rippleShader
- setLockScreenColor(0xffffffff.toInt()) // default color
+ setLockScreenColor(Utils.getColorAttr(context,
+ android.R.attr.colorAccent).defaultColor)
dwellShader.color = 0xffffffff.toInt() // default color
dwellShader.progress = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.kt
index 6e29fa5..95ec503 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.kt
@@ -36,4 +36,10 @@
* @return Vendor extension if needed for authentication.
*/
fun getVendorExtension(userId: Int): AuthenticateReason.Vendor?
+
+ /**
+ * @param userId the user Id.
+ * @return true if the InteractiveToAuthFeature is enabled, false if disabled.
+ */
+ fun isEnabled(userId: Int): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProviderImpl.kt
new file mode 100644
index 0000000..06fba6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProviderImpl.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 ArrowOS
+ *
+ * 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.systemui.biometrics
+
+import android.content.Context
+import android.database.ContentObserver
+import android.hardware.biometrics.common.AuthenticateReason
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.settings.SecureSettings
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import com.android.internal.R.bool.config_performantAuthDefault
+
+class FingerprintInteractiveToAuthProviderImpl @Inject constructor(
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val context: Context,
+ private val secureSettings: SecureSettings,
+ private val selectedUserInteractor: SelectedUserInteractor,
+) : FingerprintInteractiveToAuthProvider {
+ private val defaultValue = if (context.resources.getBoolean(config_performantAuthDefault)) {
+ 1
+ } else {
+ 0
+ }
+
+ override val enabledForCurrentUser =
+ selectedUserInteractor.selectedUser.flatMapLatest { currentUserId ->
+ conflatedCallbackFlow {
+ val callback = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(isEnabled(currentUserId))
+ }
+ }
+ secureSettings.registerContentObserver(
+ Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, true, callback
+ )
+ trySend(isEnabled(currentUserId))
+ awaitClose { secureSettings.unregisterContentObserver(callback) }
+ }
+ }
+ .flowOn(backgroundDispatcher)
+
+ override fun getVendorExtension(userId: Int): AuthenticateReason.Vendor? = null
+
+ override fun isEnabled(userId: Int): Boolean {
+ var value = Settings.Secure.getIntForUser(
+ context.contentResolver,
+ Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+ -1,
+ userId,
+ )
+ if (value == -1) {
+ value = defaultValue
+ Settings.Secure.putIntForUser(
+ context.contentResolver,
+ Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+ value,
+ userId,
+ )
+ }
+ return value == 0
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2c3ebe9..d881aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -48,6 +48,7 @@
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
+import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
@@ -221,6 +222,9 @@
private boolean mAttemptedToDismissKeyguard;
private final Set<Callback> mCallbacks = new HashSet<>();
+ private boolean mUseFrameworkDimming;
+ private int[][] mBrightnessAlphaArray;
+
@VisibleForTesting
public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES =
new VibrationAttributes.Builder()
@@ -761,6 +765,8 @@
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
+ initUdfpsFrameworkDimming();
+
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mBroadcastReceiver, filter,
@@ -983,6 +989,64 @@
return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
}
+ private void initUdfpsFrameworkDimming() {
+ mUseFrameworkDimming = mContext.getResources().getBoolean(
+ com.android.systemui.R.bool.config_udfpsFrameworkDimming);
+
+ if (mUseFrameworkDimming) {
+ String[] array = mContext.getResources().getStringArray(
+ com.android.systemui.R.array.config_udfpsDimmingBrightnessAlphaArray);
+ mBrightnessAlphaArray = new int[array.length][2];
+ for (int i = 0; i < array.length; i++) {
+ String[] s = array[i].split(",");
+ mBrightnessAlphaArray[i][0] = Integer.parseInt(s[0]);
+ mBrightnessAlphaArray[i][1] = Integer.parseInt(s[1]);
+ }
+ }
+ }
+
+ private static int interpolate(int x, int xa, int xb, int ya, int yb) {
+ return ya - (ya - yb) * (x - xa) / (xb - xa);
+ }
+
+ private int getBrightness() {
+ int brightness = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, 100);
+ // Since the brightness is taken from the system settings, we need to interpolate it
+ final int brightnessMin = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingBrightnessMin);
+ final int brightnessMax = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingBrightnessMax);
+ if (brightnessMax > 0) {
+ brightness = interpolate(brightness, 0, 255, brightnessMin, brightnessMax);
+ }
+ return brightness;
+ }
+
+ private void updateViewDimAmount() {
+ if (mOverlay == null || !mUseFrameworkDimming) {
+ return;
+ } else if (isFingerDown()) {
+ int curBrightness = getBrightness();
+ int i, dimAmount;
+ for (i = 0; i < mBrightnessAlphaArray.length; i++) {
+ if (mBrightnessAlphaArray[i][0] >= curBrightness) break;
+ }
+ if (i == 0) {
+ dimAmount = mBrightnessAlphaArray[i][1];
+ } else if (i == mBrightnessAlphaArray.length) {
+ dimAmount = mBrightnessAlphaArray[i-1][1];
+ } else {
+ dimAmount = interpolate(curBrightness,
+ mBrightnessAlphaArray[i][0], mBrightnessAlphaArray[i-1][0],
+ mBrightnessAlphaArray[i][1], mBrightnessAlphaArray[i-1][1]);
+ }
+ mOverlay.setDimAmount(dimAmount / 255.0f);
+ } else {
+ mOverlay.setDimAmount(0.0f);
+ }
+ }
+
public boolean isFingerDown() {
return mOnFingerDown;
}
@@ -1070,6 +1134,7 @@
for (Callback cb : mCallbacks) {
cb.onFingerDown();
}
+ updateViewDimAmount();
}
private void onFingerUp(long requestId, @NonNull View view) {
@@ -1112,6 +1177,23 @@
mOnFingerDown = false;
unconfigureDisplay(view);
cancelAodSendFingerUpAction();
+
+ // Add a delay to ensure that the dim amount is updated after the display has had chance
+ // to switch out of HBM mode. The delay, in ms is stored in config_udfpsDimmingDisableDelay.
+ // If the delay is 0, the dim amount will be updated immediately.
+ final int delay = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingDisableDelay);
+ if (delay > 0) {
+ mFgExecutor.executeDelayed(() -> {
+ // A race condition exists where the overlay is destroyed before the dim amount
+ // is updated. This check ensures that the overlay is still valid.
+ if (mOverlay != null && mOverlay.matchesRequestId(requestId)) {
+ updateViewDimAmount();
+ }
+ }, delay);
+ } else {
+ updateViewDimAmount();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 16865ca..d6ec519 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -158,6 +158,8 @@
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
+ private val frameworkDimming = context.getResources().getBoolean(
+ R.bool.config_udfpsFrameworkDimming)
private val coreLayoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
0 /* flags set in computeLayoutParams() */,
@@ -169,12 +171,23 @@
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+ if (frameworkDimming) {
+ flags = flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ }
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ dimAmount = 0.0f
// Avoid announcing window title.
accessibilityTitle = " "
inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
}
+ var dimAmount
+ get() = coreLayoutParams.dimAmount
+ set(value) {
+ coreLayoutParams.dimAmount = value
+ windowManager.updateViewLayout(getTouchOverlay(), coreLayoutParams)
+ }
+
/** If the overlay is currently showing. */
val isShowing: Boolean
get() = getTouchOverlay() != null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
new file mode 100644
index 0000000..2488132
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -0,0 +1,159 @@
+/*
+ * 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.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.android.systemui.R;
+
+/**
+ * Surface View for providing the Global High-Brightness Mode (GHBM) illumination for UDFPS.
+ */
+public class UdfpsSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private static final String TAG = "UdfpsSurfaceView";
+
+ /**
+ * Notifies {@link UdfpsView} when to enable GHBM illumination.
+ */
+ interface GhbmIlluminationListener {
+ /**
+ * @param surface the surface for which GHBM should be enabled.
+ * @param onDisplayConfigured a runnable that should be run after GHBM is enabled.
+ */
+ void enableGhbm(@NonNull Surface surface, @Nullable Runnable onDisplayConfigured);
+ }
+
+ @NonNull private final SurfaceHolder mHolder;
+ @NonNull private final Paint mSensorPaint;
+
+ @Nullable private GhbmIlluminationListener mGhbmIlluminationListener;
+ @Nullable private Runnable mOnDisplayConfigured;
+ boolean mAwaitingSurfaceToStartIllumination;
+ boolean mHasValidSurface;
+
+ private Drawable mUdfpsIconPressed;
+
+ public UdfpsSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Make this SurfaceView draw on top of everything else in this window. This allows us to
+ // 1) Always show the HBM circle on top of everything else, and
+ // 2) Properly composite this view with any other animations in the same window no matter
+ // what contents are added in which order to this view hierarchy.
+ setZOrderOnTop(true);
+
+ mHolder = getHolder();
+ mHolder.addCallback(this);
+ mHolder.setFormat(PixelFormat.RGBA_8888);
+
+ mSensorPaint = new Paint(0 /* flags */);
+ mSensorPaint.setAntiAlias(true);
+ mSensorPaint.setColor(context.getColor(R.color.config_udfpsColor));
+ mSensorPaint.setStyle(Paint.Style.FILL);
+
+ mUdfpsIconPressed = context.getDrawable(R.drawable.udfps_icon_pressed);
+ }
+
+ @Override public void surfaceCreated(SurfaceHolder holder) {
+ mHasValidSurface = true;
+ if (mAwaitingSurfaceToStartIllumination) {
+ doIlluminate(mOnDisplayConfigured);
+ mOnDisplayConfigured = null;
+ mAwaitingSurfaceToStartIllumination = false;
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // Unused.
+ }
+
+ @Override public void surfaceDestroyed(SurfaceHolder holder) {
+ mHasValidSurface = false;
+ }
+
+ void setGhbmIlluminationListener(@Nullable GhbmIlluminationListener listener) {
+ mGhbmIlluminationListener = listener;
+ }
+
+ /**
+ * Note: there is no corresponding method to stop GHBM illumination. It is expected that
+ * {@link UdfpsView} will hide this view, which would destroy the surface and remove the
+ * illumination dot.
+ */
+ void startGhbmIllumination(@Nullable Runnable onDisplayConfigured) {
+ if (mGhbmIlluminationListener == null) {
+ Log.e(TAG, "startIllumination | mGhbmIlluminationListener is null");
+ return;
+ }
+
+ if (mHasValidSurface) {
+ doIlluminate(onDisplayConfigured);
+ } else {
+ mAwaitingSurfaceToStartIllumination = true;
+ mOnDisplayConfigured = onDisplayConfigured;
+ }
+ }
+
+ private void doIlluminate(@Nullable Runnable onDisplayConfigured) {
+ if (mGhbmIlluminationListener == null) {
+ Log.e(TAG, "doIlluminate | mGhbmIlluminationListener is null");
+ return;
+ }
+
+ mGhbmIlluminationListener.enableGhbm(mHolder.getSurface(), onDisplayConfigured);
+ }
+
+ /**
+ * Immediately draws the illumination dot on this SurfaceView's surface.
+ */
+ void drawIlluminationDot(@NonNull RectF sensorRect) {
+ if (!mHasValidSurface) {
+ Log.e(TAG, "drawIlluminationDot | the surface is destroyed or was never created.");
+ return;
+ }
+ Canvas canvas = null;
+ try {
+ canvas = mHolder.lockCanvas();
+ mUdfpsIconPressed.setBounds(
+ Math.round(sensorRect.left),
+ Math.round(sensorRect.top),
+ Math.round(sensorRect.right),
+ Math.round(sensorRect.bottom)
+ );
+ mUdfpsIconPressed.draw(canvas);
+ canvas.drawOval(sensorRect, mSensorPaint);
+ } finally {
+ // Make sure the surface is never left in a bad state.
+ if (canvas != null) {
+ mHolder.unlockCanvasAndPost(canvas);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 76bcd6e..a8e4e95 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -24,9 +24,11 @@
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
+import android.view.Surface
import android.widget.FrameLayout
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.doze.DozeReceiver
+import com.android.systemui.res.R
private const val TAG = "UdfpsView"
@@ -47,6 +49,8 @@
textSize = 32f
}
+ private var ghbmView: UdfpsSurfaceView? = null
+
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null
@@ -73,6 +77,10 @@
return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
}
+ override fun onFinishInflate() {
+ ghbmView = findViewById(R.id.hbm_view)
+ }
+
override fun dozeTimeTick() {
animationViewController?.dozeTimeTick()
}
@@ -106,12 +114,34 @@
fun configureDisplay(onDisplayConfigured: Runnable) {
isDisplayConfigured = true
animationViewController?.onDisplayConfiguring()
- mUdfpsDisplayMode?.enable(onDisplayConfigured)
+ val gView = ghbmView
+ if (gView != null) {
+ gView.setGhbmIlluminationListener(this::doIlluminate)
+ gView.visibility = VISIBLE
+ gView.startGhbmIllumination(onDisplayConfigured)
+ } else {
+ doIlluminate(null /* surface */, onDisplayConfigured)
+ }
+ }
+
+ private fun doIlluminate(surface: Surface?, onDisplayConfigured: Runnable?) {
+ if (ghbmView != null && surface == null) {
+ Log.e(TAG, "doIlluminate | surface must be non-null for GHBM")
+ }
+
+ mUdfpsDisplayMode?.enable {
+ onDisplayConfigured?.run()
+ ghbmView?.drawIlluminationDot(RectF(sensorRect))
+ }
}
fun unconfigureDisplay() {
isDisplayConfigured = false
animationViewController?.onDisplayUnconfigured()
+ ghbmView?.let { view ->
+ view.setGhbmIlluminationListener(null)
+ view.visibility = INVISIBLE
+ }
mUdfpsDisplayMode?.disable(null /* onDisabled */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 307b985..152cda4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -16,11 +16,15 @@
package com.android.systemui.biometrics.dagger
+import android.content.Context
+
import android.content.res.Resources
import com.android.internal.R
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProviderImpl
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
import com.android.systemui.biometrics.data.repository.BiometricStatusRepositoryImpl
@@ -39,8 +43,11 @@
import com.android.systemui.biometrics.udfps.OverlapDetector
import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.ThreadFactory
+import com.android.systemui.util.settings.SecureSettings
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -49,6 +56,7 @@
import dagger.multibindings.IntoSet
import java.util.concurrent.Executor
import javax.inject.Qualifier
+import kotlinx.coroutines.CoroutineDispatcher
/** Dagger module for all things biometric. */
@Module
@@ -127,6 +135,20 @@
BoundingBoxOverlapDetector(values[2])
}
}
+
+ @Provides
+ fun providesFingerprintInteractiveToAuth(
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ context: Context,
+ secureSettings: SecureSettings,
+ selectedUserInteractor: SelectedUserInteractor,
+ ): FingerprintInteractiveToAuthProvider =
+ FingerprintInteractiveToAuthProviderImpl(
+ backgroundDispatcher,
+ context,
+ secureSettings,
+ selectedUserInteractor,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index 94cea57..329f464 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.hardware.biometrics.PromptInfo
+import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.systemui.biometrics.Utils
@@ -174,6 +175,7 @@
request: BiometricPromptRequest.Credential,
text: CharSequence? = null,
pattern: List<LockPatternView.Cell>? = null,
+ patternSize: Byte = LockPatternUtils.PATTERN_SIZE_DEFAULT
): CredentialStatus =
withContext(bgDispatcher) {
val credential =
@@ -183,7 +185,7 @@
is BiometricPromptRequest.Credential.Password ->
LockscreenCredential.createPasswordOrNone(text ?: "")
is BiometricPromptRequest.Credential.Pattern ->
- LockscreenCredential.createPattern(pattern ?: listOf())
+ LockscreenCredential.createPattern(pattern ?: listOf(), patternSize)
}
credential.use { c -> verifyCredential(request, c) }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
index eff6987..b207f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
@@ -28,14 +28,14 @@
launch {
viewModel.header.collect { header ->
lockPatternView.setOnPatternListener(
- OnPatternDetectedListener { pattern ->
+ OnPatternDetectedListener { pattern, patternSize ->
if (pattern.isPatternTooShort()) {
// Pattern size is less than the minimum
// do not count it as a failed attempt
viewModel.showPatternTooShortError()
} else {
lockPatternView.isEnabled = false
- launch { viewModel.checkCredential(pattern, header) }
+ launch { viewModel.checkCredential(pattern, patternSize, header) }
}
}
)
@@ -60,13 +60,13 @@
}
private class OnPatternDetectedListener(
- private val onDetected: (pattern: List<LockPatternView.Cell>) -> Unit
+ private val onDetected: (pattern: List<LockPatternView.Cell>, patternSize: Byte) -> Unit
) : LockPatternView.OnPatternListener {
override fun onPatternCellAdded(pattern: List<LockPatternView.Cell>) {}
override fun onPatternCleared() {}
override fun onPatternStart() {}
- override fun onPatternDetected(pattern: List<LockPatternView.Cell>) {
- onDetected(pattern)
+ override fun onPatternDetected(pattern: List<LockPatternView.Cell>, patternSize: Byte) {
+ onDetected(pattern, patternSize)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 46be8c7..34efef4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -118,8 +118,9 @@
/** Check a pattern and update [validatedAttestation] or [remainingAttempts]. */
suspend fun checkCredential(
pattern: List<LockPatternView.Cell>,
+ patternSize: Byte,
header: CredentialHeaderViewModel
- ) = checkCredential(credentialInteractor.checkCredential(header.asRequest(), pattern = pattern))
+ ) = checkCredential(credentialInteractor.checkCredential(header.asRequest(), pattern = pattern, patternSize = patternSize))
private suspend fun checkCredential(result: CredentialStatus) {
when (result) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index d8be1af..c112a0c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -66,6 +66,9 @@
/** The length of the hinted PIN, or `null`, if pin length hint should not be shown. */
val hintedPinLength: StateFlow<Int?> = authenticationInteractor.hintedPinLength
+ /** The current pattern size. */
+ val patternSize: StateFlow<Byte> = authenticationInteractor.patternSize
+
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 69f8032..32c6b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -48,10 +48,11 @@
) {
/** The number of columns in the dot grid. */
- val columnCount = 3
-
+ val columnCount: Byte
+ get() = interactor.patternSize.value
/** The number of rows in the dot grid. */
- val rowCount = 3
+ val rowCount: Byte
+ get() = interactor.patternSize.value
private val _selectedDots = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 19af371..f619424 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -66,6 +66,7 @@
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
+import com.android.systemui.leaf.LeafModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -209,6 +210,7 @@
KeyboardModule.class,
KeyguardBlueprintModule.class,
KeyguardSectionsModule.class,
+ LeafModule.class,
LetterboxModule.class,
LogModule.class,
MediaProjectionModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 3194942..afb3570 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -248,7 +248,7 @@
false /* ignoresSetting */,
dozeParameters.longPressUsesProx(),
false /* immediatelyReRegister */,
- true /* requiresAod */
+ !mScreenOffUdfpsEnabled /* requiresAod */
),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6bb84649..350a117 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -316,7 +316,7 @@
val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
// TODO(b/254512502): Tracking Bug
- val MEDIA_SESSION_ACTIONS = unreleasedFlag("media_session_actions")
+ val MEDIA_SESSION_ACTIONS = releasedFlag("media_session_actions")
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 81a5206..5aa7ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -59,7 +59,8 @@
private final LeakDetector mLeakDetector;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_ASSETS_PATHS);
+ | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS
+ | ActivityInfo.CONFIG_UI_MODE);
private final FragmentService mManager;
private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 9f321d8..ed6ab98 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -78,8 +78,8 @@
}
@Override
- public void handleShowShutdownUi(boolean isReboot, String reason) {
- mExtension.get().showShutdownUi(isReboot, reason);
+ public void handleShowShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
+ mExtension.get().showShutdownUi(isReboot, reason, rebootCustom);
}
@Override
@@ -114,9 +114,9 @@
}
@Override
- public void reboot(boolean safeMode) {
+ public void reboot(boolean safeMode, String reason) {
try {
- mBarService.reboot(safeMode);
+ mBarService.reboot(safeMode, reason);
} catch (RemoteException e) {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 46206b7..10b489bd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -34,6 +33,7 @@
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.StatusBarManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
@@ -58,6 +58,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
@@ -187,6 +188,14 @@
static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+ /* Valid settings for restart actions keys.
+ * see lineage-sdk config.xml config_restartActionsList */
+ private static final String RESTART_ACTION_KEY_RESTART = "restart";
+ private static final String RESTART_ACTION_KEY_RESTART_RECOVERY = "restart_recovery";
+ private static final String RESTART_ACTION_KEY_RESTART_BOOTLOADER = "restart_bootloader";
+ private static final String RESTART_ACTION_KEY_RESTART_DOWNLOAD = "restart_download";
+ private static final String RESTART_ACTION_KEY_RESTART_FASTBOOT = "restart_fastboot";
+
// See NotificationManagerService#scheduleDurationReachedLocked
private static final long TOAST_FADE_TIME = 333;
// See NotificationManagerService.LONG_DELAY
@@ -223,6 +232,8 @@
protected final ArrayList<Action> mOverflowItems = new ArrayList<>();
@VisibleForTesting
protected final ArrayList<Action> mPowerItems = new ArrayList<>();
+ @VisibleForTesting
+ protected final ArrayList<Action> mRestartItems = new ArrayList<>();
@VisibleForTesting
protected ActionsDialogLite mDialog;
@@ -233,6 +244,7 @@
protected MyAdapter mAdapter;
protected MyOverflowAdapter mOverflowAdapter;
protected MyPowerOptionsAdapter mPowerAdapter;
+ protected MyRestartOptionsAdapter mRestartAdapter;
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
@@ -549,6 +561,16 @@
return action.shouldShow();
}
+ private boolean shouldShowRestartSubmenu() {
+ KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ boolean keyguardLocked = km.inKeyguardRestrictedInputMode() && km.isKeyguardSecure();
+ boolean advancedRestartEnabled = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.POWERMENU_ADVANCED, 0) == 1;
+ boolean isPrimaryUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
+
+ return advancedRestartEnabled && !keyguardLocked && isPrimaryUser;
+ }
+
/**
* Returns the maximum number of power menu items to show based on which GlobalActions
* layout is being used.
@@ -583,6 +605,25 @@
}
@VisibleForTesting
+ protected String[] getRestartActions() {
+ List<String> actions = new ArrayList<>();
+
+ actions.add("restart");
+ actions.add("restart_recovery");
+ if (SystemProperties.getBoolean("ro.boot.dynamic_partitions", false) ||
+ SystemProperties.getBoolean("ro.fastbootd.available", false)) {
+ actions.add("restart_fastboot");
+ }
+ if ("samsung".equals(SystemProperties.get("ro.product.vendor.manufacturer", "google"))) {
+ actions.add("restart_download");
+ } else {
+ actions.add("restart_bootloader");
+ }
+
+ return actions.toArray(String[]::new);
+ }
+
+ @VisibleForTesting
protected void createActionItems() {
// Simple toggle style if there's no vibrator, otherwise use a tri-state
if (!mHasVibrator) {
@@ -596,11 +637,19 @@
mItems.clear();
mOverflowItems.clear();
mPowerItems.clear();
+ mRestartItems.clear();
String[] defaultActions = getDefaultActions();
+ String[] restartActions = getRestartActions();
ShutDownAction shutdownAction = new ShutDownAction();
RestartAction restartAction = new RestartAction();
+ RestartSystemAction sysAction = new RestartSystemAction();
+ RestartRecoveryAction recAction = new RestartRecoveryAction();
+ RestartBootloaderAction blAction = new RestartBootloaderAction();
+ RestartDownloadAction dlAction = new RestartDownloadAction();
+ RestartFastbootAction fbAction = new RestartFastbootAction();
ArraySet<String> addedKeys = new ArraySet<>();
+ ArraySet<String> addedRestartKeys = new ArraySet<String>();
List<Action> tempActions = new ArrayList<>();
CurrentUserProvider currentUser = new CurrentUserProvider();
@@ -666,6 +715,27 @@
addedKeys.add(actionKey);
}
+ for (int i = 0; i < restartActions.length; i++) {
+ String actionKey = restartActions[i];
+ if (addedRestartKeys.contains(actionKey)) {
+ // If we already have added this, don't add it again.
+ continue;
+ }
+ if (RESTART_ACTION_KEY_RESTART.equals(actionKey)) {
+ addIfShouldShowAction(mRestartItems, sysAction);
+ } else if (RESTART_ACTION_KEY_RESTART_RECOVERY.equals(actionKey)) {
+ addIfShouldShowAction(mRestartItems, recAction);
+ } else if (RESTART_ACTION_KEY_RESTART_BOOTLOADER.equals(actionKey)) {
+ addIfShouldShowAction(mRestartItems, blAction);
+ } else if (RESTART_ACTION_KEY_RESTART_DOWNLOAD.equals(actionKey)) {
+ addIfShouldShowAction(mRestartItems, dlAction);
+ } else if (RESTART_ACTION_KEY_RESTART_FASTBOOT.equals(actionKey)) {
+ addIfShouldShowAction(mRestartItems, fbAction);
+ }
+ // Add here so we don't add more than one.
+ addedRestartKeys.add(actionKey);
+ }
+
// replace power and restart with a single power options action, if needed
if (tempActions.contains(shutdownAction) && tempActions.contains(restartAction)
&& tempActions.size() > getMaxShownPowerItems()) {
@@ -695,6 +765,7 @@
mAdapter = new MyAdapter();
mOverflowAdapter = new MyOverflowAdapter();
mPowerAdapter = new MyPowerOptionsAdapter();
+ mRestartAdapter = new MyRestartOptionsAdapter();
}
/**
@@ -719,6 +790,7 @@
this::onRefresh,
mKeyguardShowing,
mPowerAdapter,
+ mRestartAdapter,
mUiEventLogger,
mShadeController,
mKeyguardUpdateMonitor,
@@ -764,6 +836,15 @@
}
@Override
+ public void onUiModeChanged() {
+ // Colors may change, depending on UI mode
+ mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.refreshDialog();
+ }
+ }
+
+ @Override
public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing()
&& (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp
@@ -824,7 +905,7 @@
}
mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
- mWindowManagerFuncs.reboot(true);
+ mWindowManagerFuncs.reboot(true, null);
return true;
}
return false;
@@ -953,7 +1034,9 @@
@VisibleForTesting
final class RestartAction extends SinglePressAction implements LongPressAction {
RestartAction() {
- super(R.drawable.ic_restart, R.string.global_action_restart);
+ super(R.drawable.ic_restart, shouldShowRestartSubmenu()
+ ? com.android.systemui.R.string.global_action_restart_more
+ : R.string.global_action_restart);
}
@Override
@@ -965,7 +1048,7 @@
}
mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
- mWindowManagerFuncs.reboot(true);
+ mWindowManagerFuncs.reboot(true, null);
return true;
}
return false;
@@ -989,7 +1072,130 @@
return;
}
mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
- mWindowManagerFuncs.reboot(false);
+ if (mDialog != null && shouldShowRestartSubmenu()) {
+ mDialog.showRestartOptionsMenu();
+ } else {
+ mWindowManagerFuncs.reboot(false, null);
+ }
+ }
+ }
+
+ private final class RestartSystemAction extends SinglePressAction implements LongPressAction {
+ public RestartSystemAction() {
+ super(R.drawable.ic_restart,
+ com.android.systemui.R.string.global_action_restart_system);
+ }
+
+ @Override
+ public boolean onLongPress() {
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
+ mWindowManagerFuncs.reboot(true, null);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false, null);
+ }
+ }
+
+ private final class RestartRecoveryAction extends SinglePressAction {
+ private RestartRecoveryAction() {
+ super(com.android.systemui.R.drawable.ic_lock_restart_recovery,
+ com.android.systemui.R.string.global_action_restart_recovery);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_RECOVERY);
+ }
+ }
+
+ private final class RestartBootloaderAction extends SinglePressAction {
+ private RestartBootloaderAction() {
+ super(com.android.systemui.R.drawable.ic_lock_restart_bootloader,
+ com.android.systemui.R.string.global_action_restart_bootloader);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_BOOTLOADER);
+ }
+ }
+
+ private final class RestartFastbootAction extends SinglePressAction {
+ private RestartFastbootAction() {
+ super(com.android.systemui.R.drawable.ic_lock_restart_fastboot,
+ com.android.systemui.R.string.global_action_restart_fastboot);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_FASTBOOT);
+ }
+ }
+
+ private final class RestartDownloadAction extends SinglePressAction {
+ private RestartDownloadAction() {
+ super(com.android.systemui.R.drawable.ic_lock_restart_bootloader,
+ com.android.systemui.R.string.global_action_restart_download);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_DOWNLOAD);
}
}
@@ -1027,14 +1233,7 @@
@Override
public boolean shouldShow() {
- // Include screenshot in power menu for legacy nav because it is not accessible
- // through Recents in that mode
- return is2ButtonNavigationEnabled();
- }
-
- boolean is2ButtonNavigationEnabled() {
- return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
- com.android.internal.R.integer.config_navBarInteractionMode);
+ return false;
}
}
@@ -1455,8 +1654,9 @@
Action item = mAdapter.getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
- // don't dismiss the dialog if we're opening the power options menu
- if (!(item instanceof PowerOptionsAction)) {
+ // don't dismiss the dialog if we're opening the power/restart options menu
+ if (!(item instanceof PowerOptionsAction ||
+ (item instanceof RestartAction && shouldShowRestartSubmenu()))) {
// Usually clicking an item shuts down the phone, locks, or starts an
// activity. We don't want to animate back into the power button when that
// happens, so we disable the dialog animation before dismissing.
@@ -1502,7 +1702,7 @@
Log.w(TAG, "No power options action found at position: " + position);
return null;
}
- int viewLayoutResource = com.android.systemui.res.R.layout.global_actions_power_item;
+ int viewLayoutResource = com.android.systemui.res.R.layout.global_actions_grid_item_lite;
View view = convertView != null ? convertView
: LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
view.setOnClickListener(v -> onClickItem(position));
@@ -1544,7 +1744,8 @@
private void onClickItem(int position) {
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
- if (mDialog != null) {
+ if (mDialog != null &&
+ !(item instanceof RestartAction && shouldShowRestartSubmenu())) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
@@ -1558,6 +1759,18 @@
}
}
+ public class MyRestartOptionsAdapter extends MyPowerOptionsAdapter {
+ @Override
+ public int getCount() {
+ return mRestartItems.size();
+ }
+
+ @Override
+ public Action getItem(int position) {
+ return mRestartItems.get(position);
+ }
+ }
+
/**
* The adapter used for items in the power options menu, triggered by the PowerOptionsAction.
*/
@@ -2143,6 +2356,7 @@
mAdapter.notifyDataSetChanged();
mOverflowAdapter.notifyDataSetChanged();
mPowerAdapter.notifyDataSetChanged();
+ mRestartAdapter.notifyDataSetChanged();
}
};
@@ -2225,6 +2439,7 @@
protected final MyAdapter mAdapter;
protected final MyOverflowAdapter mOverflowAdapter;
protected final MyPowerOptionsAdapter mPowerOptionsAdapter;
+ private final MyRestartOptionsAdapter mRestartOptionsAdapter;
protected final IStatusBarService mStatusBarService;
protected final IBinder mToken = new Binder();
protected Drawable mBackgroundDrawable;
@@ -2237,6 +2452,7 @@
private final StatusBarWindowController mStatusBarWindowController;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
+ private Dialog mRestartOptionsDialog;
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
@@ -2318,6 +2534,7 @@
Runnable onRefreshCallback,
boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter,
+ MyRestartOptionsAdapter restartAdapter,
UiEventLogger uiEventLogger,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -2330,6 +2547,7 @@
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
mPowerOptionsAdapter = powerAdapter;
+ mRestartOptionsAdapter = restartAdapter;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mLightBarController = lightBarController;
@@ -2420,6 +2638,12 @@
mPowerOptionsDialog.show();
}
+ public void showRestartOptionsMenu() {
+ mRestartOptionsDialog = GlobalActionsPowerDialog.create(mContext,
+ mRestartOptionsAdapter);
+ mRestartOptionsDialog.show();
+ }
+
protected void showPowerOverflowMenu() {
mOverflowPopup = createPowerOverflowPopup();
mOverflowPopup.show();
@@ -2675,6 +2899,7 @@
public void dismiss() {
dismissOverflow();
dismissPowerOptions();
+ dismissRestartOptions();
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
super.dismiss();
@@ -2692,6 +2917,12 @@
}
}
+ private void dismissRestartOptions() {
+ if (mRestartOptionsDialog != null) {
+ mRestartOptionsDialog.dismiss();
+ }
+ }
+
protected final void setRotationSuggestionsEnabled(boolean enabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -2729,6 +2960,7 @@
// Dismiss the dropdown menus.
dismissOverflow();
dismissPowerOptions();
+ dismissRestartOptions();
// Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c5027cc..e565ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -67,9 +67,10 @@
}
@Override
- public void showShutdownUi(boolean isReboot, String reason) {
- mShutdownUi.showShutdownUi(isReboot, reason);
+ public void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
+ mShutdownUi.showShutdownUi(isReboot, reason, rebootCustom);
}
+
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
index b8bf142..142b9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
@@ -26,6 +26,8 @@
import android.view.WindowManager;
import android.widget.ListAdapter;
+import androidx.constraintlayout.helper.widget.Flow;
+
/**
* Creates a customized Dialog for displaying the Shut Down and Restart actions.
*/
@@ -36,11 +38,15 @@
*/
public static Dialog create(@NonNull Context context, ListAdapter adapter) {
ViewGroup listView = (ViewGroup) LayoutInflater.from(context).inflate(
- com.android.systemui.res.R.layout.global_actions_power_dialog, null);
+ com.android.systemui.res.R.layout.global_actions_power_dialog_flow, null);
+
+ Flow flow = listView.findViewById(com.android.systemui.R.id.power_flow);
for (int i = 0; i < adapter.getCount(); i++) {
View action = adapter.getView(i, null, listView);
+ action.setId(View.generateViewId());
listView.addView(action);
+ flow.addView(action);
}
Resources res = context.getResources();
@@ -53,7 +59,8 @@
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.setTitle(""); // prevent Talkback from speaking first item name twice
window.setBackgroundDrawable(res.getDrawable(
- com.android.systemui.res.R.drawable.control_background, context.getTheme()));
+ com.android.systemui.res.R.drawable.global_actions_lite_background,
+ context.getTheme()));
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
return dialog;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
index 51978ec..2b0900f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
@@ -54,8 +54,9 @@
* Display the shutdown UI.
* @param isReboot Whether the device will be rebooting after this shutdown.
* @param reason Cause for the shutdown.
+ * @param rebootCustom Whether the device is rebooting via advanced reboot options
*/
- public void showShutdownUi(boolean isReboot, String reason) {
+ public void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
ScrimDrawable background = new ScrimDrawable();
final Dialog d = new Dialog(mContext,
@@ -119,8 +120,8 @@
reasonView.setTextColor(color);
messageView.setTextColor(color);
- messageView.setText(getRebootMessage(isReboot, reason));
- String rebootReasonMessage = getReasonMessage(reason);
+ messageView.setText(getRebootMessage(isReboot, reason, rebootCustom));
+ String rebootReasonMessage = getReasonMessage(reason, rebootCustom);
if (rebootReasonMessage != null) {
reasonView.setVisibility(View.VISIBLE);
reasonView.setText(rebootReasonMessage);
@@ -130,6 +131,15 @@
}
/**
+ * Display the shutdown UI.
+ * @param isReboot Whether the device will be rebooting after this shutdown.
+ * @param reason Cause for the shutdown.
+ */
+ public void showShutdownUi(boolean isReboot, String reason) {
+ showShutdownUi(isReboot, reason);
+ }
+
+ /**
* Returns the layout resource to use for UI while shutting down.
* @param isReboot Whether this is a reboot or a shutdown.
* @return
@@ -139,26 +149,44 @@
}
@StringRes
- @VisibleForTesting int getRebootMessage(boolean isReboot, @Nullable String reason) {
+ private int getRebootMessage(boolean isReboot, @Nullable String reason, boolean custom) {
if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
return R.string.reboot_to_update_reboot;
+ } else if (reason != null && !custom && reason.equals(PowerManager.REBOOT_RECOVERY)) {
+ return com.android.systemui.R.string.global_action_restart_progress;
} else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
- return R.string.reboot_to_reset_message;
+ return com.android.systemui.R.string.global_action_restart_recovery_progress;
+ } else if (reason != null && reason.equals(PowerManager.REBOOT_BOOTLOADER)) {
+ return com.android.systemui.R.string.global_action_restart_bootloader_progress;
+ } else if (reason != null && reason.equals(PowerManager.REBOOT_DOWNLOAD)) {
+ return com.android.systemui.R.string.global_action_restart_download_progress;
+ } else if (reason != null && reason.equals(PowerManager.REBOOT_FASTBOOT)) {
+ return com.android.systemui.R.string.global_action_restart_fastboot_progress;
} else if (isReboot) {
- return R.string.reboot_to_reset_message;
+ return com.android.systemui.R.string.global_action_restart_progress;
} else {
return R.string.shutdown_progress;
}
}
+ @StringRes
+ private int getRebootMessage(boolean isReboot, @Nullable String reason) {
+ return getRebootMessage(isReboot, reason, false);
+ }
+
@Nullable
- @VisibleForTesting String getReasonMessage(@Nullable String reason) {
+ private String getReasonMessage(@Nullable String reason, boolean custom) {
if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
return mContext.getString(R.string.reboot_to_update_title);
- } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
+ } else if (reason != null && !custom && reason.equals(PowerManager.REBOOT_RECOVERY)) {
return mContext.getString(R.string.reboot_to_reset_title);
} else {
return null;
}
}
+
+ @Nullable
+ private String getReasonMessage(@Nullable String reason) {
+ return getReasonMessage(reason, false);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
index e7803c5..d9ae201 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.binder
import android.os.VibrationEffect
+import com.android.systemui.statusbar.VibratorHelper
import kotlin.time.Duration.Companion.milliseconds
object KeyguardBottomAreaVibrations {
@@ -26,7 +27,13 @@
private const val SmallVibrationScale = 0.3f
private const val BigVibrationScale = 0.6f
-
+ val vibratorHelper: VibratorHelper? = null
+ val areAllPrimitivesSupported = vibratorHelper?.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_TICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_FALL
+ ) ?: false
+ val ShakeAlt = VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK)
val Shake =
VibrationEffect.startComposition()
.apply {
@@ -45,6 +52,7 @@
}
.compose()
+ val ActivatedAlt = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)
val Activated =
VibrationEffect.startComposition()
.addPrimitive(
@@ -59,6 +67,7 @@
)
.compose()
+ val DeactivatedAlt = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)
val Deactivated =
VibrationEffect.startComposition()
.addPrimitive(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3630b40..e4bc4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -238,7 +238,12 @@
isVisible ->
settingsMenu.animateVisibility(visible = isVisible)
if (isVisible) {
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
+ vibratorHelper?.vibrate(
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Activated
+ } else {
+ KeyguardBottomAreaVibrations.ActivatedAlt
+ })
settingsMenu.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
viewModel = viewModel.settingsMenuViewModel,
@@ -405,7 +410,12 @@
shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ vibratorHelper?.vibrate(
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Shake
+ } else {
+ KeyguardBottomAreaVibrations.ShakeAlt
+ })
}
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
@@ -475,9 +485,17 @@
)
vibratorHelper?.vibrate(
if (viewModel.isActivated) {
- KeyguardBottomAreaVibrations.Activated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Activated
+ } else {
+ KeyguardBottomAreaVibrations.ActivatedAlt
+ }
} else {
- KeyguardBottomAreaVibrations.Deactivated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Deactivated
+ } else {
+ KeyguardBottomAreaVibrations.DeactivatedAlt
+ }
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index f2d39da..f5a5fc9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -117,9 +117,17 @@
view.setOnClickListener {
vibratorHelper?.vibrate(
if (viewModel.isActivated) {
- KeyguardBottomAreaVibrations.Activated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Activated
+ } else {
+ KeyguardBottomAreaVibrations.ActivatedAlt
+ }
} else {
- KeyguardBottomAreaVibrations.Deactivated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Deactivated
+ } else {
+ KeyguardBottomAreaVibrations.DeactivatedAlt
+ }
}
)
viewModel.onClicked(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index abd79ab..5c74dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -226,7 +226,12 @@
shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ vibratorHelper?.vibrate(
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Shake
+ } else {
+ KeyguardBottomAreaVibrations.ShakeAlt
+ })
}
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
@@ -305,9 +310,17 @@
)
vibratorHelper?.vibrate(
if (viewModel.isActivated) {
- KeyguardBottomAreaVibrations.Activated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Activated
+ } else {
+ KeyguardBottomAreaVibrations.ActivatedAlt
+ }
} else {
- KeyguardBottomAreaVibrations.Deactivated
+ if (KeyguardBottomAreaVibrations.areAllPrimitivesSupported) {
+ KeyguardBottomAreaVibrations.Deactivated
+ } else {
+ KeyguardBottomAreaVibrations.DeactivatedAlt
+ }
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/leaf/LeafModule.kt b/packages/SystemUI/src/com/android/systemui/leaf/LeafModule.kt
new file mode 100644
index 0000000..ee81cb6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/leaf/LeafModule.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.leaf
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.CaffeineTile
+import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.CompassTile
+import com.android.systemui.qs.tiles.DataSwitchTile
+import com.android.systemui.qs.tiles.PowerShareTile
+import com.android.systemui.qs.tiles.WifiTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface LeafModule {
+ /** Inject CaffeineTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(CaffeineTile.TILE_SPEC)
+ fun bindCaffeineTile(caffeineTile: CaffeineTile): QSTileImpl<*>
+
+ /** Inject CellularTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(CellularTile.TILE_SPEC)
+ fun bindCellularTile(cellularTile: CellularTile): QSTileImpl<*>
+
+ /** Inject CompassTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(CompassTile.TILE_SPEC)
+ fun bindCompassTile(compassTile: CompassTile): QSTileImpl<*>
+
+ /** Inject DataSwitchTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(DataSwitchTile.TILE_SPEC)
+ fun bindDataSwitchTile(dataSwitchTileTile: DataSwitchTile): QSTileImpl<*>
+
+ /** Inject PowerShareTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(PowerShareTile.TILE_SPEC)
+ fun bindPowerShareTile(powerShareTile: PowerShareTile): QSTileImpl<*>
+
+ /** Inject WifiTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(WifiTile.TILE_SPEC)
+ fun bindWifiTile(wifiTile: WifiTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 13ba9c3..05d1dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1968,6 +1968,11 @@
setNavBarMode(mode);
mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
}
+
+ @Override
+ public void onSettingsChanged() {
+ mEdgeBackGestureHandler.onSettingsChanged();
+ }
};
private final Gefingerpoken mTouchHandler = new Gefingerpoken() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 42a25bd..d108600 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -20,7 +20,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
import android.content.Context;
@@ -77,6 +76,7 @@
public class NavigationBarControllerImpl implements
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener,
Dumpable, NavigationBarController {
private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
@@ -94,7 +94,7 @@
* Indicates whether the active display is a large screen, e.g. tablets, foldable devices in
* the unfolded state.
*/
- @VisibleForTesting boolean mIsLargeScreen;
+ @VisibleForTesting boolean mTaskbarShowing;
/**
* Indicates whether the device is a phone, rather than everything else (e.g. foldables,
* tablets) is considered not a handheld device.
@@ -148,7 +148,6 @@
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController, lightBarController, pipOptional,
backAnimation.orElse(null), taskStackChangeListeners);
- mIsLargeScreen = isLargeScreen(mContext);
mIsPhone =
mContext.getResources().getIntArray(R.array.config_foldedDeviceStates).length == 0;
dumpManager.registerDumpable(this);
@@ -156,10 +155,9 @@
@Override
public void onConfigChanged(Configuration newConfig) {
- boolean isOldConfigLargeScreen = mIsLargeScreen;
- mIsLargeScreen = isLargeScreen(mContext);
+ boolean oldShouldShowTaskbar = shouldShowTaskbar();
boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
- boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
+ boolean largeScreenChanged = shouldShowTaskbar() != oldShouldShowTaskbar;
// TODO(b/332635834): Disable this logging once b/332635834 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
@@ -205,6 +203,16 @@
});
}
+ @Override
+ public void onTaskbarEnabled(boolean enabled) {
+ boolean oldShouldShowTaskbar = shouldShowTaskbar();
+ mTaskbarShowing = enabled;
+ boolean largeScreenChanged = shouldShowTaskbar() != oldShouldShowTaskbar;
+ if (largeScreenChanged) {
+ updateNavbarForTaskbar();
+ }
+ }
+
private void updateAccessibilityButtonModeIfNeeded() {
final int mode = mSecureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
@@ -285,7 +293,7 @@
@VisibleForTesting
boolean supportsTaskbar() {
// Enable for tablets, unfolded state on a foldable device or (non handheld AND flag is set)
- return mIsLargeScreen || (!mIsPhone && enableTaskbarNavbarUnification());
+ return shouldShowTaskbar() || (!mIsPhone && enableTaskbarNavbarUnification());
}
private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
@@ -298,7 +306,6 @@
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsLargeScreen = isLargeScreen(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -462,6 +469,10 @@
}
}
+ private boolean shouldShowTaskbar() {
+ return mTaskbarShowing;
+ }
+
@Override
public @Nullable NavigationBarView getDefaultNavigationBarView() {
return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
@@ -496,7 +507,7 @@
@NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mIsLargeScreen=" + mIsLargeScreen);
+ pw.println("mTaskbarShowing=" + mTaskbarShowing);
pw.println("mNavMode=" + mNavMode);
for (int i = 0; i < mNavigationBars.size(); i++) {
if (i > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
index c1d98c9..48b7d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
@@ -18,11 +18,17 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.content.Context;
+import android.content.om.IOverlayManager;
import android.content.res.Configuration;
import android.graphics.drawable.Icon;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -43,12 +49,15 @@
import com.android.systemui.navigationbar.buttons.ReverseLinearLayout.ReverseRelativeLayout;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Objects;
-public class NavigationBarInflaterView extends FrameLayout {
+public class NavigationBarInflaterView extends FrameLayout
+ implements TunerService.Tunable {
+
private static final String TAG = "NavBarInflater";
public static final String NAV_BAR_VIEWS = "sysui_nav_bar";
@@ -82,6 +91,11 @@
private static final String ABSOLUTE_SUFFIX = "A";
private static final String ABSOLUTE_VERTICAL_CENTERED_SUFFIX = "C";
+ private static final String KEY_NAVIGATION_HINT =
+ Settings.Secure.NAVIGATION_BAR_HINT;
+ private static final String OVERLAY_NAVIGATION_HIDE_HINT =
+ "org.leafos.overlay.customization.navbar.nohint";
+
private static class Listener implements NavigationModeController.ModeChangedListener {
private final WeakReference<NavigationBarInflaterView> mSelf;
@@ -119,6 +133,8 @@
private OverviewProxyService mOverviewProxyService;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
+ private boolean mIsHintEnabled;
+
public NavigationBarInflaterView(Context context, AttributeSet attrs) {
super(context, attrs);
createInflaters();
@@ -161,11 +177,21 @@
: mOverviewProxyService.shouldShowSwipeUpUI()
? R.string.config_navBarLayoutQuickstep
: R.string.config_navBarLayout;
+ if (!mIsHintEnabled && defaultResource == R.string.config_navBarLayoutHandle) {
+ return getContext().getString(defaultResource).replace(HOME_HANDLE, "");
+ }
return getContext().getString(defaultResource);
}
private void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ updateHint();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Dependency.get(TunerService.class).addTunable(this, KEY_NAVIGATION_HINT);
}
@Override
@@ -174,6 +200,15 @@
super.onDetachedFromWindow();
}
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (KEY_NAVIGATION_HINT.equals(key)) {
+ mIsHintEnabled = TunerService.parseIntegerSwitch(newValue, true);
+ updateHint();
+ onLikelyDefaultLayoutChange();
+ }
+ }
+
public void onLikelyDefaultLayoutChange() {
// Reevaluate new layout
final String newValue = getDefaultLayout();
@@ -227,6 +262,24 @@
}
}
+ private void updateHint() {
+ final IOverlayManager iom = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
+ final boolean state = mNavBarMode == NAV_BAR_MODE_GESTURAL && !mIsHintEnabled;
+ final int userId = ActivityManager.getCurrentUser();
+ try {
+ iom.setEnabled(OVERLAY_NAVIGATION_HIDE_HINT, state, userId);
+ if (state) {
+ // As overlays are also used to apply navigation mode, it is needed to set
+ // our customization overlay to highest priority to ensure it is applied.
+ iom.setHighestPriority(OVERLAY_NAVIGATION_HIDE_HINT, userId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to " + (state ? "enable" : "disable")
+ + " overlay " + OVERLAY_NAVIGATION_HIDE_HINT + " for user " + userId);
+ }
+ }
+
private void initiallyFill(ButtonDispatcher buttonDispatcher) {
addAll(buttonDispatcher, mHorizontal.findViewById(R.id.ends_group));
addAll(buttonDispatcher, mHorizontal.findViewById(R.id.center_group));
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index 99daf36..0e5be0f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -19,12 +19,16 @@
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -61,6 +65,7 @@
public interface ModeChangedListener {
void onNavigationModeChanged(int mode);
+ default void onSettingsChanged() {}
}
private final Context mContext;
@@ -83,6 +88,21 @@
}
};
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onSettingsChanged();
+ }
+ }
+ }
+
+ private SettingsObserver mSettingsObserver;
+
// The primary user SysUI process doesn't get AppInfo changes from overlay package changes for
// the secondary user (b/158613864), so we need to update the interaction mode here as well
// as a fallback if we don't receive the configuration change
@@ -118,6 +138,11 @@
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
+ mSettingsObserver = new SettingsObserver(new Handler());
+ mContext.getContentResolver().registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BACK_GESTURE_HEIGHT),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+
configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@Override
public void onThemeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index d40949f..ab2c1d0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -47,6 +47,8 @@
import android.os.Trace;
import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.TypedValue;
import android.view.Choreographer;
@@ -271,6 +273,8 @@
private int mRightInset;
private int mSysUiFlags;
+ private int mEdgeHeight;
+
// For Tf-Lite model.
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
private Map<String, Integer> mVocab;
@@ -471,6 +475,28 @@
updateCurrentUserResources();
}
+ private void updateEdgeHeightValue() {
+ if (mDisplaySize == null) {
+ return;
+ }
+ int edgeHeightSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.BACK_GESTURE_HEIGHT, 0, UserHandle.USER_CURRENT);
+ // edgeHeigthSettings cant be range 0 - 3
+ // 0 means full height
+ // 1 measns half of the screen
+ // 2 means lower third of the screen
+ // 3 means lower sicth of the screen
+ if (edgeHeightSetting == 0) {
+ mEdgeHeight = mDisplaySize.y;
+ } else if (edgeHeightSetting == 1) {
+ mEdgeHeight = mDisplaySize.y / 2;
+ } else if (edgeHeightSetting == 2) {
+ mEdgeHeight = mDisplaySize.y / 3;
+ } else {
+ mEdgeHeight = mDisplaySize.y / 6;
+ }
+ }
+
public void setStateChangeCallback(Runnable callback) {
mStateChangeCallback = callback;
}
@@ -590,6 +616,10 @@
}
}
+ public void onSettingsChanged() {
+ updateEdgeHeightValue();
+ }
+
public void onNavBarTransientStateChanged(boolean isTransient) {
mIsNavBarShownTransiently = isTransient;
}
@@ -855,6 +885,11 @@
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
return false;
}
+ if (mEdgeHeight != 0) {
+ if (y < (mDisplaySize.y - mBottomGestureHeight - mEdgeHeight)) {
+ return false;
+ }
+ }
// If the point is way too far (twice the margin), it is
// not interesting to us for logging purposes, nor we
// should process it. Simply return false and keep
@@ -1198,6 +1233,7 @@
mEdgeBackPlugin.setDisplaySize(mDisplaySize);
}
updateBackAnimationThresholds();
+ updateEdgeHeightValue();
}
private void updateBackAnimationThresholds() {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 7a4be3f..3665372 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -125,7 +125,7 @@
iconSize = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
iconColor =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
val height = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_height)
@@ -135,4 +135,4 @@
iconsContainer.setPaddingRelative(padding, 0, padding, 0)
iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 43f3a22..6694556 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -489,6 +489,11 @@
return changed;
}
+ @Override
+ public int getMaxColumns() {
+ return mMaxColumns;
+ }
+
/**
* Set the amount of excess space that we gave this view compared to the actual available
* height. This is because this view is in a scrollview.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 0cb695c..af83acf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
+
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -44,6 +46,9 @@
import javax.inject.Inject;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
/**
* Performs the animated transition between the QQS and QS views.
*
@@ -135,12 +140,18 @@
private float mLastPosition;
private final QSHost mHost;
private final DelayableExecutor mExecutor;
+ private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
private int mQQSTop;
private int[] mTmpLoc1 = new int[2];
private int[] mTmpLoc2 = new int[2];
+ private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
+ requestAnimatorUpdate();
+ return null;
+ };
+
@Inject
public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
QSPanelController qsPanelController,
@@ -153,6 +164,7 @@
mQuickQSPanelController = quickQSPanelController;
mHost = qsTileHost;
mExecutor = executor;
+ mTunerService = tunerService;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
@@ -212,11 +224,13 @@
public void onViewAttachedToWindow(@NonNull View view) {
updateAnimators();
setCurrentPosition();
+ mQuickQSPanelController.mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
}
@Override
public void onViewDetachedFromWindow(@NonNull View v) {
mHost.removeCallback(this);
+ mQuickQSPanelController.mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
}
private void addNonFirstPageAnimators(int page) {
@@ -315,8 +329,12 @@
View view = mQsRootView;
+ int visibleTileCount = isRoundQS() ?
+ mQuickQSPanelController.getTileLayout().getMaxColumns() :
+ mQuickQSPanelController.getTileLayout().getNumVisibleTiles();
+
// This case: less tiles to animate in small displays.
- if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
+ if (count < visibleTileCount) {
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
@@ -344,8 +362,8 @@
// Icons
translateContent(
- quickTileView.getIcon(),
- tileView.getIcon(),
+ isRoundQS() ? quickTileView.getIconWithBackground() : quickTileView.getIcon(),
+ isRoundQS() ? tileView.getIconWithBackground() : tileView.getIcon(),
view,
xOffset,
yOffset,
@@ -387,13 +405,13 @@
// Therefore, we use a quadratic interpolator animator to animate the alpha
// for tiles in QQS to match.
quadraticInterpolatorBuilder
- .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+ .addFloat(isRoundQS() ? quickTileView.getLabelContainer() : quickTileView.getSecondaryLabel(), "alpha", 0, 1);
nonFirstPageAlphaBuilder
- .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0);
+ .addFloat(isRoundQS() ? quickTileView.getLabelContainer() : quickTileView.getSecondaryLabel(), "alpha", 0, 0);
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
- mAllViews.add(quickTileView.getSecondaryLabel());
+ mAllViews.add(isRoundQS() ? quickTileView.getLabelContainer() : quickTileView.getSecondaryLabel());
} else if (!isIconInAnimatedRow(count)) {
// Pretend there's a corresponding QQS tile (for the position) that we are
// expanding from.
@@ -412,8 +430,8 @@
mOtherFirstPageTilesHeightAnimator.addView(tileView);
tileView.setClipChildren(true);
tileView.setClipToPadding(true);
- firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
- mAllViews.add(tileView.getSecondaryLabel());
+ firstPageBuilder.addFloat(isRoundQS() ? tileView.getLabelContainer() : tileView.getSecondaryLabel(), "alpha", 0, 1);
+ mAllViews.add(isRoundQS() ? tileView.getLabelContainer() : tileView.getSecondaryLabel());
}
mAllViews.add(tileView);
@@ -555,6 +573,12 @@
mBrightnessOpacityAnimator = null;
View qsBrightness = mQsPanelController.getBrightnessView();
View qqsBrightness = mQuickQSPanelController.getBrightnessView();
+
+ if (mTunerService.getValue(QSPanel.QS_SHOW_BRIGHTNESS_SLIDER, 2) == 0) {
+ qsBrightness.setVisibility(View.GONE);
+ qqsBrightness.setVisibility(View.GONE);
+ }
+
if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) {
// animating in split shade mode
mAnimatedQsViews.add(qsBrightness);
@@ -567,6 +591,8 @@
.addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
.addFloat(qqsBrightness, "translationY", 0, translationY)
.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
+ .setInterpolator(mQuickQSPanelController.mMediaHost.getVisible() ?
+ Interpolators.ALPHA_OUT : com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR)
.build();
} else if (qsBrightness != null) {
// The brightness slider's visible bottom edge must maintain a constant margin from the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 4ee2db7..bd16c89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -19,8 +19,10 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
@@ -109,6 +111,12 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
// bottom and footer are inside the screen.
+ Configuration config = getResources().getConfiguration();
+ boolean navBelow = config.smallestScreenWidthDp >= 600
+ || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+
+ // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+ // subtract its height. We do not care if the collapsed notifications fit in the screen.
int availableHeight = View.MeasureSpec.getSize(heightMeasureSpec);
if (!mSceneContainerEnabled) {
@@ -116,6 +124,9 @@
(MarginLayoutParams) mQSPanelContainer.getLayoutParams();
int maxQs = availableHeight - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
+ if (navBelow && isRoundQS()) {
+ maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+ }
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
@@ -123,6 +134,8 @@
mQSPanelContainer.measure(qsPanelWidthSpec,
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
+ int height = layoutParams.topMargin + layoutParams.bottomMargin
+ + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 5a872d6..c95197f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -21,12 +21,23 @@
import android.content.Context;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.Uri;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.text.BidiFormatter;
+import android.text.format.Formatter;
+import android.text.format.Formatter.BytesResult;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -35,16 +46,21 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
+import com.android.settingslib.net.DataUsageController;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.res.R;
+import java.util.List;
+
/**
* Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
* {@link FooterActionsView}
*/
public class QSFooterView extends FrameLayout {
+ private static final String TAG = "QSFooterView";
+
private PageIndicator mPageIndicator;
- private TextView mBuildText;
+ private TextView mUsageText;
private View mEditButton;
@Nullable
@@ -54,51 +70,119 @@
private boolean mExpanded;
private float mExpansionAmount;
- private boolean mShouldShowBuildText;
+ private boolean mShouldShowUsageText;
@Nullable
private OnClickListener mExpandClickListener;
- private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
- new Handler(mContext.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- setBuildText();
- }
- };
+ private DataUsageController mDataController;
+ private SubscriptionManager mSubManager;
+
+ private boolean mHasNoSims;
+ private boolean mIsWifiConnected;
+ private String mWifiSsid;
public QSFooterView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mDataController = new DataUsageController(context);
+ mSubManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mPageIndicator = findViewById(R.id.footer_page_indicator);
- mBuildText = findViewById(R.id.build);
+ mUsageText = findViewById(R.id.build);
mEditButton = findViewById(android.R.id.edit);
updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- setBuildText();
+ setUsageText();
}
- private void setBuildText() {
- if (mBuildText == null) return;
- if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
- mBuildText.setText(mContext.getString(
- com.android.internal.R.string.bugreport_status,
- Build.VERSION.RELEASE_OR_CODENAME,
- Build.ID));
- // Set as selected for marquee before its made visible, then it won't be announced when
- // it's made visible.
- mBuildText.setSelected(true);
- mShouldShowBuildText = true;
+ private void setUsageText() {
+ if (mUsageText == null || !mExpanded) return;
+ DataUsageController.DataUsageInfo info;
+ String suffix;
+ if (mIsWifiConnected) {
+ info = mDataController.getWifiDailyDataUsageInfo(true);
+ if (info == null) {
+ info = mDataController.getWifiDailyDataUsageInfo(false);
+ suffix = mContext.getResources().getString(R.string.usage_wifi_default_suffix);
+ } else {
+ suffix = getWifiSsid();
+ }
+ } else if (!mHasNoSims) {
+ mDataController.setSubscriptionId(
+ SubscriptionManager.getDefaultDataSubscriptionId());
+ info = mDataController.getDailyDataUsageInfo();
+ suffix = getSlotCarrierName();
} else {
- mBuildText.setText(null);
- mShouldShowBuildText = false;
- mBuildText.setSelected(false);
+ mShouldShowUsageText = false;
+ mUsageText.setText(null);
+ updateVisibilities();
+ return;
+ }
+ if (info == null) {
+ Log.w(TAG, "setUsageText: DataUsageInfo is NULL.");
+ return;
+ }
+ mShouldShowUsageText = true;
+ mUsageText.setText(formatDataUsage(info.usageLevel) + " " +
+ mContext.getResources().getString(R.string.usage_data) +
+ " (" + suffix + ")");
+ updateVisibilities();
+ }
+
+ private CharSequence formatDataUsage(long byteValue) {
+ final BytesResult res = Formatter.formatBytes(mContext.getResources(), byteValue,
+ Formatter.FLAG_IEC_UNITS);
+ return BidiFormatter.getInstance().unicodeWrap(mContext.getString(
+ com.android.internal.R.string.fileSizeSuffix, res.value, res.units));
+ }
+
+ private String getSlotCarrierName() {
+ CharSequence result = mContext.getResources().getString(R.string.usage_data_default_suffix);
+ int subId = mSubManager.getDefaultDataSubscriptionId();
+ final List<SubscriptionInfo> subInfoList =
+ mSubManager.getActiveSubscriptionInfoList(true);
+ if (subInfoList != null) {
+ for (SubscriptionInfo subInfo : subInfoList) {
+ if (subId == subInfo.getSubscriptionId()) {
+ result = subInfo.getDisplayName();
+ break;
+ }
+ }
+ }
+ return result.toString();
+ }
+
+ private String getWifiSsid() {
+ if (mWifiSsid == null) {
+ return mContext.getResources().getString(R.string.usage_wifi_default_suffix);
+ } else {
+ return mWifiSsid.replace("\"", "");
+ }
+ }
+
+ protected void setWifiSsid(String ssid) {
+ if (mWifiSsid != ssid) {
+ mWifiSsid = ssid;
+ setUsageText();
+ }
+ }
+
+ protected void setIsWifiConnected(boolean connected) {
+ if (mIsWifiConnected != connected) {
+ mIsWifiConnected = connected;
+ setUsageText();
+ }
+ }
+
+ protected void setNoSims(boolean hasNoSims) {
+ if (mHasNoSims != hasNoSims) {
+ mHasNoSims = hasNoSims;
+ setUsageText();
}
}
@@ -111,7 +195,7 @@
private void updateResources() {
updateFooterAnimator();
updateEditButtonResources();
- updateBuildTextResources();
+ updateUsageTextResources();
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.height = getResources().getDimensionPixelSize(R.dimen.qs_footer_height);
int sideMargin = getResources().getDimensionPixelSize(R.dimen.qs_footer_margin);
@@ -131,8 +215,8 @@
mEditButton.setPadding(padding, padding, padding, padding);
}
- private void updateBuildTextResources() {
- FontSizeUtils.updateFontSizeFromStyle(mBuildText, R.style.TextAppearance_QS_Status_Build);
+ private void updateUsageTextResources() {
+ FontSizeUtils.updateFontSizeFromStyle(mUsageText, R.style.TextAppearance_QS_Status_Build);
}
private void updateFooterAnimator() {
@@ -143,7 +227,7 @@
private TouchAnimator createFooterAnimator() {
TouchAnimator.Builder builder = new TouchAnimator.Builder()
.addFloat(mPageIndicator, "alpha", 0, 1)
- .addFloat(mBuildText, "alpha", 0, 1)
+ .addFloat(mUsageText, "alpha", 0, 1)
.addFloat(mEditButton, "alpha", 0, 1)
.setStartDelay(0.9f);
return builder.build();
@@ -170,21 +254,18 @@
if (mFooterAnimator != null) {
mFooterAnimator.setPosition(headerExpansionFraction);
}
- }
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED), false,
- mDeveloperSettingsObserver, UserHandle.USER_ALL);
- }
-
- @Override
- @VisibleForTesting
- public void onDetachedFromWindow() {
- mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);
- super.onDetachedFromWindow();
+ if (mUsageText == null) return;
+ if (headerExpansionFraction == 1.0f) {
+ mUsageText.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mUsageText.setSelected(true);
+ }
+ }, 1000);
+ } else {
+ mUsageText.setSelected(false);
+ }
}
void disable(int state2) {
@@ -197,16 +278,12 @@
void updateEverything() {
post(() -> {
updateVisibilities();
- updateClickabilities();
+ setUsageText();
setClickable(false);
});
}
- private void updateClickabilities() {
- mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
- }
-
private void updateVisibilities() {
- mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
+ mUsageText.setVisibility(mExpanded && mShouldShowUsageText ? View.VISIBLE : View.INVISIBLE);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index ffbd06f..43d2e89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,8 +16,15 @@
package com.android.systemui.qs;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkScoreManager;
+import android.net.wifi.WifiManager;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
@@ -29,8 +36,12 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.retail.domain.interactor.RetailModeInteractor;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.util.ViewController;
+import com.android.settingslib.wifi.WifiStatusTracker;
+
import javax.inject.Inject;
/**
@@ -41,12 +52,29 @@
private final UserTracker mUserTracker;
private final QSPanelController mQsPanelController;
- private final TextView mBuildText;
private final PageIndicator mPageIndicator;
private final View mEditButton;
private final FalsingManager mFalsingManager;
private final ActivityStarter mActivityStarter;
private final RetailModeInteractor mRetailModeInteractor;
+ private final WifiStatusTracker mWifiTracker;
+ private final NetworkController mNetworkController;
+ private final Context mContext;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mWifiTracker.handleBroadcast(intent);
+ onWifiStatusUpdated();
+ }
+ };
+
+ private final SignalCallback mSignalCallback = new SignalCallback() {
+ @Override
+ public void setNoSims(boolean show, boolean simDetected) {
+ mView.setNoSims(show);
+ }
+ };
@Inject
QSFooterViewController(QSFooterView view,
@@ -54,8 +82,9 @@
FalsingManager falsingManager,
ActivityStarter activityStarter,
QSPanelController qsPanelController,
- RetailModeInteractor retailModeInteractor
- ) {
+ RetailModeInteractor retailModeInteractor,
+ NetworkController networkController,
+ Context context) {
super(view);
mUserTracker = userTracker;
mQsPanelController = qsPanelController;
@@ -63,27 +92,18 @@
mActivityStarter = activityStarter;
mRetailModeInteractor = retailModeInteractor;
- mBuildText = mView.findViewById(R.id.build);
+ mNetworkController = networkController;
+ mContext = context;
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
mEditButton = mView.findViewById(android.R.id.edit);
+ mWifiTracker = new WifiStatusTracker(context, context.getSystemService(WifiManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ this::onWifiStatusUpdated);
}
@Override
protected void onViewAttached() {
- mBuildText.setOnLongClickListener(view -> {
- CharSequence buildText = mBuildText.getText();
- if (!TextUtils.isEmpty(buildText)) {
- ClipboardManager service =
- mUserTracker.getUserContext().getSystemService(ClipboardManager.class);
- String label = getResources().getString(R.string.build_number_clip_data_label);
- service.setPrimaryClip(ClipData.newPlainText(label, buildText));
- Toast.makeText(getContext(), R.string.build_number_copy_toast, Toast.LENGTH_SHORT)
- .show();
- return true;
- }
- return false;
- });
-
mEditButton.setOnClickListener(view -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
@@ -92,11 +112,22 @@
.postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view));
});
mQsPanelController.setFooterPageIndicator(mPageIndicator);
- mView.updateEverything();
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ mContext.registerReceiver(mReceiver, filter);
+ mWifiTracker.fetchInitialState();
+ mWifiTracker.setListening(true);
+ onWifiStatusUpdated();
+ mNetworkController.addCallback(mSignalCallback);
}
@Override
- protected void onViewDetached() {}
+ protected void onViewDetached() {
+ mContext.unregisterReceiver(mReceiver);
+ mNetworkController.removeCallback(mSignalCallback);
+ }
@Override
public void setVisibility(int visibility) {
@@ -125,4 +156,9 @@
public void disable(int state1, int state2, boolean animate) {
mView.disable(state2);
}
+
+ private void onWifiStatusUpdated() {
+ mView.setIsWifiConnected(mWifiTracker.connected);
+ mView.setWifiSsid(mWifiTracker.ssid);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a000d63..3c6f6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
@@ -546,6 +547,7 @@
public void setBrightnessMirrorController(
BrightnessMirrorController brightnessMirrorController) {
mQSPanelController.setBrightnessMirror(brightnessMirrorController);
+ mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7a7ee59..360fd94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,8 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -39,6 +42,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
+import com.android.systemui.Dependency;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.res.R;
@@ -52,8 +56,12 @@
/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable {
- public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
- public static final String QS_SHOW_HEADER = "qs_show_header";
+ public static final String QS_SHOW_AUTO_BRIGHTNESS =
+ Settings.Secure.QS_SHOW_AUTO_BRIGHTNESS;
+ public static final String QS_SHOW_BRIGHTNESS_SLIDER =
+ Settings.Secure.QS_SHOW_BRIGHTNESS_SLIDER;
+ public static final String QS_BRIGHTNESS_SLIDER_POSITION =
+ Settings.Secure.QS_BRIGHTNESS_SLIDER_POSITION;
private static final String TAG = "QSPanel";
@@ -70,14 +78,21 @@
@Nullable
protected View mBrightnessView;
+ protected View mAutoBrightnessView;
+
@Nullable
protected BrightnessSliderController mToggleSliderController;
+ protected Runnable mBrightnessRunnable;
+
+ protected boolean mTop;
+
/** Whether or not the QS media player feature is enabled. */
protected boolean mUsingMediaPlayer;
protected boolean mExpanded;
protected boolean mListening;
+ protected boolean mIsAutomaticBrightnessAvailable = false;
private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
new ArrayList<>();
@@ -89,7 +104,10 @@
private PageIndicator mFooterPageIndicator;
private int mContentMarginStart;
private int mContentMarginEnd;
- private boolean mUsingHorizontalLayout;
+ private int mMaxColumnsPortrait;
+ private int mMaxColumnsLandscape;
+ private int mMaxColumnsMediaPlayer;
+ protected boolean mUsingHorizontalLayout;
@Nullable
private LinearLayout mHorizontalLinearLayout;
@@ -119,12 +137,27 @@
R.dimen.quick_settings_bottom_margin_media);
mMediaTopMargin = getResources().getDimensionPixelSize(
R.dimen.qs_tile_margin_vertical);
+ mMaxColumnsPortrait = getResources().getInteger(R.integer.qs_panel_num_columns);
+ mMaxColumnsLandscape = getResources().getInteger(R.integer.qs_panel_num_columns_landscape);
+ mMaxColumnsMediaPlayer = getResources().getInteger(R.integer.qs_panel_num_columns_media);
+ if (isRoundQS()) {
+ mMaxColumnsPortrait = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.QS_NUM_COLUMNS, mMaxColumnsPortrait);
+ mMaxColumnsLandscape = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.QS_NUM_COLUMNS_LANDSCAPE, mMaxColumnsLandscape);
+ }
+
mContext = context;
setOrientation(VERTICAL);
mMovableContentStartIndex = getChildCount();
+ mIsAutomaticBrightnessAvailable = getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available);
+
+ TunerService tunerService = Dependency.get(TunerService.class);
+ mTop = tunerService.getValue(QS_BRIGHTNESS_SLIDER_POSITION, 1) == 0;
}
void initialize(QSLogger qsLogger) {
@@ -179,6 +212,7 @@
mClippingRect.left = 0;
mClippingRect.top = -1000;
mHorizontalContentContainer.setClipBounds(mClippingRect);
+ updateColumns();
}
/**
@@ -192,21 +226,28 @@
mChildrenLayoutTop.remove(mBrightnessView);
mMovableContentStartIndex--;
}
- addView(view, 0);
mBrightnessView = view;
-
- setBrightnessViewMargin();
-
- mMovableContentStartIndex++;
+ mAutoBrightnessView = view.findViewById(R.id.brightness_icon);
+ setBrightnessViewMargin(mTop);
+ if (mBrightnessView != null) {
+ addView(mBrightnessView);
+ mMovableContentStartIndex++;
+ }
}
- private void setBrightnessViewMargin() {
+ private void setBrightnessViewMargin(boolean top) {
if (mBrightnessView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams();
- lp.topMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_top);
- lp.bottomMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom);
+ if (top) {
+ lp.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_top_brightness_margin_top);
+ lp.bottomMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_top_brightness_margin_bottom);
+ } else {
+ lp.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_bottom_brightness_margin_top);
+ lp.bottomMargin = 0;
+ }
mBrightnessView.setLayoutParams(lp);
}
}
@@ -331,13 +372,31 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) {
- updateViewVisibilityForTuningValue(mBrightnessView, newValue);
- }
+ switch (key) {
+ case QS_SHOW_BRIGHTNESS_SLIDER:
+ boolean value =
+ TunerService.parseInteger(newValue, 2) >= 1;
+ if (mBrightnessView != null) {
+ mBrightnessView.setVisibility(value ? VISIBLE : GONE);
+ }
+ break;
+ case QS_BRIGHTNESS_SLIDER_POSITION:
+ mTop = TunerService.parseInteger(newValue, 1) == 0;
+ updateBrightnessSliderPosition();
+ break;
+ case QS_SHOW_AUTO_BRIGHTNESS:
+ if (mAutoBrightnessView != null) {
+ mAutoBrightnessView.setVisibility(mIsAutomaticBrightnessAvailable &&
+ TunerService.parseIntegerSwitch(newValue, true) ? View.VISIBLE : View.GONE);
+ }
+ break;
+ default:
+ break;
+ }
}
- private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
- view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
+ public void setBrightnessRunnable(Runnable runnable) {
+ mBrightnessRunnable = runnable;
}
@@ -373,7 +432,7 @@
updatePageIndicator();
- setBrightnessViewMargin();
+ setBrightnessViewMargin(mTop);
if (mTileLayout != null) {
mTileLayout.updateResources();
@@ -443,10 +502,20 @@
private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) {
int index = parent == this ? mMovableContentStartIndex : 0;
+ if (mBrightnessView != null && mTop) {
+ switchToParent(mBrightnessView, parent, index);
+ index++;
+ }
+
// Let's first move the tileLayout to the new parent, since that should come first.
switchToParent((View) newLayout, parent, index);
index++;
+ if (mBrightnessView != null && !mTop) {
+ switchToParent(mBrightnessView, parent, index);
+ index++;
+ }
+
if (mFooter != null) {
// Then the footer with the settings
switchToParent(mFooter, parent, index);
@@ -612,16 +681,31 @@
}
}
+ public void updateColumns() {
+ boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+
+ int mColumnsMediaPlayer = mUsingHorizontalLayout ?
+ mMaxColumnsMediaPlayer :
+ mMaxColumnsLandscape;
+
+ mTileLayout.setMaxColumns(isLandscape ?
+ mColumnsMediaPlayer :
+ mMaxColumnsPortrait);
+ }
+
void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force) {
if (horizontal != mUsingHorizontalLayout || force) {
Log.d(getDumpableTag(), "setUsingHorizontalLayout: " + horizontal + ", " + force);
mUsingHorizontalLayout = horizontal;
ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
switchAllContentToParent(newParent, mTileLayout);
+ if (mBrightnessRunnable != null) {
+ updateResources();
+ mBrightnessRunnable.run();
+ }
reAttachMediaHost(mediaHostView, horizontal);
if (needsDynamicRowsAndColumns()) {
- mTileLayout.setMinRows(horizontal ? 2 : 1);
- mTileLayout.setMaxColumns(horizontal ? 2 : 4);
+ updateColumns();
}
updateMargins(mediaHostView);
mHorizontalLinearLayout.setVisibility(horizontal ? View.VISIBLE : View.GONE);
@@ -678,6 +762,16 @@
mCanCollapse = canCollapse;
}
+ protected void updateBrightnessSliderPosition() {
+ if (mBrightnessView == null) return;
+ ViewGroup newParent = mUsingHorizontalLayout ? mHorizontalContentContainer : this;
+ switchAllContentToParent(newParent, mTileLayout);
+ if (mBrightnessRunnable != null) {
+ updateResources();
+ mBrightnessRunnable.run();
+ }
+ }
+
public interface QSTileLayout {
/** */
default void saveInstanceState(Bundle outState) {}
@@ -731,6 +825,12 @@
return false;
}
+ /** Gets the max number of columns to show
+ *
+ * @return The maximum number of visible columns.
+ */
+ int getMaxColumns();
+
/**
* Sets the expansion value and proposedTranslation to panel.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 2440651..d5f0377 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -18,7 +18,6 @@
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
-import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
import android.view.MotionEvent;
@@ -61,6 +60,7 @@
private BrightnessMirrorHandler mBrightnessMirrorHandler;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private boolean mListening;
+ private BrightnessMirrorController mBrightnessMirrorController;
private final boolean mSceneContainerEnabled;
@@ -126,13 +126,22 @@
updateMediaDisappearParameters();
- mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
+ mTunerService.addTunable(mView, QSPanel.QS_SHOW_BRIGHTNESS_SLIDER);
+ mTunerService.addTunable(mView, QSPanel.QS_SHOW_AUTO_BRIGHTNESS);
+ mTunerService.addTunable(mView, QSPanel.QS_BRIGHTNESS_SLIDER_POSITION);
+
+ mView.setBrightnessRunnable(() -> {
+ mView.updateResources();
+ updateBrightnessMirror();
+ });
+
mView.updateResources();
mView.setSceneContainerEnabled(mSceneContainerEnabled);
if (mView.isListening()) {
refreshAllTiles();
}
switchTileLayout(true);
+ mView.updateColumns();
mBrightnessMirrorHandler.onQsPanelAttached();
PagedTileLayout pagedTileLayout= ((PagedTileLayout) mView.getOrCreateTileLayout());
pagedTileLayout.setOnTouchListener(mTileLayoutTouchListener);
@@ -147,6 +156,7 @@
@Override
protected void onViewDetached() {
mTunerService.removeTunable(mView);
+ mView.setBrightnessRunnable(null);
mBrightnessMirrorHandler.onQsPanelDettached();
super.onViewDetached();
}
@@ -154,6 +164,7 @@
@Override
protected void onConfigurationChanged() {
mView.updateResources();
+ mView.updateColumns();
int newDensity = mView.getResources().getConfiguration().densityDpi;
if (newDensity != mLastDensity) {
mLastDensity = newDensity;
@@ -186,6 +197,12 @@
mView.setCanCollapse(!shouldUseSplitNotificationShade);
}
+ private void updateBrightnessMirror() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessSliderController.setMirrorControllerAndMirror(mBrightnessMirrorController);
+ }
+ }
+
/** */
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
@@ -208,6 +225,7 @@
}
public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
+ mBrightnessMirrorController = brightnessMirrorController;
mBrightnessMirrorHandler.setController(brightnessMirrorController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6c32ed3..7b5a3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,8 +16,12 @@
package com.android.systemui.qs;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
+
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -25,30 +29,103 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.tuner.TunerService;
/**
* Version of QSPanel that only shows N Quick Tiles in the QS Header.
*/
-public class QuickQSPanel extends QSPanel {
+public class QuickQSPanel extends QSPanel implements TunerService.Tunable {
private static final String TAG = "QuickQSPanel";
// A fallback value for max tiles number when setting via Tuner (parseNumTiles)
public static final int TUNER_MAX_TILES_FALLBACK = 6;
+ private QSLogger mQsLogger;
+
+ // Tile Columns on normal conditions
+ public int mMaxColumnsPortrait = 5;
+ public int mMaxColumnsLandscape = 6;
+ // Tile Columns when media player is visible
+ public int mMaxColumnsMediaPlayer = 4;
+
private boolean mDisabledByPolicy;
private int mMaxTiles;
public QuickQSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
+ mMaxColumnsPortrait = getResources().getInteger(R.integer.quick_qs_panel_num_columns);
+ mMaxColumnsLandscape = getResources().getInteger(R.integer.quick_qs_panel_num_columns_landscape);
+ mMaxColumnsMediaPlayer = getResources().getInteger(R.integer.quick_qs_panel_num_columns_media);
+ if (isRoundQS()) {
+ mMaxColumnsPortrait = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.QQS_NUM_COLUMNS, mMaxColumnsPortrait);
+ mMaxColumnsLandscape = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.QQS_NUM_COLUMNS_LANDSCAPE, mMaxColumnsLandscape);
+
+ mMaxTiles = Math.max(mMaxColumnsPortrait, mMaxColumnsLandscape);
+ mMaxTiles = Math.max(mMaxColumnsMediaPlayer, mMaxTiles);
+ }
}
@Override
protected void setHorizontalContentContainerClipping() {
mHorizontalContentContainer.setClipToPadding(false);
mHorizontalContentContainer.setClipChildren(false);
+ updateColumns();
+ }
+
+
+ @Override
+ public void setBrightnessView(@NonNull View view) {
+ if (mBrightnessView != null) {
+ removeView(mBrightnessView);
+ }
+ mBrightnessView = view;
+ mAutoBrightnessView = view.findViewById(R.id.brightness_icon);
+ setBrightnessViewMargin(mTop);
+ if (mBrightnessView != null) {
+ addView(mBrightnessView);
+
+ TunerService tunerService = Dependency.get(TunerService.class);
+ if (tunerService.getValue(QS_SHOW_BRIGHTNESS_SLIDER, 2) > 1) {
+ mBrightnessView.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ View getBrightnessView() {
+ return mBrightnessView;
+ }
+
+ private void setBrightnessViewMargin(boolean top) {
+ if (mBrightnessView != null) {
+ MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams();
+ if (top) {
+ lp.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_top_brightness_margin_top);
+ lp.bottomMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_top_brightness_margin_bottom);
+ } else {
+ lp.topMargin = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_bottom_brightness_margin_top);
+ lp.bottomMargin = 0;
+ }
+ mBrightnessView.setLayoutParams(lp);
+ }
+ }
+
+ @Override
+ void initialize(QSLogger qsLogger) {
+ mQsLogger = qsLogger;
+ super.initialize(mQsLogger);
+ if (mHorizontalContentContainer != null) {
+ mHorizontalContentContainer.setClipChildren(false);
+ }
}
@Override
@@ -89,16 +166,33 @@
return !mExpanded;
}
+ public void updateColumns() {
+ boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+
+ int mColumnsMediaPlayer = mUsingHorizontalLayout ?
+ mMaxColumnsMediaPlayer :
+ mMaxColumnsLandscape;
+
+ mTileLayout.setMaxColumns(isLandscape ?
+ mColumnsMediaPlayer :
+ mMaxColumnsPortrait);
+ }
+
public void setMaxTiles(int maxTiles) {
mMaxTiles = maxTiles;
}
@Override
public void onTuningChanged(String key, String newValue) {
- if (QS_SHOW_BRIGHTNESS.equals(key)) {
- // No Brightness or Tooltip for you!
- super.onTuningChanged(key, "0");
- }
+ switch (key) {
+ case QS_SHOW_BRIGHTNESS_SLIDER:
+ boolean value =
+ TunerService.parseInteger(newValue, 2) > 1;
+ super.onTuningChanged(key, value ? newValue : "0");
+ break;
+ default:
+ super.onTuningChanged(key, newValue);
+ }
}
public int getNumQuickTiles() {
@@ -179,7 +273,7 @@
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
setLayoutParams(lp);
- setMaxColumns(4);
+ setMaxColumns(6);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index a8e88da..84b4d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -34,6 +34,11 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.leak.RotationUtils;
+import com.android.systemui.settings.brightness.BrightnessController;
+import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
+import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
import java.util.List;
@@ -47,6 +52,11 @@
public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
private final Provider<Boolean> mUsingCollapsedLandscapeMediaProvider;
+ private final BrightnessController mBrightnessController;
+ private final TunerService mTunerService;
+ private final BrightnessSliderController mBrightnessSliderController;
+ private final BrightnessMirrorHandler mBrightnessMirrorHandler;
+ private BrightnessMirrorController mBrightnessMirrorController;
@Inject
QuickQSPanelController(QuickQSPanel view, QSHost qsHost,
@@ -56,11 +66,21 @@
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
Provider<Boolean> usingCollapsedLandscapeMediaProvider,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, SplitShadeStateController splitShadeStateController
+ DumpManager dumpManager, SplitShadeStateController splitShadeStateController,
+ TunerService tunerService,
+ BrightnessController.Factory brightnessControllerFactory,
+ BrightnessSliderController.Factory brightnessSliderFactory
) {
super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager, splitShadeStateController);
mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
+ mTunerService = tunerService;
+
+ mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
+ mView.setBrightnessView(mBrightnessSliderController.getRootView());
+
+ mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
+ mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
}
@Override
@@ -69,6 +89,7 @@
updateMediaExpansion();
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+ mBrightnessSliderController.init();
}
private void updateMediaExpansion() {
@@ -91,25 +112,70 @@
@Override
protected void onViewAttached() {
super.onViewAttached();
+
+ mTunerService.addTunable(mView, QSPanel.QS_BRIGHTNESS_SLIDER_POSITION);
+ mTunerService.addTunable(mView, QSPanel.QS_SHOW_AUTO_BRIGHTNESS);
+ mTunerService.addTunable(mView, QSPanel.QS_SHOW_BRIGHTNESS_SLIDER);
+
+ mView.setBrightnessRunnable(() -> {
+ mView.updateResources();
+ updateBrightnessMirror();
+ });
+ mView.updateColumns();
+ mBrightnessMirrorHandler.onQsPanelAttached();
}
@Override
protected void onViewDetached() {
super.onViewDetached();
+ mTunerService.removeTunable(mView);
+ mView.setBrightnessRunnable(null);
+ mBrightnessMirrorHandler.onQsPanelDettached();
+ }
+
+ private void updateBrightnessMirror() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessSliderController.setMirrorControllerAndMirror(mBrightnessMirrorController);
+ }
+ }
+
+ @Override
+ void setListening(boolean listening) {
+ super.setListening(listening);
+
+ // Set the listening as soon as the QS fragment starts listening regardless of the
+ //expansion, so it will update the current brightness before the slider is visible.
+ if (listening) {
+ mBrightnessController.registerCallbacks();
+ } else {
+ mBrightnessController.unregisterCallbacks();
+ }
+ }
+
+ public boolean isListening() {
+ return mView.isListening();
}
private void setMaxTiles(int parseNumTiles) {
mView.setMaxTiles(parseNumTiles);
+ mView.updateColumns();
setTiles();
}
@Override
+ public void refreshAllTiles() {
+ mBrightnessController.checkRestrictionAndSetEnabled();
+ super.refreshAllTiles();
+ }
+
+ @Override
protected void onConfigurationChanged() {
int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
if (newMaxTiles != mView.getNumQuickTiles()) {
setMaxTiles(newMaxTiles);
}
updateMediaExpansion();
+ mView.updateColumns();
}
@Override
@@ -131,4 +197,9 @@
public int getNumQuickTiles() {
return mView.getNumQuickTiles();
}
+
+ public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
+ mBrightnessMirrorController = brightnessMirrorController;
+ mBrightnessMirrorHandler.setController(brightnessMirrorController);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 9d4eba5..72aa28a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -1,6 +1,7 @@
package com.android.systemui.qs;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
import android.content.Context;
import android.content.res.Resources;
@@ -103,6 +104,11 @@
return updateColumns();
}
+ @Override
+ public int getMaxColumns() {
+ return mMaxColumns;
+ }
+
public void addTile(TileRecord tile) {
mRecords.add(tile);
tile.tile.setListening(this, mListening);
@@ -132,7 +138,7 @@
Resources res = getResources();
int columns = useSmallLandscapeLockscreenResources()
? res.getInteger(R.integer.small_land_lockscreen_quick_settings_num_columns)
- : res.getInteger(R.integer.quick_settings_num_columns);
+ : res.getInteger(R.integer.quick_qs_panel_max_tiles);
mResourceColumns = Math.max(1, columns);
mResourceCellHeight = res.getDimensionPixelSize(mResourceCellHeightResId);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
@@ -170,9 +176,12 @@
return true;
}
- private boolean updateColumns() {
+ public boolean updateColumns() {
int oldColumns = mColumns;
- mColumns = Math.min(mResourceColumns, mMaxColumns);
+ if (isRoundQS())
+ mColumns = mMaxColumns;
+ else
+ mColumns = Math.min(mResourceColumns, mMaxColumns);
return oldColumns != mColumns;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
index edc16be..b8461dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
@@ -20,6 +20,7 @@
import android.text.TextUtils
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import com.android.systemui.util.qs.QSStyleUtils.isRoundQS
/** Class for displaying tiles in [QSCustomizer] with the new design (labels on the side). */
class CustomizeTileView(context: Context) : QSTileViewImpl(context, collapsed = false) {
@@ -47,7 +48,7 @@
return if (showAppLabel && !TextUtils.isEmpty(text)) {
VISIBLE
} else {
- GONE
+ if (isRoundQS()) INVISIBLE else GONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index f8d4080..26d3c52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -23,6 +23,7 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
+import android.view.ContextThemeWrapper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -70,13 +71,15 @@
public QSCustomizer(Context context, AttributeSet attrs) {
super(context, attrs);
- LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
+ Context themedContext =
+ new ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings);
+ LayoutInflater.from(themedContext).inflate(R.layout.qs_customize_panel_content, this);
mClipper = new QSDetailClipper(findViewById(R.id.customize_container));
mToolbar = findViewById(com.android.internal.R.id.action_bar);
TypedValue value = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
+ themedContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
mToolbar.setNavigationIcon(
- getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ getResources().getDrawable(value.resourceId, themedContext.getTheme()));
mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, com.android.internal.R.string.reset)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
@@ -310,4 +313,4 @@
mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, com.android.internal.R.string.reset)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e5af8e6..c80d549 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs.customize;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQS;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -22,10 +24,13 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.provider.Settings;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
+import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -84,7 +89,7 @@
private static final int ACTION_ADD = 1;
private static final int ACTION_MOVE = 2;
- private static final int NUM_COLUMNS_ID = R.integer.quick_settings_num_columns;
+ private static final int NUM_COLUMNS_ID = R.integer.qs_panel_num_columns;
private final Context mContext;
@@ -144,6 +149,11 @@
mSizeLookup.setSpanIndexCacheEnabled(true);
mTempTextView = new TextView(context);
mMinTileViewHeight = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+
+ if (isRoundQS()) {
+ mNumColumns = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.QS_NUM_COLUMNS, mNumColumns);
+ }
}
@Override
@@ -445,6 +455,22 @@
if (position == mFocusIndex) {
focusOnHolder(holder);
}
+ holder.mTileView.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ int position = holder.getLayoutPosition();
+ if (position < mEditIndex) {
+ if (canRemoveTiles()) {
+ move(position, mEditIndex, true);
+ }
+ } else {
+ move(position, mEditIndex, true);
+ }
+ }
+ return false;
+ }
+ });
}
private void focusOnHolder(Holder holder) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 35cac4b..7041ea7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -38,6 +38,9 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -122,6 +125,12 @@
private boolean mShowingDetail;
private int mIsFullQs;
+ private final boolean mHasVibrator;
+ private final Vibrator mVibrator;
+ private final VibratorManager mVibratorManager;
+ private static final VibrationEffect QS_TILE_TOUCH_HAPTIC =
+ VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
/**
@@ -202,6 +211,10 @@
resetStates();
mUiHandler.post(() -> mLifecycle.setCurrentState(CREATED));
+
+ mVibratorManager = (VibratorManager) mContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
+ mVibrator = mVibratorManager.getDefaultVibrator();
+ mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
}
protected final void resetStates() {
@@ -283,6 +296,7 @@
}
public void click(@Nullable View view) {
+ if (mHasVibrator) mVibrator.vibrate(QS_TILE_TOUCH_HAPTIC);
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
.addTaggedData(FIELD_STATUS_BAR_STATE,
mStatusBarStateController.getState())));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 9fefcbd..e95ad2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tileimpl
+import android.animation.AnimatorSet
import android.animation.ArgbEvaluator
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
@@ -28,7 +29,9 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
+import android.graphics.drawable.GradientDrawable
import android.os.Trace
+import android.provider.Settings
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
@@ -50,6 +53,7 @@
import com.android.systemui.FontSizeUtils
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
+import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.AdapterState
@@ -57,6 +61,8 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import com.android.systemui.res.R
+import com.android.systemui.util.qs.QSStyleUtils.isRoundQS
+import com.android.wm.shell.animation.Interpolators
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -73,6 +79,7 @@
private const val CHEVRON_NAME = "chevron"
private const val OVERLAY_NAME = "overlay"
const val UNAVAILABLE_ALPHA = 0.3f
+ const val INACTIVE_ALPHA = 0.8f
@VisibleForTesting
internal const val TILE_STATE_RES_PREFIX = "tile_states_"
}
@@ -99,8 +106,10 @@
}
private val colorActive = Utils.getColorAttrDefaultColor(context, R.attr.shadeActive)
- private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
- private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
+ private val colorOffstate = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
+ private val colorInactive = if (isRoundQS()) Utils.applyAlpha(INACTIVE_ALPHA, colorOffstate)
+ else colorOffstate
+ private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive)
private val overlayColorActive = Utils.applyAlpha(
/* alpha= */ 0.11f,
@@ -109,18 +118,25 @@
/* alpha= */ 0.08f,
Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive))
- private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
- private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
+ private val colorLabelActive = Utils.getColorAttrDefaultColor(context,
+ if (isRoundQS()) R.attr.onShadeInactive
+ else R.attr.onShadeActive)
+ private val colorLabelInactive =
+ Utils.getColorAttrDefaultColor(context, if (isRoundQS()) R.attr.onShadeInactiveVariant
+ else R.attr.onShadeInactive)
private val colorLabelUnavailable =
Utils.getColorAttrDefaultColor(context, R.attr.outline)
private val colorSecondaryLabelActive =
- Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant)
+ Utils.getColorAttrDefaultColor(context, if (isRoundQS()) R.attr.onShadeInactiveVariant
+ else R.attr.onShadeActiveVariant)
private val colorSecondaryLabelInactive =
- Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
+ Utils.getColorAttrDefaultColor(context, if (isRoundQS()) R.attr.outline
+ else R.attr.onShadeInactiveVariant)
private val colorSecondaryLabelUnavailable =
Utils.getColorAttrDefaultColor(context, R.attr.outline)
+ private lateinit var iconContainer: LinearLayout
private lateinit var label: TextView
protected lateinit var secondaryLabel: TextView
private lateinit var labelContainer: IgnorableChildLinearLayout
@@ -135,15 +151,24 @@
protected var showRippleEffect = true
private lateinit var ripple: RippleDrawable
- private lateinit var backgroundDrawable: LayerDrawable
+ private lateinit var backgroundDrawable: Drawable
private lateinit var backgroundBaseDrawable: Drawable
private lateinit var backgroundOverlayDrawable: Drawable
private var backgroundColor: Int = 0
private var backgroundOverlayColor: Int = 0
-
+ private var paintColor: Int = 0
+ private var radiusActive: Float = 0f
+ private var radiusInactive: Float = 0f
+ private val shapeAnimator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = QS_ANIM_LENGTH
+ interpolator = Interpolators.FAST_OUT_SLOW_IN
+ addUpdateListener { animation ->
+ setCornerRadius(animation.animatedValue as Float)
+ }
+ }
private val singleAnimator: ValueAnimator = ValueAnimator().apply {
- setDuration(QS_ANIM_LENGTH)
+ duration = QS_ANIM_LENGTH
addUpdateListener { animation ->
setAllColors(
// These casts will throw an exception if some property is missing. We should
@@ -157,6 +182,10 @@
}
}
+ private val tileAnimator = AnimatorSet().apply {
+ playTogether(singleAnimator, shapeAnimator)
+ }
+
private var accessibilityClass: String? = null
private var stateDescriptionDeltas: CharSequence? = null
private var lastStateDescription: CharSequence? = null
@@ -177,24 +206,50 @@
"Theme.SystemUI.QuickSettings")
}
setId(generateViewId())
- orientation = LinearLayout.HORIZONTAL
- gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ if (isRoundQS()) {
+ orientation = LinearLayout.VERTICAL
+ gravity = Gravity.CENTER
+ } else {
+ orientation = LinearLayout.HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ }
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES
clipChildren = false
clipToPadding = false
isFocusable = true
- background = createTileBackground()
+ background = if (isRoundQS()) null else createTileBackground()
+
+ if (isRoundQS()) {
+ val iconContainerSize = context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+ radiusActive = iconContainerSize / 2f
+ radiusInactive = iconContainerSize / 4f
+ iconContainer = LaunchableLinearLayout(context)
+ iconContainer.layoutParams = LayoutParams(iconContainerSize, iconContainerSize)
+ iconContainer.clipChildren = false
+ iconContainer.clipToPadding = false
+ iconContainer.orientation = LinearLayout.VERTICAL
+ iconContainer.gravity = Gravity.CENTER
+ iconContainer.background = createTileBackground()
+ }
+
setColor(getBackgroundColorForState(QSTile.State.DEFAULT_STATE))
- val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
- val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
- setPaddingRelative(startPadding, padding, padding, padding)
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- val iconSize = resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- addView(icon, LayoutParams(iconSize, iconSize))
+ if (isRoundQS()) {
+ setCornerRadius(getCornerRadiusForState(QSTile.State.DEFAULT_STATE))
+ iconContainer.addView(icon, LayoutParams(iconSize, iconSize))
+ addView(iconContainer, 0)
+ } else {
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
+ setPaddingRelative(startPadding, padding, padding, padding)
+ addView(icon, LayoutParams(iconSize, iconSize))
+ }
createAndAddLabels()
createAndAddSideView()
+ updateResources()
}
override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -221,6 +276,14 @@
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
+ if (isRoundQS()) {
+ updateRoundQSResources()
+ } else {
+ updateDefaultResources()
+ }
+ }
+
+ fun updateDefaultResources() {
val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
icon.layoutParams.apply {
height = iconSize
@@ -255,19 +318,56 @@
setOverlayColor(backgroundOverlayColor)
}
+ fun updateRoundQSResources() {
+ labelContainer.invalidate()
+ labelContainer.apply {
+ ignoreLastView = collapsed
+ forceUnspecifiedMeasure = collapsed
+ }
+ secondaryLabel.alpha = if (collapsed) 0f else 1f
+
+ orientation = LinearLayout.VERTICAL
+ gravity = Gravity.CENTER
+
+ val iconContainerSize = context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+ iconContainer.layoutParams.apply {
+ height = iconContainerSize
+ width = iconContainerSize
+ }
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ icon.layoutParams.apply {
+ height = iconSize
+ width = iconSize
+ }
+ iconContainer.setPaddingRelative(padding, padding, padding, padding)
+ (labelContainer.layoutParams as MarginLayoutParams).apply {
+ topMargin = padding / 2
+ }
+ }
+
private fun createAndAddLabels() {
labelContainer = LayoutInflater.from(context)
- .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
+ .inflate(if (isRoundQS()) R.layout.qs_tile_label_round else R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
label = labelContainer.requireViewById(R.id.tile_label)
secondaryLabel = labelContainer.requireViewById(R.id.app_label)
- if (collapsed) {
- labelContainer.ignoreLastView = true
- // Ideally, it'd be great if the parent could set this up when measuring just this child
- // instead of the View class having to support this. However, due to the mysteries of
- // LinearLayout's double measure pass, we cannot overwrite `measureChild` or any of its
- // sibling methods to have special behavior for labelContainer.
- labelContainer.forceUnspecifiedMeasure = true
- secondaryLabel.alpha = 0f
+ if (isRoundQS()) {
+ labelContainer.invalidate()
+ labelContainer.apply {
+ ignoreLastView = collapsed
+ forceUnspecifiedMeasure = collapsed
+ }
+ secondaryLabel.alpha = if (collapsed) 0f else 1f
+ } else {
+ if (collapsed) {
+ labelContainer.ignoreLastView = true
+ // Ideally, it'd be great if the parent could set this up when measuring just this child
+ // instead of the View class having to support this. However, due to the mysteries of
+ // LinearLayout's double measure pass, we cannot overwrite `measureChild` or any of its
+ // sibling methods to have special behavior for labelContainer.
+ labelContainer.forceUnspecifiedMeasure = true
+ secondaryLabel.alpha = 0f
+ }
}
setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
@@ -276,7 +376,7 @@
private fun createAndAddSideView() {
sideView = LayoutInflater.from(context)
- .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
+ .inflate(if (isRoundQS()) R.layout.qs_tile_side_icon_round else R.layout.qs_tile_side_icon, this, false) as ViewGroup
customDrawableView = sideView.requireViewById(R.id.customDrawable)
chevronView = sideView.requireViewById(R.id.chevron)
setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
@@ -284,13 +384,18 @@
}
fun createTileBackground(): Drawable {
- ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- backgroundDrawable = ripple.findDrawableByLayerId(R.id.background) as LayerDrawable
- backgroundBaseDrawable =
- backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_base)
- backgroundOverlayDrawable =
- backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_overlay)
- backgroundOverlayDrawable.mutate().setTintMode(PorterDuff.Mode.SRC)
+ if (isRoundQS()) {
+ ripple = mContext.getDrawable(R.drawable.qs_tile_background_no_mask) as RippleDrawable
+ backgroundDrawable = ripple.findDrawableByLayerId(R.id.background) as GradientDrawable
+ } else {
+ ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ backgroundDrawable = ripple.findDrawableByLayerId(R.id.background) as LayerDrawable
+ backgroundBaseDrawable =
+ (backgroundDrawable as LayerDrawable).findDrawableByLayerId(R.id.qs_tile_background_base)
+ backgroundOverlayDrawable =
+ (backgroundDrawable as LayerDrawable).findDrawableByLayerId(R.id.qs_tile_background_overlay)
+ backgroundOverlayDrawable.mutate().setTintMode(PorterDuff.Mode.SRC)
+ }
return ripple
}
@@ -322,7 +427,7 @@
}
override fun getIconWithBackground(): View {
- return icon
+ return if (isRoundQS()) iconContainer else icon
}
override fun init(tile: QSTile) {
@@ -366,14 +471,26 @@
override fun setClickable(clickable: Boolean) {
super.setClickable(clickable)
- background = if (clickable && showRippleEffect) {
- ripple.also {
- // In case that the colorBackgroundDrawable was used as the background, make sure
- // it has the correct callback instead of null
- backgroundDrawable.callback = it
+ if (isRoundQS()) {
+ iconContainer.background = if (clickable && showRippleEffect) {
+ ripple.also {
+ // In case that the backgroundDrawable was used as the background, make sure
+ // it has the correct callback instead of null
+ backgroundDrawable.callback = it
+ }
+ } else {
+ backgroundDrawable
}
} else {
- backgroundDrawable
+ background = if (clickable && showRippleEffect) {
+ ripple.also {
+ // In case that the backgroundDrawable was used as the background, make sure
+ // it has the correct callback instead of null
+ backgroundDrawable.callback = it
+ }
+ } else {
+ backgroundDrawable
+ }
}
}
@@ -397,6 +514,10 @@
launchableViewDelegate.setShouldBlockVisibilityChanges(block)
}
+ override fun getAnimatedView(): LaunchableView {
+ return if (isRoundQS()) getIconWithBackground() as LaunchableView else this
+ }
+
override fun setVisibility(visibility: Int) {
launchableViewDelegate.setVisibility(visibility)
}
@@ -520,7 +641,7 @@
if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) {
secondaryLabel.text = state.secondaryLabel
secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) {
- GONE
+ if (isRoundQS()) INVISIBLE else GONE
} else {
VISIBLE
}
@@ -528,13 +649,23 @@
// Colors
if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
- singleAnimator.cancel()
+ if (isRoundQS()) {
+ tileAnimator.cancel()
+ } else {
+ singleAnimator.cancel()
+ }
mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
state.spec,
state.state,
state.disabledByPolicy,
getBackgroundColorForState(state.state, state.disabledByPolicy))
if (allowAnimations) {
+ if (isRoundQS()) {
+ shapeAnimator.setFloatValues(
+ (backgroundDrawable as GradientDrawable).cornerRadius,
+ getCornerRadiusForState(state.state)
+ )
+ }
singleAnimator.setValues(
colorValuesHolder(
BACKGROUND_NAME,
@@ -562,7 +693,11 @@
getOverlayColorForState(state.state)
)
)
- singleAnimator.start()
+ if (isRoundQS()) {
+ tileAnimator.start()
+ } else {
+ singleAnimator.start()
+ }
} else {
setAllColors(
getBackgroundColorForState(state.state, state.disabledByPolicy),
@@ -571,6 +706,9 @@
getChevronColorForState(state.state, state.disabledByPolicy),
getOverlayColorForState(state.state)
)
+ if (isRoundQS()) {
+ setCornerRadius(getCornerRadiusForState(state.state))
+ }
}
}
@@ -598,7 +736,11 @@
}
private fun setColor(color: Int) {
- backgroundBaseDrawable.mutate().setTint(color)
+ if (isRoundQS()) {
+ backgroundDrawable.mutate().setTint(color)
+ } else {
+ backgroundBaseDrawable.mutate().setTint(color)
+ }
backgroundColor = color
}
@@ -615,8 +757,10 @@
}
private fun setOverlayColor(overlayColor: Int) {
- backgroundOverlayDrawable.setTint(overlayColor)
- backgroundOverlayColor = overlayColor
+ if (!isRoundQS()) {
+ backgroundOverlayDrawable.setTint(overlayColor)
+ backgroundOverlayColor = overlayColor
+ }
}
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
@@ -640,6 +784,27 @@
return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
}
+ private fun setCornerRadius(cornerRadius: Float) {
+ val mBg = ripple.findDrawableByLayerId(R.id.background) as GradientDrawable
+ mBg.cornerRadius = cornerRadius
+ }
+
+ private fun getCornerRadiusForState(state: Int): Float {
+ var qsTileShape : Int = Settings.Secure.getInt(context.contentResolver, Settings.Secure.QS_TILE_SHAPE, 2)
+ if (qsTileShape == 0)
+ return radiusActive
+
+ if (qsTileShape == 1)
+ return radiusInactive
+
+ return when (state) {
+ Tile.STATE_ACTIVE -> radiusActive
+ Tile.STATE_INACTIVE -> radiusInactive
+ Tile.STATE_UNAVAILABLE -> radiusInactive
+ else -> radiusInactive
+ }
+ }
+
/*
* The view should not be animated if it's not on screen and no part of it is visible.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 17251c3..d76b758 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -50,6 +50,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.GlobalSettings;
import dagger.Lazy;
@@ -57,10 +58,9 @@
import javax.inject.Inject;
/** Quick settings tile: Airplane mode **/
-public class AirplaneModeTile extends QSTileImpl<BooleanState> {
+public class AirplaneModeTile extends SecureQSTile<BooleanState> {
public static final String TILE_SPEC = "airplane";
-
private final SettingObserver mSetting;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Lazy<ConnectivityManager> mLazyConnectivityManager;
@@ -81,10 +81,11 @@
BroadcastDispatcher broadcastDispatcher,
Lazy<ConnectivityManager> lazyConnectivityManager,
GlobalSettings globalSettings,
- UserTracker userTracker
+ UserTracker userTracker,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mBroadcastDispatcher = broadcastDispatcher;
mLazyConnectivityManager = lazyConnectivityManager;
@@ -103,7 +104,11 @@
}
@Override
- public void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
boolean airplaneModeEnabled = mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !airplaneModeEnabled);
if (!airplaneModeEnabled && TelephonyProperties.in_ecm_mode().orElse(false)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 426aa55..412a254 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -42,11 +42,12 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
-public class BatterySaverTile extends QSTileImpl<BooleanState> implements
+public class BatterySaverTile extends SecureQSTile<BooleanState> implements
BatteryController.BatteryStateChangeCallback {
public static final String TILE_SPEC = "battery";
@@ -72,10 +73,11 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
BatteryController batteryController,
- SecureSettings secureSettings
+ SecureSettings secureSettings,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
@@ -131,7 +133,11 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
if (getState().state == Tile.STATE_UNAVAILABLE) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 18d2f30..ceee8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -54,6 +54,7 @@
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.List;
import java.util.concurrent.Executor;
@@ -61,7 +62,7 @@
import javax.inject.Inject;
/** Quick settings tile: Bluetooth **/
-public class BluetoothTile extends QSTileImpl<BooleanState> {
+public class BluetoothTile extends SecureQSTile<BooleanState> {
public static final String TILE_SPEC = "bt";
@@ -92,10 +93,11 @@
QSLogger qsLogger,
BluetoothController bluetoothController,
FeatureFlags featureFlags,
- BluetoothTileDialogViewModel dialogViewModel
+ BluetoothTileDialogViewModel dialogViewModel,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mController = bluetoothController;
mController.observe(getLifecycle(), mCallback);
mExecutor = new HandlerExecutor(mainHandler);
@@ -109,7 +111,11 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
mDialogViewModel.showDialog(mContext, view);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java
new file mode 100644
index 0000000..28806c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ * Copyright (c) 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.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.service.quicksettings.Tile;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Caffeine **/
+public class CaffeineTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "caffeine";
+
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_caffeine);
+
+ private final PowerManager.WakeLock mWakeLock;
+ private int mSecondsRemaining;
+ private int mDuration;
+ private static int[] DURATIONS = new int[] {
+ 5 * 60, // 5 min
+ 10 * 60, // 10 min
+ 30 * 60, // 30 min
+ -1, // infinity
+ };
+ private CountDownTimer mCountdownTimer = null;
+ public long mLastClickTime = -1;
+ private final Receiver mReceiver = new Receiver();
+
+ @Inject
+ public CaffeineTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mWakeLock = mContext.getSystemService(PowerManager.class).newWakeLock(
+ PowerManager.FULL_WAKE_LOCK, "CaffeineTile");
+ mReceiver.init();
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ stopCountDown();
+ mReceiver.destroy();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ @Override
+ public void handleClick(@Nullable View view) {
+ // If last user clicks < 5 seconds
+ // we cycle different duration
+ // otherwise toggle on/off
+ if (mWakeLock.isHeld() && (mLastClickTime != -1) &&
+ (SystemClock.elapsedRealtime() - mLastClickTime < 5000)) {
+ // cycle duration
+ mDuration++;
+ if (mDuration >= DURATIONS.length) {
+ // all durations cycled, turn if off
+ mDuration = -1;
+ stopCountDown();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ } else {
+ // change duration
+ startCountDown(DURATIONS[mDuration]);
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ }
+ } else {
+ // toggle
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ stopCountDown();
+ } else {
+ mWakeLock.acquire();
+ mDuration = 0;
+ startCountDown(DURATIONS[mDuration]);
+ }
+ }
+ mLastClickTime = SystemClock.elapsedRealtime();
+ refreshState();
+ }
+
+ @Override
+ protected void handleLongClick(@Nullable View view) {
+ // Set duration to infinity on long click
+ int infinityIndex = DURATIONS.length - 1;
+ if (mLastClickTime == infinityIndex) {
+ // Already at infinity
+ return;
+ }
+ mDuration = infinityIndex;
+ startCountDown(DURATIONS[mDuration]);
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ mLastClickTime = SystemClock.elapsedRealtime();
+ refreshState();
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_caffeine_label);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_CUSTOM;
+ }
+
+ private void startCountDown(long duration) {
+ stopCountDown();
+ mSecondsRemaining = (int)duration;
+ if (duration == -1) {
+ // infinity timing, no need to start timer
+ return;
+ }
+ mCountdownTimer = new CountDownTimer(duration * 1000, 1000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ mSecondsRemaining = (int) (millisUntilFinished / 1000);
+ refreshState();
+ }
+
+ @Override
+ public void onFinish() {
+ if (mWakeLock.isHeld())
+ mWakeLock.release();
+ refreshState();
+ }
+
+ }.start();
+ }
+
+ private void stopCountDown() {
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ }
+
+ private String formatValueWithRemainingTime() {
+ if (mSecondsRemaining == -1) {
+ return "\u221E"; // infinity
+ }
+ return String.format("%02d:%02d",
+ mSecondsRemaining / 60 % 60, mSecondsRemaining % 60);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.value = mWakeLock.isHeld();
+ state.icon = mIcon;
+ state.label = mContext.getString(R.string.quick_settings_caffeine_label);
+ if (state.value) {
+ state.secondaryLabel = formatValueWithRemainingTime();
+ state.contentDescription = mContext.getString(
+ R.string.accessibility_quick_settings_caffeine_on);
+ state.state = Tile.STATE_ACTIVE;
+ } else {
+ state.secondaryLabel = null;
+ state.contentDescription = mContext.getString(
+ R.string.accessibility_quick_settings_caffeine_off);
+ state.state = Tile.STATE_INACTIVE;
+ }
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+ public void init() {
+ // Register for Intent broadcasts for...
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(this, filter, null, mHandler);
+ }
+
+ public void destroy() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ // disable caffeine if user force off (power button)
+ stopCountDown();
+ if (mWakeLock.isHeld())
+ mWakeLock.release();
+ refreshState();
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
new file mode 100644
index 0000000..c11d65a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -0,0 +1,294 @@
+/*
+ * 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.systemui.qs.tiles;
+
+import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import android.telephony.SubscriptionManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Cellular **/
+public class CellularTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "cell";
+
+ private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
+
+ private final NetworkController mController;
+ private final DataUsageController mDataController;
+ private final KeyguardStateController mKeyguard;
+ private final CellSignalCallback mSignalCallback = new CellSignalCallback();
+
+ @Inject
+ public CellularTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController,
+ KeyguardStateController keyguardStateController
+
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mController = networkController;
+ mKeyguard = keyguardStateController;
+ mDataController = mController.getMobileDataController();
+ mController.observe(getLifecycle(), mSignalCallback);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ if (getState().state == Tile.STATE_UNAVAILABLE) {
+ return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ }
+ return getCellularSettingIntent();
+ }
+
+ @Override
+ protected void handleClick(@Nullable View view) {
+ if (getState().state == Tile.STATE_UNAVAILABLE) {
+ return;
+ }
+ if (mDataController.isMobileDataEnabled()) {
+ maybeShowDisableDialog();
+ } else {
+ mDataController.setMobileDataEnabled(true);
+ }
+ }
+
+ private void maybeShowDisableDialog() {
+ if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
+ // Directly turn off mobile data if the user has seen the dialog before.
+ mDataController.setMobileDataEnabled(false);
+ return;
+ }
+ String carrierName = mController.getMobileDataNetworkName();
+ boolean isInService = mController.isMobileDataNetworkInService();
+ if (TextUtils.isEmpty(carrierName) || !isInService) {
+ carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ }
+ AlertDialog dialog = new Builder(mContext)
+ .setTitle(R.string.mobile_data_disable_title)
+ .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(
+ com.android.internal.R.string.alert_windows_notification_turn_off_action,
+ (d, w) -> {
+ mDataController.setMobileDataEnabled(false);
+ Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+ })
+ .create();
+ dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ SystemUIDialog.registerDismissListener(dialog);
+ SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
+ dialog.show();
+ }
+
+ @Override
+ protected void handleSecondaryClick(@Nullable View view) {
+ handleLongClick(view);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_cellular_detail_title);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ CallbackInfo cb = (CallbackInfo) arg;
+ if (cb == null) {
+ cb = mSignalCallback.mInfo;
+ }
+
+ final Resources r = mContext.getResources();
+ state.label = r.getString(R.string.mobile_data);
+ boolean mobileDataEnabled = mDataController.isMobileDataSupported()
+ && mDataController.isMobileDataEnabled();
+ state.value = mobileDataEnabled;
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ if (cb.noSim) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
+ } else {
+ state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
+ }
+
+ if (cb.noSim) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ state.secondaryLabel = r.getString(com.android.systemui.res.R.string.keyguard_missing_sim_message_short);
+ } else if (cb.airplaneModeEnabled) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ state.secondaryLabel = r.getString(R.string.status_bar_airplane);
+ } else if (mobileDataEnabled) {
+ state.state = Tile.STATE_ACTIVE;
+ state.secondaryLabel = appendMobileDataType(
+ // Only show carrier name if there are more than 1 subscription
+ cb.multipleSubs ? cb.dataSubscriptionName : "",
+ getMobileDataContentName(cb));
+ } else {
+ state.state = Tile.STATE_INACTIVE;
+ state.secondaryLabel = r.getString(R.string.cell_data_off);
+ }
+
+ state.contentDescription = state.label;
+ if (state.state == Tile.STATE_INACTIVE) {
+ // This information is appended later by converting the Tile.STATE_INACTIVE state.
+ state.stateDescription = "";
+ } else {
+ state.stateDescription = state.secondaryLabel;
+ }
+ }
+
+ private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+ if (TextUtils.isEmpty(dataType)) {
+ return Html.fromHtml(current.toString(), 0);
+ }
+ if (TextUtils.isEmpty(current)) {
+ return Html.fromHtml(dataType.toString(), 0);
+ }
+ String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
+ return Html.fromHtml(concat, 0);
+ }
+
+ private CharSequence getMobileDataContentName(CallbackInfo cb) {
+ if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
+ String roaming = mContext.getString(R.string.data_connection_roaming);
+ String dataDescription = cb.dataContentDescription.toString();
+ return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
+ }
+ if (cb.roaming) {
+ return mContext.getString(R.string.data_connection_roaming);
+ }
+ return cb.dataContentDescription;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_CELLULAR;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mController.hasMobileDataFeature()
+ && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM;
+ }
+
+ private static final class CallbackInfo {
+ boolean airplaneModeEnabled;
+ @Nullable
+ CharSequence dataSubscriptionName;
+ @Nullable
+ CharSequence dataContentDescription;
+ boolean noSim;
+ boolean roaming;
+ boolean multipleSubs;
+ }
+
+ private final class CellSignalCallback implements SignalCallback {
+ private final CallbackInfo mInfo = new CallbackInfo();
+
+ @Override
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
+ if (indicators.qsIcon == null) {
+ // Not data sim, don't display.
+ return;
+ }
+ mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
+ mInfo.dataContentDescription = indicators.qsDescription != null
+ ? indicators.typeContentDescriptionHtml : null;
+ mInfo.roaming = indicators.roaming;
+ mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
+ refreshState(mInfo);
+ }
+
+ @Override
+ public void setNoSims(boolean show, boolean simDetected) {
+ mInfo.noSim = show;
+ refreshState(mInfo);
+ }
+
+ @Override
+ public void setIsAirplaneMode(@NonNull IconState icon) {
+ mInfo.airplaneModeEnabled = icon.visible;
+ refreshState(mInfo);
+ }
+ }
+
+ static Intent getCellularSettingIntent() {
+ Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (dataSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ intent.putExtra(Settings.EXTRA_SUB_ID,
+ SubscriptionManager.getDefaultDataSubscriptionId());
+ }
+ return intent;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java
new file mode 100644
index 0000000..67fe515
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019-2024 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.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quicksettings.Tile;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.res.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+
+import javax.inject.Inject;
+
+public class CompassTile extends QSTileImpl<BooleanState> implements SensorEventListener {
+
+ public static final String TILE_SPEC = "compass";
+
+ private final static float ALPHA = 0.97f;
+
+ private boolean mActive = false;
+
+ private SensorManager mSensorManager;
+ private Sensor mAccelerationSensor;
+ private Sensor mGeomagneticFieldSensor;
+
+ private float[] mAcceleration;
+ private float[] mGeomagnetic;
+
+ private boolean mListeningSensors;
+
+ @Inject
+ public CompassTile(QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mAccelerationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mGeomagneticFieldSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ BooleanState state = new BooleanState();
+ state.handlesLongClick = false;
+ return state;
+ }
+
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ setListeningSensors(false);
+ mSensorManager = null;
+ }
+
+ @Override
+ protected void handleClick(@Nullable View view) {
+ mActive = !mActive;
+ refreshState();
+ setListeningSensors(mActive);
+ }
+
+ @Override
+ public void handleLongClick(@Nullable View view) {
+ handleClick(view);
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ private void setListeningSensors(boolean listening) {
+ if (listening == mListeningSensors) return;
+ mListeningSensors = listening;
+ if (mListeningSensors) {
+ mSensorManager.registerListener(
+ this, mAccelerationSensor, SensorManager.SENSOR_DELAY_GAME);
+ mSensorManager.registerListener(
+ this, mGeomagneticFieldSensor, SensorManager.SENSOR_DELAY_GAME);
+ } else {
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_compass_label);
+ }
+
+ private Drawable rotateDrawable(Drawable drawable, float degrees) {
+ // Convert drawable to bitmap
+ Bitmap bitmap = drawableToBitmap(drawable);
+
+ // Create matrix for rotation
+ Matrix matrix = new Matrix();
+ matrix.postRotate(degrees);
+
+ // Create rotated bitmap
+ Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+
+ // Convert rotated bitmap back to drawable
+ return new BitmapDrawable(mContext.getResources(), rotatedBitmap);
+ }
+
+ private Bitmap drawableToBitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+
+ // If the drawable is not a BitmapDrawable, create a new bitmap and draw the drawable on a canvas
+ Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ final Float degrees = arg == null ? 0 : (Float) arg;
+
+ state.value = mActive;
+
+ if (state.value) {
+ state.state = Tile.STATE_ACTIVE;
+ if (arg != null) {
+ state.label = formatValueWithCardinalDirection(degrees);
+ } else {
+ state.label = mContext.getString(R.string.quick_settings_compass_init);
+ }
+ } else {
+ state.label = mContext.getString(R.string.quick_settings_compass_label);
+ state.state = Tile.STATE_INACTIVE;
+ }
+ state.icon = new DrawableIcon(rotateDrawable(
+ mContext.getResources().getDrawable(R.drawable.ic_qs_compass), degrees));
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_CUSTOM;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mSensorManager != null && mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null
+ && mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null;
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ if (!listening) {
+ setListeningSensors(false);
+ mActive = false;
+ }
+ }
+
+ private String formatValueWithCardinalDirection(float degree) {
+ int cardinalDirectionIndex = (int) (Math.floor(((degree - 22.5) % 360) / 45) + 1) % 8;
+ String[] cardinalDirections = mContext.getResources().getStringArray(
+ R.array.cardinal_directions);
+
+ return mContext.getString(R.string.quick_settings_compass_value, degree,
+ cardinalDirections[cardinalDirectionIndex]);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float[] values;
+ if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+ if (mAcceleration == null) {
+ mAcceleration = event.values.clone();
+ }
+
+ values = mAcceleration;
+ } else {
+ // Magnetic field sensor
+ if (mGeomagnetic == null) {
+ mGeomagnetic = event.values.clone();
+ }
+
+ values = mGeomagnetic;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ values[i] = ALPHA * values[i] + (1 - ALPHA) * event.values[i];
+ }
+
+ if (!mActive || !mListeningSensors || mAcceleration == null || mGeomagnetic == null) {
+ // Nothing to do at this moment
+ return;
+ }
+
+ float R[] = new float[9];
+ float I[] = new float[9];
+ if (!SensorManager.getRotationMatrix(R, I, mAcceleration, mGeomagnetic)) {
+ // Rotation matrix couldn't be calculated
+ return;
+ }
+
+ // Get the current orientation
+ float[] orientation = new float[3];
+ SensorManager.getOrientation(R, orientation);
+
+ // Convert azimuth to degrees
+ Float newDegree = Float.valueOf((float) Math.toDegrees(orientation[0]));
+ newDegree = (newDegree + 360) % 360;
+
+ refreshState(newDegree);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // noop
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 58630a0..2f21caa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -43,10 +43,11 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
-public class DataSaverTile extends QSTileImpl<BooleanState> implements
+public class DataSaverTile extends SecureQSTile<BooleanState> implements
DataSaverController.Listener{
public static final String TILE_SPEC = "saver";
@@ -70,10 +71,11 @@
QSLogger qsLogger,
DataSaverController dataSaverController,
DialogTransitionAnimator dialogTransitionAnimator,
- SystemUIDialog.Factory systemUIDialogFactory
+ SystemUIDialog.Factory systemUIDialogFactory,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mDataSaverController = dataSaverController;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSystemUIDialogFactory = systemUIDialogFactory;
@@ -90,7 +92,11 @@
return new Intent(Settings.ACTION_DATA_SAVER_SETTINGS);
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
if (mState.value
|| Prefs.getBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, false)) {
// Do it right away.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java
new file mode 100644
index 0000000..87a2f21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java
@@ -0,0 +1,254 @@
+package com.android.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.systemui.R;
+import com.android.systemui.SysUIToast;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+public class DataSwitchTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "dataswitch";
+ private final SubscriptionManager mSubscriptionManager;
+ private final TelephonyManager mTelephonyManager;
+
+ BroadcastReceiver mSimReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "mSimReceiver:onReceive");
+ refreshState();
+ }
+ };
+
+ private boolean mCanSwitch = true;
+
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ mCanSwitch = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE;
+ refreshState();
+ }
+ };
+
+ private boolean mRegistered = false;
+ private int mSimCount = 0;
+
+ @Inject
+ public DataSwitchTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BroadcastDispatcher broadcastDispatcher,
+ KeyguardStateController keyguardStateController
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mSubscriptionManager = SubscriptionManager.from(host.getContext());
+ mTelephonyManager = TelephonyManager.from(host.getContext());
+ }
+
+ @Override
+ public boolean isAvailable() {
+ int count = TelephonyManager.getDefault().getPhoneCount();
+ Log.d(TAG, "phoneCount: " + count);
+ return count >= 2;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ if (listening) {
+ if (!mRegistered) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ mContext.registerReceiver(mSimReceiver, filter);
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ mRegistered = true;
+ }
+ refreshState();
+ } else if (mRegistered) {
+ mContext.unregisterReceiver(mSimReceiver);
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mRegistered = false;
+ }
+ }
+
+ private void updateSimCount() {
+ String simState = SystemProperties.get("gsm.sim.state");
+ Log.d(TAG, "DataSwitchTile:updateSimCount:simState=" + simState);
+ mSimCount = 0;
+ try {
+ String[] sims = TextUtils.split(simState, ",");
+ for (String sim : sims) {
+ if (!sim.isEmpty() && !sim.equalsIgnoreCase(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT) && !sim.equalsIgnoreCase(
+ IccCardConstants.INTENT_VALUE_ICC_NOT_READY)) {
+ mSimCount++;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error to parse sim state");
+ }
+ Log.d(TAG, "DataSwitchTile:updateSimCount:mSimCount=" + mSimCount);
+ }
+
+ @Override
+ public void handleClick(@Nullable View view) {
+ if (!mCanSwitch) {
+ Log.d(TAG, "Call state=" + mTelephonyManager.getCallState());
+ } else if (mSimCount == 0) {
+ Log.d(TAG, "handleClick:no sim card");
+ SysUIToast.makeText(mContext, mContext.getString(R.string.qs_data_switch_toast_0),
+ Toast.LENGTH_LONG).show();
+ } else if (mSimCount == 1) {
+ Log.d(TAG, "handleClick:only one sim card");
+ SysUIToast.makeText(mContext, mContext.getString(R.string.qs_data_switch_toast_1),
+ Toast.LENGTH_LONG).show();
+ } else {
+ AsyncTask.execute(() -> {
+ toggleMobileDataEnabled();
+ refreshState();
+ });
+ }
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.qs_data_switch_label);
+ }
+
+ public int getDefaultDataPhoneId() {
+ return mSubscriptionManager.getPhoneId(mSubscriptionManager.getDefaultDataSubscriptionId());
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ boolean activeSIMZero;
+ if (arg == null) {
+ int defaultPhoneId = getDefaultDataPhoneId();
+ Log.d(TAG, "default data phone id=" + defaultPhoneId);
+ activeSIMZero = defaultPhoneId == 0;
+ } else {
+ activeSIMZero = (Boolean) arg;
+ }
+ updateSimCount();
+ switch (mSimCount) {
+ case 1:
+ state.icon = ResourceIcon.get(activeSIMZero ? R.drawable.ic_qs_data_switch_1
+ : R.drawable.ic_qs_data_switch_2);
+ state.secondaryLabel = mContext.getString(
+ activeSIMZero ? R.string.qs_data_switch_text_1
+ : R.string.qs_data_switch_text_2);
+ state.value = false;
+ break;
+ case 2:
+ state.icon = ResourceIcon.get(activeSIMZero ? R.drawable.ic_qs_data_switch_1
+ : R.drawable.ic_qs_data_switch_2);
+ state.secondaryLabel = mContext.getString(
+ activeSIMZero ? R.string.qs_data_switch_text_1
+ : R.string.qs_data_switch_text_2);
+ state.value = true;
+ break;
+ default:
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_1);
+ state.secondaryLabel = mContext.getString(R.string.qs_data_switch_text_1);
+ state.value = false;
+ break;
+ }
+
+ if (mSimCount < 2) {
+ state.state = 0;
+ } else if (!mCanSwitch) {
+ state.state = 0;
+ Log.d(TAG, "call state isn't idle, set to unavailable.");
+ } else {
+ state.state = state.value ? 2 : 1;
+ }
+
+ state.label = mContext.getString(R.string.qs_data_switch_label);
+ state.contentDescription = mContext.getString(
+ activeSIMZero ? R.string.qs_data_switch_changed_1
+ : R.string.qs_data_switch_changed_2);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return -1;
+ }
+
+ /**
+ * Set whether to enable data for {@code subId}, also whether to disable data for other
+ * subscription
+ */
+ private void toggleMobileDataEnabled() {
+ // Get opposite slot 2 ^ 3 = 1, 1 ^ 3 = 2
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId() ^ 3;
+ final TelephonyManager telephonyManager =
+ mTelephonyManager.createForSubscriptionId(subId);
+ telephonyManager.setDataEnabled(true);
+ mSubscriptionManager.setDefaultDataSubId(subId);
+ Log.d(TAG, "Enabled subID: " + subId);
+
+ List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(
+ true);
+ if (subInfoList != null) {
+ for (SubscriptionInfo subInfo : subInfoList) {
+ // We never disable mobile data for opportunistic subscriptions.
+ if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
+ mTelephonyManager.createForSubscriptionId(
+ subInfo.getSubscriptionId()).setDataEnabled(false);
+ Log.d(TAG, "Disabled subID: " + subInfo.getSubscriptionId());
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 9ee417e..af5dc32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -46,11 +46,12 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
/** Quick settings tile: Hotspot **/
-public class HotspotTile extends QSTileImpl<BooleanState> {
+public class HotspotTile extends SecureQSTile<BooleanState> {
public static final String TILE_SPEC = "hotspot";
private final HotspotController mHotspotController;
@@ -71,10 +72,11 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
HotspotController hotspotController,
- DataSaverController dataSaverController
+ DataSaverController dataSaverController,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mHotspotController.observe(this, mCallbacks);
@@ -112,7 +114,11 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
final boolean isEnabled = mState.value;
if (!isEnabled && mDataSaverController.isDataSaverEnabled()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 24b2d8a..f6a4836 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -53,6 +53,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -66,7 +67,7 @@
import javax.inject.Inject;
/** Quick settings tile: Internet **/
-public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
+public class InternetTile extends SecureQSTile<QSTile.BooleanState> {
public static final String TILE_SPEC = "internet";
@@ -99,10 +100,11 @@
QSLogger qsLogger,
NetworkController networkController,
AccessPointController accessPointController,
- InternetDialogManager internetDialogManager
+ InternetDialogManager internetDialogManager,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mInternetDialogManager = internetDialogManager;
mHandler = mainHandler;
mController = networkController;
@@ -124,7 +126,10 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
mHandler.post(() -> mInternetDialogManager.create(true,
mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi(), view));
@@ -303,6 +308,9 @@
if (DEBUG) {
Log.d(TAG, "setWifiIndicators: " + indicators);
}
+ if (!indicators.isDefault) {
+ return;
+ }
synchronized (mWifiInfo) {
mWifiInfo.mEnabled = indicators.enabled;
mWifiInfo.mSsid = indicators.description;
@@ -328,7 +336,7 @@
if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: " + indicators);
}
- if (indicators.qsIcon == null) {
+ if (indicators.qsIcon == null || !indicators.isDefault) {
// Not data sim, don't display.
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index a239c28..223c7b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -46,11 +46,12 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
/** Quick settings tile: Enable/Disable NFC **/
-public class NfcTile extends QSTileImpl<BooleanState> {
+public class NfcTile extends SecureQSTile<BooleanState> {
public static final String TILE_SPEC = "nfc";
@@ -74,10 +75,11 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- BroadcastDispatcher broadcastDispatcher
+ BroadcastDispatcher broadcastDispatcher,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mBroadcastDispatcher = broadcastDispatcher;
}
@@ -89,6 +91,7 @@
@Override
public void handleSetListening(boolean listening) {
super.handleSetListening(listening);
+ if (mListening == listening) return;
mListening = listening;
if (mListening) {
mBroadcastDispatcher.registerReceiver(mNfcReceiver,
@@ -119,7 +122,11 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
if (getAdapter() == null) {
return;
}
@@ -154,11 +161,7 @@
private NfcAdapter getAdapter() {
if (mAdapter == null) {
- try {
- mAdapter = NfcAdapter.getDefaultAdapter(mContext);
- } catch (UnsupportedOperationException e) {
- mAdapter = null;
- }
+ mAdapter = NfcAdapter.getDefaultAdapter(mContext.getApplicationContext());
}
return mAdapter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/PowerShareTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/PowerShareTile.java
new file mode 100644
index 0000000..b7a3f1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/PowerShareTile.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2020 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.systemui.qs.tiles;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.quicksettings.Tile;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import vendor.lineage.powershare.V1_0.IPowerShare;
+
+import java.util.NoSuchElementException;
+
+import javax.inject.Inject;
+
+public class PowerShareTile extends QSTileImpl<BooleanState>
+ implements BatteryController.BatteryStateChangeCallback {
+
+ public static final String TILE_SPEC = "powershare";
+
+ private IPowerShare mPowerShare;
+ private BatteryController mBatteryController;
+ private NotificationManager mNotificationManager;
+ private Notification mNotification;
+ private static final String CHANNEL_ID = TILE_SPEC;
+ private static final int NOTIFICATION_ID = 273298;
+
+ @Inject
+ public PowerShareTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BatteryController batteryController) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mPowerShare = getPowerShare();
+ if (mPowerShare == null) {
+ return;
+ }
+
+ mBatteryController = batteryController;
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,
+ mContext.getString(R.string.quick_settings_powershare_label),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ mNotificationManager.createNotificationChannel(notificationChannel);
+
+ Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID);
+ builder.setContentTitle(
+ mContext.getString(R.string.quick_settings_powershare_enabled_label));
+ builder.setSmallIcon(R.drawable.ic_qs_powershare);
+ builder.setOnlyAlertOnce(true);
+ mNotification = builder.build();
+ mNotification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
+ mNotification.visibility = Notification.VISIBILITY_PUBLIC;
+
+ batteryController.addCallback(this);
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ refreshState();
+ }
+
+ @Override
+ public void refreshState() {
+ updatePowerShareState();
+
+ super.refreshState();
+ }
+
+ private void updatePowerShareState() {
+ if (!isAvailable()) {
+ return;
+ }
+
+ if (mBatteryController.isPowerSave()) {
+ try {
+ mPowerShare.setEnabled(false);
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ try {
+ if (mPowerShare.isEnabled()) {
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification);
+ } else {
+ mNotificationManager.cancel(NOTIFICATION_ID);
+ }
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mPowerShare != null;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ BooleanState state = new BooleanState();
+ state.handlesLongClick = false;
+ return state;
+ }
+
+ @Override
+ public void handleClick(@Nullable View view) {
+ try {
+ boolean powerShareEnabled = mPowerShare.isEnabled();
+
+ if (mPowerShare.setEnabled(!powerShareEnabled) != powerShareEnabled) {
+ refreshState();
+ }
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ if (mBatteryController.isPowerSave()) {
+ return mContext.getString(R.string.quick_settings_powershare_off_powersave_label);
+ } else {
+ if (getBatteryLevel() < getMinBatteryLevel()) {
+ return mContext.getString(R.string.quick_settings_powershare_off_low_battery_label);
+ }
+ }
+
+ return mContext.getString(R.string.quick_settings_powershare_label);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ if (!isAvailable()) {
+ return;
+ }
+
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_powershare);
+ try {
+ state.value = mPowerShare.isEnabled();
+ } catch (RemoteException ex) {
+ state.value = false;
+ ex.printStackTrace();
+ }
+ state.label = mContext.getString(R.string.quick_settings_powershare_label);
+
+ if (mBatteryController.isPowerSave() || getBatteryLevel() < getMinBatteryLevel()) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ } else if (!state.value) {
+ state.state = Tile.STATE_INACTIVE;
+ } else {
+ state.state = Tile.STATE_ACTIVE;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return NOTIFICATION_ID;
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ }
+
+ private synchronized IPowerShare getPowerShare() {
+ try {
+ return IPowerShare.getService();
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ } catch (NoSuchElementException ex) {
+ // service not available
+ }
+
+ return null;
+ }
+
+ private int getMinBatteryLevel() {
+ try {
+ return mPowerShare.getMinBattery();
+ } catch (RemoteException ex) {
+ ex.printStackTrace();
+ }
+
+ return 0;
+ }
+
+ private int getBatteryLevel() {
+ BatteryManager bm = mContext.getSystemService(BatteryManager.class);
+ return bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index f1d8f9f..51ee3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -49,6 +49,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
import com.android.systemui.util.settings.SecureSettings;
@@ -56,7 +57,7 @@
import javax.inject.Inject;
/** Quick settings tile: Rotation **/
-public class RotationLockTile extends QSTileImpl<BooleanState> implements
+public class RotationLockTile extends SecureQSTile<BooleanState> implements
BatteryController.BatteryStateChangeCallback {
public static final String TILE_SPEC = "rotation";
@@ -84,10 +85,11 @@
RotationLockController rotationLockController,
SensorPrivacyManager privacyManager,
BatteryController batteryController,
- SecureSettings secureSettings
+ SecureSettings secureSettings,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mController = rotationLockController;
mController.observe(this, mCallback);
mPrivacyManager = privacyManager;
@@ -131,7 +133,11 @@
}
@Override
- protected void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
final boolean newState = !mState.value;
mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick");
refreshState(newState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SecureQSTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/SecureQSTile.kt
new file mode 100644
index 0000000..4dcd2304
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SecureQSTile.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.systemui.qs.tiles
+
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.QSHost
+import com.android.systemui.plugins.FalsingManager
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController
+
+internal abstract class SecureQSTile<TState : QSTile.State> protected constructor(
+ host: QSHost, uiEventLogger: QsEventLogger, backgroundLooper: Looper, mainHandler: Handler,
+ falsingManager: FalsingManager, metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter, qsLogger: QSLogger,
+ private val keyguardController: KeyguardStateController,
+) : QSTileImpl<TState>(
+ host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger,
+) {
+ abstract override fun newTileState(): TState
+
+ protected abstract fun handleClick(view: View?, keyguardShowing: Boolean)
+
+ override fun handleClick(view: View?) {
+ handleClick(view, keyguardController.isMethodSecure && keyguardController.isShowing)
+ }
+
+ protected fun checkKeyguard(view: View?, keyguardShowing: Boolean): Boolean {
+ return if (keyguardShowing) {
+ mActivityStarter.postQSRunnableDismissingKeyguard {
+ handleClick(view, false)
+ }
+ true
+ } else {
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
new file mode 100644
index 0000000..a2e1bc4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -0,0 +1,271 @@
+/*
+ * 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.systemui.qs.tiles;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSIconViewImpl;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.WifiIcons;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Wifi **/
+public class WifiTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "wifi";
+
+ private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+
+ protected final NetworkController mController;
+ private final AccessPointController mWifiController;
+ private final QSTile.BooleanState mStateBeforeClick = newTileState();
+
+ protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
+ private boolean mExpectDisabled;
+
+ @Inject
+ public WifiTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController,
+ AccessPointController accessPointController
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mController = networkController;
+ mWifiController = accessPointController;
+ mController.observe(getLifecycle(), mSignalCallback);
+ mStateBeforeClick.spec = "wifi";
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return WIFI_SETTINGS;
+ }
+
+ @Override
+ protected void handleClick(@Nullable View view) {
+ // Secondary clicks are header clicks, just toggle.
+ mState.copyTo(mStateBeforeClick);
+ boolean wifiEnabled = mState.value;
+ // Immediately enter transient state when turning on wifi.
+ refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
+ mController.setWifiEnabled(!wifiEnabled);
+ mExpectDisabled = wifiEnabled;
+ if (mExpectDisabled) {
+ mHandler.postDelayed(() -> {
+ if (mExpectDisabled) {
+ mExpectDisabled = false;
+ refreshState();
+ }
+ }, QSIconViewImpl.QS_ANIM_LENGTH);
+ }
+ }
+
+ @Override
+ protected void handleSecondaryClick(@Nullable View view) {
+ if (!mWifiController.canConfigWifi()) {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ new Intent(Settings.ACTION_WIFI_SETTINGS), 0);
+ return;
+ }
+ if (!mState.value) {
+ mController.setWifiEnabled(true);
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_wifi_label);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
+ final CallbackInfo cb = mSignalCallback.mInfo;
+ if (mExpectDisabled) {
+ if (cb.enabled) {
+ return; // Ignore updates until disabled event occurs.
+ } else {
+ mExpectDisabled = false;
+ }
+ }
+ boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
+ boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0)
+ && (cb.ssid != null || cb.wifiSignalIconId != WifiIcons.QS_WIFI_NO_NETWORK);
+ boolean wifiNotConnected = (cb.ssid == null)
+ && (cb.wifiSignalIconId == WifiIcons.QS_WIFI_NO_NETWORK);
+ boolean isTransient = transientEnabling || cb.isTransient;
+ state.secondaryLabel = getSecondaryLabel(isTransient, cb.statusLabel);
+ state.state = Tile.STATE_ACTIVE;
+ state.dualTarget = true;
+ state.value = transientEnabling || cb.enabled;
+ final StringBuffer minimalContentDescription = new StringBuffer();
+ final StringBuffer minimalStateDescription = new StringBuffer();
+ final Resources r = mContext.getResources();
+ if (isTransient) {
+ state.icon = ResourceIcon.get(
+ com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
+ state.label = r.getString(R.string.quick_settings_wifi_label);
+ } else if (!state.value) {
+ state.state = Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
+ state.label = r.getString(R.string.quick_settings_wifi_label);
+ } else if (wifiConnected) {
+ state.icon = ResourceIcon.get(cb.wifiSignalIconId);
+ state.label = cb.ssid != null ? removeDoubleQuotes(cb.ssid) : getTileLabel();
+ } else if (wifiNotConnected) {
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+ state.label = r.getString(R.string.quick_settings_wifi_label);
+ } else {
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+ state.label = r.getString(R.string.quick_settings_wifi_label);
+ }
+ minimalContentDescription.append(
+ mContext.getString(R.string.quick_settings_wifi_label)).append(",");
+ if (state.value) {
+ if (wifiConnected) {
+ minimalStateDescription.append(cb.wifiSignalContentDescription);
+ minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
+ if (!TextUtils.isEmpty(state.secondaryLabel)) {
+ minimalContentDescription.append(",").append(state.secondaryLabel);
+ }
+ }
+ }
+ state.stateDescription = minimalStateDescription.toString();
+ state.contentDescription = minimalContentDescription.toString();
+ state.dualLabelContentDescription = r.getString(
+ R.string.accessibility_quick_settings_open_settings, getTileLabel());
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ }
+
+ private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+ return isTransient
+ ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
+ : statusLabel;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_WIFI;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ }
+
+ @Nullable
+ private static String removeDoubleQuotes(String string) {
+ if (string == null) return null;
+ final int length = string.length();
+ if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+ return string.substring(1, length - 1);
+ }
+ return string;
+ }
+
+ protected static final class CallbackInfo {
+ boolean enabled;
+ boolean connected;
+ int wifiSignalIconId;
+ @Nullable
+ String ssid;
+ @Nullable
+ String wifiSignalContentDescription;
+ boolean isTransient;
+ @Nullable
+ public String statusLabel;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("CallbackInfo[")
+ .append("enabled=").append(enabled)
+ .append(",connected=").append(connected)
+ .append(",wifiSignalIconId=").append(wifiSignalIconId)
+ .append(",ssid=").append(ssid)
+ .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
+ .append(",isTransient=").append(isTransient)
+ .append(']').toString();
+ }
+ }
+
+ protected final class WifiSignalCallback implements SignalCallback {
+ final CallbackInfo mInfo = new CallbackInfo();
+
+ @Override
+ public void setWifiIndicators(@NonNull WifiIndicators indicators) {
+ if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled);
+ if (indicators.qsIcon == null) {
+ return;
+ }
+ mInfo.enabled = indicators.enabled;
+ mInfo.connected = indicators.qsIcon.visible;
+ mInfo.wifiSignalIconId = indicators.qsIcon.icon;
+ mInfo.ssid = indicators.description;
+ mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription;
+ mInfo.isTransient = indicators.isTransient;
+ mInfo.statusLabel = indicators.statusLabel;
+ refreshState();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7a9384a..3028c54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -44,11 +44,12 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
/** Quick settings tile: Work profile on/off */
-public class WorkModeTile extends QSTileImpl<BooleanState> implements
+public class WorkModeTile extends SecureQSTile<BooleanState> implements
ManagedProfileController.Callback {
public static final String TILE_SPEC = "work";
@@ -69,10 +70,11 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- ManagedProfileController managedProfileController
+ ManagedProfileController managedProfileController,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mProfileController = managedProfileController;
mProfileController.observe(getLifecycle(), this);
}
@@ -88,7 +90,11 @@
}
@Override
- public void handleClick(@Nullable View view) {
+ protected void handleClick(@Nullable View view, boolean keyguardShowing) {
+ if (checkKeyguard(view, keyguardShowing)) {
+ return;
+ }
+
mProfileController.setWorkModeEnabled(!mState.value);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 1410473..4eb3d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -35,8 +35,10 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager;
+import android.net.INetworkPolicyListener;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
@@ -75,6 +77,7 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
+import com.android.systemui.Dependency;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -85,6 +88,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.AccessPointController;
+import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.toast.SystemUIToast;
@@ -194,6 +198,9 @@
private WifiStateWorker mWifiStateWorker;
private boolean mHasActiveSubId;
+ private final HotspotController mHotspotController;
+ private final NetworkPolicyManager mPolicyManager;
+
@VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@VisibleForTesting
@@ -234,6 +241,26 @@
}
};
+ private final HotspotController.Callback mHotspotCallback =
+ new HotspotController.Callback() {
+ @Override
+ public void onHotspotChanged(boolean enabled, int numDevices) {
+ mCallback.onHotspotChanged();
+ }
+
+ @Override
+ public void onHotspotAvailabilityChanged(boolean available) {
+ mCallback.onHotspotChanged();
+ }
+ };
+
+ private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
+ @Override
+ public void onRestrictBackgroundChanged(final boolean isDataSaving) {
+ mCallback.onHotspotChanged();
+ }
+ };
+
protected List<SubscriptionInfo> getSubscriptionInfo() {
return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo();
}
@@ -252,6 +279,7 @@
LocationController locationController,
DialogTransitionAnimator dialogTransitionAnimator,
WifiStateWorker wifiStateWorker,
+ HotspotController hotspotController,
FeatureFlags featureFlags
) {
if (DEBUG) {
@@ -286,6 +314,8 @@
mDialogTransitionAnimator = dialogTransitionAnimator;
mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
mWifiStateWorker = wifiStateWorker;
+ mHotspotController = hotspotController;
+ mPolicyManager = NetworkPolicyManager.from(context);
mFeatureFlags = featureFlags;
}
@@ -298,6 +328,8 @@
mAccessPointController.addAccessPointCallback(this);
mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
mExecutor);
+ mHotspotController.addCallback(mHotspotCallback);
+ mPolicyManager.registerListener(mPolicyListener);
// Listen the subscription changes
mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
refreshHasActiveSubId();
@@ -342,6 +374,7 @@
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
mConnectedWifiInternetMonitor.unregisterCallback();
+ mHotspotController.removeCallback(mHotspotCallback);
mCallback = null;
}
@@ -794,6 +827,18 @@
startActivity(intent, view);
}
+ void launchHotspotSetting(View view) {
+ final Intent intent = new Intent(Settings.ACTION_WIFI_TETHER_SETTING);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent, view);
+ }
+
+ void launchMobileNetworkSetting(View view) {
+ final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ intent.putExtra(Settings.EXTRA_SUB_ID, mDefaultDataSubId);
+ startActivity(intent, view);
+ }
+
/**
* Enable or disable Wi-Fi.
*
@@ -1028,6 +1073,30 @@
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
}
+ boolean isHotspotAvailable() {
+ return mHotspotController.isHotspotSupported();
+ }
+
+ boolean isHotspotEnabled() {
+ return mHotspotController.isHotspotEnabled();
+ }
+
+ boolean isHotspotTransient() {
+ return mHotspotController.isHotspotTransient();
+ }
+
+ int getHotspotNumDevices() {
+ return mHotspotController.getNumConnectedDevices();
+ }
+
+ void setHotspotEnabled(boolean enabled) {
+ mHotspotController.setHotspotEnabled(enabled);
+ }
+
+ boolean isDataSaverEnabled() {
+ return mPolicyManager.getRestrictBackground();
+ }
+
boolean connect(WifiEntry ap) {
if (ap == null) {
if (DEBUG) {
@@ -1390,6 +1459,8 @@
@Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
void onWifiScan(boolean isScan);
+
+ void onHotspotChanged();
}
void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 0dd0a60..8f6efd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -17,12 +17,15 @@
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.ServiceState;
@@ -38,6 +41,7 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
@@ -90,6 +94,7 @@
static final int MAX_NETWORK_COUNT = 4;
+ private final Context mContext;
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
@@ -118,6 +123,7 @@
private LinearLayout mSecondaryMobileNetworkLayout;
private LinearLayout mTurnWifiOnLayout;
private LinearLayout mEthernetLayout;
+ private LinearLayout mHotspotLayout;
private TextView mWifiToggleTitleText;
private LinearLayout mWifiScanNotifyLayout;
private TextView mWifiScanNotifyText;
@@ -133,7 +139,13 @@
private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
+ private View mMobileConnectedSpace;
+ private ImageView mHotspotIcon;
+ private TextView mHotspotTitleText;
+ private TextView mHotspotSummaryText;
+ private Switch mHotspotToggle;
private Switch mWiFiToggle;
+ private View mWifiConnectedSpace;
private Button mDoneButton;
@VisibleForTesting
@@ -182,6 +194,7 @@
@Background Executor executor,
KeyguardStateController keyguardStateController,
SystemUIDialog.Factory systemUIDialogFactory) {
+ mContext = context;
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
if (DEBUG) {
@@ -199,7 +212,6 @@
mCanConfigWifi = canConfigWifi;
mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
mKeyguard = keyguardStateController;
-
mUiEventLogger = uiEventLogger;
mDialogTransitionAnimator = dialogTransitionAnimator;
mAdapter = new InternetAdapter(mInternetDialogController);
@@ -245,6 +257,7 @@
mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
mEthernetLayout = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mHotspotLayout = mDialogView.requireViewById(R.id.hotspot_layout);
mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
mWifiScanNotifyLayout = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
@@ -265,12 +278,19 @@
mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+ mMobileConnectedSpace = mDialogView.requireViewById(R.id.mobile_connected_space);
+ mHotspotIcon = mDialogView.requireViewById(R.id.hotspot_icon);
+ mHotspotTitleText = mDialogView.requireViewById(R.id.hotspot_title);
+ mHotspotSummaryText = mDialogView.requireViewById(R.id.hotspot_summary);
+ mHotspotToggle = mDialogView.requireViewById(R.id.hotspot_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+ mWifiConnectedSpace = mDialogView.requireViewById(R.id.wifi_connected_space);
mBackgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
mBackgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect);
setOnClickListener(dialog);
+ setHotspotLayout();
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
@@ -305,6 +325,9 @@
Log.d(TAG, "onStop");
}
mMobileNetworkLayout.setOnClickListener(null);
+ mMobileNetworkLayout.setOnLongClickListener(null);
+ mHotspotLayout.setOnClickListener(null);
+ mHotspotToggle.setOnCheckedChangeListener(null);
mConnectedWifListLayout.setOnClickListener(null);
if (mSecondaryMobileNetworkLayout != null) {
mSecondaryMobileNetworkLayout.setOnClickListener(null);
@@ -335,8 +358,10 @@
*
* @param shouldUpdateMobileNetwork {@code true} for update the mobile network layout,
* otherwise {@code false}.
+ * @param shouldUpdateHotspot {@code true} for update the hotspot layout,
+ * otherwise {@code false}.
*/
- void updateDialog(boolean shouldUpdateMobileNetwork) {
+ void updateDialog(boolean shouldUpdateMobileNetwork, boolean shouldUpdateHotspot) {
if (DEBUG) {
Log.d(TAG, "updateDialog");
}
@@ -351,6 +376,10 @@
mInternetDialogController.isCarrierNetworkActive());
}
+ if (shouldUpdateHotspot) {
+ setHotspotLayout();
+ }
+
if (!mCanConfigWifi) {
return;
}
@@ -364,6 +393,10 @@
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
}
+ void updateDialog(boolean shouldUpdateMobileNetwork) {
+ updateDialog(shouldUpdateMobileNetwork, false /* shouldUpdateHotspot */);
+ }
+
private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
@@ -382,6 +415,13 @@
dialog.getContext(), mDefaultDataSubId, isChecked, false);
}
});
+ mMobileNetworkLayout.setOnLongClickListener(v -> {
+ mInternetDialogController.launchMobileNetworkSetting(v);
+ return true;
+ });
+ mHotspotLayout.setOnClickListener(mInternetDialogController::launchHotspotSetting);
+ mHotspotToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> mInternetDialogController.setHotspotEnabled(isChecked));
mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
mWiFiToggle.setOnCheckedChangeListener(
@@ -450,6 +490,8 @@
mSignalIcon.setImageDrawable(drawable);
});
});
+ mMobileConnectedSpace.setVisibility(
+ isNetworkConnected ? View.VISIBLE : View.GONE);
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
mMobileToggleDivider.setVisibility(
@@ -538,6 +580,26 @@
}
}
+ private void setHotspotLayout() {
+ if (!mInternetDialogController.isHotspotAvailable()) {
+ mHotspotLayout.setVisibility(View.GONE);
+ return;
+ }
+ mHotspotLayout.setVisibility(View.VISIBLE);
+ mHotspotTitleText.setText(getHotspotTitle());
+ mHotspotSummaryText.setText(getHotspotSummary());
+
+ boolean enabled = mInternetDialogController.isHotspotEnabled();
+ mHotspotIcon.setImageResource(enabled ? R.drawable.ic_internet_hotspot
+ : R.drawable.ic_internet_hotspot_disabled);
+ mHotspotToggle.setChecked(enabled);
+
+ boolean dataSaver = mInternetDialogController.isDataSaverEnabled();
+ mHotspotTitleText.setEnabled(!dataSaver);
+ mHotspotSummaryText.setEnabled(!dataSaver);
+ mHotspotToggle.setEnabled(!dataSaver);
+ }
+
@MainThread
private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
if (mWiFiToggle.isChecked() != isWifiEnabled) {
@@ -548,8 +610,15 @@
? R.style.TextAppearance_InternetDialog_Active
: R.style.TextAppearance_InternetDialog);
}
- mTurnWifiOnLayout.setBackground(
- (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+
+ boolean showBackground = isDeviceLocked && mConnectedWifiEntry != null;
+ ViewGroup.LayoutParams lp = mTurnWifiOnLayout.getLayoutParams();
+ lp.height = mContext.getResources().getDimensionPixelSize(
+ showBackground ? R.dimen.internet_dialog_wifi_network_height
+ : R.dimen.internet_dialog_wifi_toggle_height);
+ mTurnWifiOnLayout.setLayoutParams(lp);
+ mTurnWifiOnLayout.setBackground(showBackground ? mBackgroundOn : null);
+ mWifiConnectedSpace.setVisibility(showBackground ? View.VISIBLE : View.GONE);
if (!mCanChangeWifiState && mWiFiToggle.isEnabled()) {
mWiFiToggle.setEnabled(false);
@@ -684,6 +753,35 @@
return mInternetDialogController.getMobileNetworkSummary(subId);
}
+ private CharSequence getHotspotTitle() {
+ final WifiManager wifiManager = mInternetDialogController.getWifiManager();
+ if (wifiManager != null) {
+ final SoftApConfiguration softApConfig = wifiManager.getSoftApConfiguration();
+ if (softApConfig != null) {
+ return softApConfig.getSsid();
+ }
+ }
+ return mContext.getString(R.string.quick_settings_hotspot_label);
+ }
+
+ String getHotspotSummary() {
+ if (mInternetDialogController.isDataSaverEnabled()) {
+ return mContext.getString(
+ R.string.quick_settings_hotspot_secondary_label_data_saver_enabled);
+ } else if (mInternetDialogController.isHotspotTransient()) {
+ return mContext.getString(R.string.quick_settings_hotspot_secondary_label_transient);
+ } else if (mInternetDialogController.isHotspotEnabled()) {
+ int numDevices = mInternetDialogController.getHotspotNumDevices();
+ if (numDevices > 0) {
+ return mContext.getResources().getQuantityString(
+ R.plurals.quick_settings_internet_hotspot_summary_num_devices,
+ numDevices, numDevices);
+ }
+ return mContext.getString(R.string.switch_bar_on);
+ }
+ return mContext.getString(R.string.switch_bar_off);
+ }
+
private void setProgressBarVisible(boolean visible) {
if (mIsProgressBarVisible == visible) {
return;
@@ -846,6 +944,12 @@
}
@Override
+ public void onHotspotChanged() {
+ mHandler.post(() -> updateDialog(false /* shouldUpdateMobileNetwork */,
+ true /* shouldUpdateHotspot */));
+ }
+
+ @Override
public void onWindowFocusChanged(SystemUIDialog dialog, boolean hasFocus) {
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
if (!hasFocus && dialog.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5f8b5dd..81dd601 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -281,6 +281,12 @@
}
@Override
+ public void setTaskbarEnabled(boolean enabled) {
+ verifyCallerAndClearCallingIdentityPostMain("setTaskbarEnabled", () ->
+ onTaskbarEnabled(enabled));
+ }
+
+ @Override
public void notifyTaskbarStatus(boolean visible, boolean stashed) {
verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
onTaskbarStatusUpdated(visible, stashed));
@@ -888,6 +894,12 @@
}
}
+ private void onTaskbarEnabled(boolean enabled) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onTaskbarEnabled(enabled);
+ }
+ }
+
private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
@@ -1093,6 +1105,7 @@
/** Notify the recents app (overview) is started by 3-button navigation. */
default void onToggleRecentApps() {}
default void onHomeRotationEnabled(boolean enabled) {}
+ default void onTaskbarEnabled(boolean enabled) {}
default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onTaskbarAutohideSuspend(boolean suspend) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index bbf7ed5..eb1138b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -39,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.google.common.util.concurrent.ListenableFuture;
@@ -164,6 +166,7 @@
mImageData.quickShareAction = createQuickShareAction(
mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image,
mParams.owner);
+ mImageData.deleteAction = createDeleteAction(uri, smartActionsEnabled);
mImageData.subject = getSubjectString(mImageTime);
mParams.mActionsReadyListener.onActionsReady(mImageData);
@@ -285,6 +288,29 @@
.build();
}
+ @VisibleForTesting
+ Notification.Action createDeleteAction(Uri uri, boolean smartActionsEnabled) {
+ // Make sure pending intents for the system user are still unique across users
+ // by setting the (otherwise unused) request code to the current user id.
+ int requestCode = mContext.getUserId();
+
+ // Create a delete action for the notification
+ Intent wrappedIntent = new Intent(mContext, DeleteScreenshotReceiver.class)
+ .putExtra(ScreenshotController.SCREENSHOT_URI_ID, uri.toString())
+ .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+ .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
+ smartActionsEnabled)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ PendingIntent deleteAction = PendingIntent.getBroadcast(mContext, requestCode, wrappedIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE);
+ Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+ Icon.createWithResource(mContext.getResources(), R.drawable.ic_screenshot_delete),
+ mContext.getResources().getString(com.android.internal.R.string.delete), deleteAction);
+ return deleteActionBuilder.build();
+ }
+
private Intent createFillInIntent(Uri uri, long imageTime) {
Intent fillIn = new Intent();
fillIn.setType("image/png");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index ee3e7ba..938b786 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -168,6 +168,7 @@
static class SavedImageData {
public Uri uri;
public List<Notification.Action> smartActions;
+ public Notification.Action deleteAction;
public Notification.Action quickShareAction;
public UserHandle owner;
public String subject; // Title for sharing
@@ -179,6 +180,7 @@
uri = null;
smartActions = null;
quickShareAction = null;
+ deleteAction = null;
subject = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 7a62bae..ce1acd6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -54,6 +54,8 @@
SCREENSHOT_EDIT_TAPPED(308),
@UiEvent(doc = "screenshot share button tapped")
SCREENSHOT_SHARE_TAPPED(309),
+ @UiEvent(doc = "screenshot delete button tapped")
+ SCREENSHOT_DELETE_TAPPED(369),
@UiEvent(doc = "screenshot smart action chip tapped")
SCREENSHOT_SMART_ACTION_TAPPED(374),
@UiEvent(doc = "screenshot scroll tapped")
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index be30a15..fc18c72 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -153,6 +153,7 @@
private OverlayActionChip mEditChip;
private OverlayActionChip mScrollChip;
private OverlayActionChip mQuickShareChip;
+ private OverlayActionChip mDeleteChip;
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
@@ -175,6 +176,7 @@
private enum PendingInteraction {
PREVIEW,
EDIT,
+ DELETE,
SHARE,
QUICK_SHARE
}
@@ -382,6 +384,7 @@
mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip));
+ mDeleteChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_delete_chip));
setFocusable(true);
mActionsContainer.setScrollX(0);
@@ -691,6 +694,7 @@
if (mQuickShareChip != null) {
mQuickShareChip.setIsPending(false);
}
+ mDeleteChip.setIsPending(false);
mPendingInteraction = PendingInteraction.SHARE;
});
chips.add(mShareChip);
@@ -705,16 +709,31 @@
if (mQuickShareChip != null) {
mQuickShareChip.setIsPending(false);
}
+ mDeleteChip.setIsPending(false);
mPendingInteraction = PendingInteraction.EDIT;
});
chips.add(mEditChip);
+ mDeleteChip.setContentDescription(mContext.getString(R.string.screenshot_delete_label));
+ mDeleteChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_delete), true);
+ mDeleteChip.setOnClickListener(v -> {
+ mDeleteChip.setIsPending(true);
+ mEditChip.setIsPending(false);
+ mShareChip.setIsPending(false);
+ if (mQuickShareChip != null) {
+ mQuickShareChip.setIsPending(false);
+ }
+ mPendingInteraction = PendingInteraction.DELETE;
+ });
+ chips.add(mDeleteChip);
+
mScreenshotPreview.setOnClickListener(v -> {
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
if (mQuickShareChip != null) {
mQuickShareChip.setIsPending(false);
}
+ mDeleteChip.setIsPending(false);
mPendingInteraction = PendingInteraction.PREVIEW;
});
@@ -810,6 +829,12 @@
ActionIntentCreator.INSTANCE.createEdit(imageData.uri, mContext),
imageData.owner, true);
});
+ if (imageData.deleteAction != null) {
+ mDeleteChip.setPendingIntent(imageData.deleteAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_DELETE_TAPPED);
+ animateDismissal();
+ });
+ }
mScreenshotPreview.setOnClickListener(v -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED, 0, mPackageName);
prepareSharedTransition();
@@ -848,6 +873,9 @@
case EDIT:
mEditChip.callOnClick();
break;
+ case DELETE:
+ mDeleteChip.callOnClick();
+ break;
case QUICK_SHARE:
mQuickShareChip.callOnClick();
break;
@@ -890,6 +918,7 @@
mQuickShareChip.setOnClickListener(v -> {
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
+ mDeleteChip.setIsPending(false);
mQuickShareChip.setIsPending(true);
mPendingInteraction = PendingInteraction.QUICK_SHARE;
});
@@ -1077,8 +1106,10 @@
mShareChip.setOnClickListener(null);
mScrollingScrim.setVisibility(View.GONE);
mEditChip.setOnClickListener(null);
+ mDeleteChip.setOnClickListener(null);
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
+ mDeleteChip.setIsPending(false);
mPendingInteraction = null;
for (OverlayActionChip chip : mSmartChips) {
mActionsView.removeView(chip);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index e92630f..339fc58 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -36,11 +36,15 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.MathUtils;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
@@ -65,6 +69,7 @@
private static final String TAG = "CentralSurfaces.BrightnessController";
private static final int SLIDER_ANIMATION_DURATION = 1000;
+ private static final int MSG_UPDATE_ICON = 0;
private static final int MSG_UPDATE_SLIDER = 1;
private static final int MSG_ATTACH_LISTENER = 2;
private static final int MSG_DETACH_LISTENER = 3;
@@ -73,6 +78,7 @@
private static final Uri BRIGHTNESS_MODE_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ private final ImageView mIcon;
private final int mDisplayId;
private final Context mContext;
private final ToggleSlider mControl;
@@ -91,6 +97,7 @@
private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
@Override
public void onDisplayChanged(int displayId) {
+ if (mUserChangedBrightness) return;
mBackgroundHandler.post(mUpdateSliderRunnable);
}
};
@@ -104,6 +111,15 @@
private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
private ValueAnimator mSliderAnimator;
+ private boolean mUserChangedBrightness;
+
+ private final boolean mHasVibrator;
+ private final Vibrator mVibrator;
+ private final VibratorManager mVibratorManager;
+ private static final VibrationEffect BRIGHTNESS_SLIDER_HAPTIC =
+ VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ private static int mLastTrackingUpdate = 0;
@Override
public void setMirror(BrightnessMirrorController controller) {
@@ -216,6 +232,7 @@
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
mUserTracker.getUserId());
mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ mMainHandler.obtainMessage(MSG_UPDATE_ICON, mAutomatic ? 1 : 0).sendToTarget();
}
};
@@ -254,6 +271,9 @@
mExternalChange = true;
try {
switch (msg.what) {
+ case MSG_UPDATE_ICON:
+ updateIcon(msg.arg1 != 0);
+ break;
case MSG_UPDATE_SLIDER:
updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0);
break;
@@ -314,6 +334,17 @@
mMainHandler = new Handler(mainLooper, mHandlerCallback);
mBrightnessObserver = new BrightnessObserver(mMainHandler);
+
+ mVibratorManager = (VibratorManager) mContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
+ mVibrator = mVibratorManager.getDefaultVibrator();
+ mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+
+ mIcon = control.getIcon();
+ mIcon.setOnClickListener(v -> Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE, mAutomatic ?
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL :
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC,
+ UserHandle.USER_CURRENT));
}
public void registerCallbacks() {
@@ -330,6 +361,7 @@
@Override
public void onChanged(boolean tracking, int value, boolean stopTracking) {
+ updateIcon(mAutomatic);
if (mExternalChange) return;
if (mSliderAnimator != null) {
@@ -355,7 +387,15 @@
BrightnessSynchronizer.brightnessFloatToInt(valFloat));
}
+ mUserChangedBrightness = tracking && !stopTracking;
setBrightness(valFloat);
+
+ mLastTrackingUpdate = (mLastTrackingUpdate + 1) % 5;
+
+ // Give haptic feedback every 5 changes, only if brightness is changed manually
+ if (mHasVibrator && tracking && mLastTrackingUpdate == 0)
+ mVibrator.vibrate(BRIGHTNESS_SLIDER_HAPTIC);
+
if (!tracking) {
AsyncTask.execute(new Runnable() {
public void run() {
@@ -389,6 +429,14 @@
mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
}
+ private void updateIcon(boolean automatic) {
+ if (mIcon != null) {
+ mIcon.setImageResource(mAutomatic ?
+ com.android.systemui.R.drawable.ic_qs_brightness_auto_on :
+ com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
+ }
+ }
+
private void updateVrMode(boolean isEnabled) {
if (mIsVrModeEnabled != isEnabled) {
mIsVrModeEnabled = isEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 861a2ed..872bd2e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -23,6 +23,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.SeekBar;
import androidx.annotation.Nullable;
@@ -56,6 +57,7 @@
private Listener mListener;
private ToggleSlider mMirror;
+ private ImageView mIcon;
private BrightnessMirrorController mMirrorController;
private boolean mTracking;
private final FalsingManager mFalsingManager;
@@ -89,6 +91,7 @@
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mBrightnessSliderHapticPlugin = brightnessSliderHapticPlugin;
+ mIcon = mView.findViewById(R.id.brightness_icon);
}
/**
@@ -98,6 +101,9 @@
return mView;
}
+ public ImageView getIcon() {
+ return mIcon;
+ }
@Override
protected void onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index c43d20c..32165b1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -25,6 +25,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.SeekBar.OnSeekBarChangeListener;
import androidx.annotation.Keep;
@@ -41,7 +42,7 @@
* {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}.
*
*/
-public class BrightnessSliderView extends FrameLayout {
+public class BrightnessSliderView extends LinearLayout {
@NonNull
private ToggleSeekBar mSlider;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
index 648e33b..0789b81 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
@@ -17,6 +17,7 @@
package com.android.systemui.settings.brightness;
import android.view.MotionEvent;
+import android.widget.ImageView;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -39,4 +40,6 @@
void showView();
void hideView();
boolean isVisible();
+
+ ImageView getIcon();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index df9c57c..0ee2793 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -21,13 +21,20 @@
import android.annotation.IdRes
import android.app.PendingIntent
import android.app.StatusBarManager
+import android.content.Context
import android.content.Intent
+import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Insets
+import android.net.Uri
+import android.graphics.Color
import android.os.Bundle
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
+import android.os.VibrationEffect
+import android.os.Vibrator
import android.provider.AlarmClock
+import android.provider.CalendarContract
import android.view.DisplayCutout
import android.view.View
import android.view.WindowInsets
@@ -92,6 +99,7 @@
private val privacyIconsController: HeaderPrivacyIconsController,
private val insetsProvider: StatusBarContentInsetsProvider,
private val configurationController: ConfigurationController,
+ private val context: Context,
private val variableDateViewControllerFactory: VariableDateViewController.Factory,
@Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
private val dumpManager: DumpManager,
@@ -102,7 +110,7 @@
private val nextAlarmController: NextAlarmController,
private val activityStarter: ActivityStarter,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
-) : ViewController<View>(header), Dumpable {
+) : ViewController<View>(header), Dumpable, View.OnClickListener, View.OnLongClickListener {
companion object {
/** IDs for transitions and constraints for the [MotionLayout]. */
@@ -138,12 +146,15 @@
private val mShadeCarrierGroup: ShadeCarrierGroup = header.requireViewById(R.id.carrier_group)
private val systemIconsHoverContainer: View =
header.requireViewById(R.id.hover_system_icons_container)
+ private val vibrator: Vibrator = header.context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private var roundedCorners = 0
private var cutout: DisplayCutout? = null
private var lastInsets: WindowInsets? = null
private var nextAlarmIntent: PendingIntent? = null
+ private var textColorPrimary = Color.TRANSPARENT
+ private var privacyChipVisible = false
private var qsDisabled = false
private var visible = false
set(value) {
@@ -194,6 +205,7 @@
if (qsVisible && field != value) {
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
+ updateVisibility()
}
}
@@ -245,6 +257,8 @@
val update =
combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(visible)
header.updateAllConstraints(update)
+ privacyChipVisible = visible
+ setBatteryClickable(qsExpandedFraction == 1f || !visible)
}
}
@@ -289,6 +303,16 @@
updateCarrierGroupPadding()
clock.onDensityOrFontScaleChanged()
}
+
+ override fun onUiModeChanged() {
+ updateResources()
+ }
+
+ override fun onThemeChanged() {
+ clock.setTextAppearance(R.style.TextAppearance_QS_Status)
+ date.setTextAppearance(R.style.TextAppearance_QS_Status)
+ mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+ }
}
private val nextAlarmCallback =
@@ -315,6 +339,39 @@
shadeCarrierGroupControllerBuilder.setShadeCarrierGroup(mShadeCarrierGroup).build()
privacyIconsController.onParentVisible()
+
+ // click actions
+ clock.setOnClickListener(this)
+ date.setOnClickListener(this)
+ setBatteryClickable(true)
+ }
+
+ override fun onClick(v: View) {
+ if (v == clock) {
+ activityStarter.postStartActivityDismissingKeyguard(Intent(
+ AlarmClock.ACTION_SHOW_ALARMS), 0)
+ } else if (v == date) {
+ val builder: Uri.Builder = CalendarContract.CONTENT_URI.buildUpon()
+ builder.appendPath("time")
+ builder.appendPath(System.currentTimeMillis().toString())
+ val todayIntent: Intent = Intent(Intent.ACTION_VIEW, builder.build())
+ activityStarter.postStartActivityDismissingKeyguard(todayIntent, 0)
+ } else if (v == batteryIcon) {
+ activityStarter.postStartActivityDismissingKeyguard(Intent(
+ Intent.ACTION_POWER_USAGE_SUMMARY), 0)
+ }
+ }
+
+ override fun onLongClick(v: View): Boolean {
+ if (v == clock || v == date) {
+ val nIntent: Intent = Intent(Intent.ACTION_MAIN)
+ nIntent.setClassName("com.android.settings",
+ "com.android.settings.Settings\$DateTimeSettingsActivity")
+ activityStarter.startActivity(nIntent, true /* dismissShade */)
+ vibrator.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE))
+ return true
+ }
+ return false
}
override fun onViewAttached() {
@@ -337,6 +394,7 @@
demoModeController.addCallback(demoModeReceiver)
statusBarIconController.addIconGroup(iconManager)
nextAlarmController.addCallback(nextAlarmCallback)
+ updateResources()
systemIconsHoverContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createListener(systemIconsHoverContainer)
)
@@ -508,6 +566,7 @@
header.progress = qsExpandedFraction
updateBatteryMode()
}
+ setBatteryClickable(qsExpandedFraction == 1f || !privacyChipVisible)
}
private fun logInstantEvent(message: String) {
@@ -543,6 +602,25 @@
header.setPadding(padding, header.paddingTop, padding, header.paddingBottom)
updateQQSPaddings()
qsBatteryModeController.updateResources()
+
+ val fillColor = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ val fillColorInverse = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
+ iconManager.setTint(fillColor, fillColorInverse)
+ val textColor = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ val textColorInverse = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
+ val colorStateList = Utils.getColorAttr(context, android.R.attr.textColorPrimary)
+ if (textColor != textColorPrimary) {
+ val textColorSecondary = Utils.getColorAttrDefaultColor(context,
+ android.R.attr.textColorSecondary)
+ textColorPrimary = textColor
+ if (iconManager != null) {
+ iconManager.setTint(textColor, textColorInverse)
+ }
+ clock.setTextColor(textColorPrimary)
+ date.setTextColor(textColorPrimary)
+ mShadeCarrierGroup.updateColors(textColorPrimary, colorStateList)
+ batteryIcon.updateColors(textColorPrimary, textColorSecondary, textColorPrimary)
+ }
}
private fun updateQQSPaddings() {
@@ -558,6 +636,11 @@
)
}
+ private fun setBatteryClickable(clickable: Boolean) {
+ batteryIcon.setOnClickListener(if (clickable) this else null)
+ batteryIcon.setClickable(clickable)
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("visible: $visible")
pw.println("shadeExpanded: $qsVisible")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
index 6ca0ad4..69156a7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
@@ -32,7 +32,6 @@
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.FontSizeUtils;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
import com.android.systemui.util.LargeScreenUtils;
@@ -152,6 +151,11 @@
com.android.settingslib.R.string.not_default_data_content_description));
}
+ public void updateColors(ColorStateList colorStateList) {
+ mMobileRoaming.setImageTintList(colorStateList);
+ mMobileSignal.setImageTintList(colorStateList);
+ }
+
@VisibleForTesting
View getRSSIView() {
return mMobileGroup;
@@ -162,7 +166,10 @@
}
public void updateTextAppearance(@StyleRes int resId) {
- FontSizeUtils.updateFontSizeFromStyle(mCarrierText, resId);
+ mCarrierText.setTextAppearance(resId);
+ if (mModernMobileView != null) {
+ mModernMobileView.updateTextAppearance(resId);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
index e84fb48..0eabfc9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
@@ -18,12 +18,14 @@
import android.annotation.StyleRes;
import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.FontSizeUtils;
import com.android.systemui.res.R;
/**
@@ -58,8 +60,21 @@
return findViewById(R.id.shade_carrier_divider2);
}
+ public void updateColors(int color, ColorStateList colorStateList) {
+ getNoSimTextView().setTextColor(color);
+ ShadeCarrier[] shadeCarriers = { getCarrier1View(), getCarrier2View(), getCarrier3View() };
+ for (ShadeCarrier shadeCarrier : shadeCarriers) {
+ for (int i = 0; i < shadeCarrier.getChildCount(); i++) {
+ shadeCarrier.updateColors(colorStateList);
+ if (shadeCarrier.getChildAt(i) instanceof TextView) {
+ ((TextView) shadeCarrier.getChildAt(i)).setTextColor(color);
+ }
+ }
+ }
+ }
+
public void updateTextAppearance(@StyleRes int resId) {
- FontSizeUtils.updateFontSizeFromStyle(getNoSimTextView(), resId);
+ getNoSimTextView().setTextAppearance(resId);
getCarrier1View().updateTextAppearance(resId);
getCarrier2View().updateTextAppearance(resId);
getCarrier3View().updateTextAppearance(resId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index bb81683..5762ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -323,7 +323,7 @@
default void showPinningEnterExitToast(boolean entering) { }
default void showPinningEscapeToast() { }
default void handleShowGlobalActionsMenu() { }
- default void handleShowShutdownUi(boolean isReboot, String reason) { }
+ default void handleShowShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { }
default void showWirelessChargingAnimation(int batteryLevel) { }
@@ -1003,11 +1003,11 @@
}
@Override
- public void showShutdownUi(boolean isReboot, String reason) {
+ public void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_SHUTDOWN_UI);
- mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0, 0, reason)
- .sendToTarget();
+ mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0,
+ rebootCustom ? 1 : 0, reason).sendToTarget();
}
}
@@ -1637,7 +1637,8 @@
break;
case MSG_SHOW_SHUTDOWN_UI:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj);
+ mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj,
+ msg.arg2 != 0);
}
break;
case MSG_SET_TOP_APP_HIDES_STATUS_BAR:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java
index 80c3551..0bd208e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java
@@ -165,8 +165,7 @@
newNotification,
sbn.getUser(),
sbn.getOverrideGroupKey(),
- sbn.getPostTime());
+ sbn.getPostTime(),
+ sbn.getIsContentSecure());
}
-
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 64dfc6c..21bad0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -135,6 +135,15 @@
}
/**
+ * @see areAllPrimitivesSupported#hasVibrator()
+ */
+ public boolean areAllPrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+ return mVibrator != null &&
+ mVibrator.areAllPrimitivesSupported(primitiveIds);
+ }
+
+ /**
* @see Vibrator#cancel()
*/
public void cancel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index d6df987..fca43d57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -18,11 +18,15 @@
import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import android.content.Context;
+import android.content.ContentResolver;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.NetworkCapabilities;
+import android.net.Uri;
import android.os.Handler;
+import android.os.UserHandle;
import android.os.Looper;
+import android.provider.Settings;
import android.provider.Settings.Global;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
@@ -158,6 +162,37 @@
}
};
mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
+ Handler mHandler = new Handler();
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.observe();
+ }
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SHOW_FOURG_ICON), false,
+ this, UserHandle.USER_ALL);
+ updateSettings();
+ }
+
+ /*
+ * @hide
+ */
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ }
+ }
+
+ private void updateSettings() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mConfig = Config.readConfig(mContext);
+ setConfiguration(mConfig);
+ notifyListeners();
}
void setConfiguration(Config config) {
@@ -289,7 +324,8 @@
qsInfo.description,
mSubscriptionInfo.getSubscriptionId(),
mCurrentState.roaming,
- sbInfo.showTriangle);
+ sbInfo.showTriangle,
+ mCurrentState.isDefault);
callback.setMobileDataIndicators(mobileDataIndicators);
}
@@ -299,11 +335,6 @@
CharSequence qsDescription = null;
if (mCurrentState.dataSim) {
- // only show QS icons if the state is also default
- if (!mCurrentState.isDefault) {
- return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
- }
-
if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
qsTypeIcon = dataTypeIcon;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 6be407a..e9be3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -113,7 +113,8 @@
@JvmField val activityOut: Boolean,
@JvmField val description: String?,
@JvmField val isTransient: Boolean,
- @JvmField val statusLabel: String?
+ @JvmField val statusLabel: String?,
+ @JvmField val isDefault: Boolean
) {
override fun toString(): String {
return StringBuilder("WifiIndicators[")
@@ -125,6 +126,7 @@
.append(",qsDescription=").append(description)
.append(",isTransient=").append(isTransient)
.append(",statusLabel=").append(statusLabel)
+ .append(",isDefault=").append(isDefault)
.append(']').toString()
}
}
@@ -142,7 +144,8 @@
@JvmField val qsDescription: CharSequence?,
@JvmField val subId: Int,
@JvmField val roaming: Boolean,
- @JvmField val showTriangle: Boolean
+ @JvmField val showTriangle: Boolean,
+ @JvmField val isDefault: Boolean
) {
override fun toString(): String {
return java.lang.StringBuilder("MobileDataIndicators[")
@@ -158,6 +161,7 @@
.append(",subId=").append(subId)
.append(",roaming=").append(roaming)
.append(",showTriangle=").append(showTriangle)
+ .append(",isDefault=").append(isDefault)
.append(']').toString()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 08defcc..dcdd12c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -112,18 +112,16 @@
}
IconState statusIcon = new IconState(
wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = null;
- if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
- && !mNetworkController.isEthernetDefault())) {
- qsIcon = new IconState(mCurrentState.connected,
- mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
- : getQsCurrentIconId(), contentDescription);
- }
+ IconState qsIcon = new IconState(mCurrentState.connected,
+ mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
+ : getQsCurrentIconId(), contentDescription);
+ boolean isDefault = mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ && !mNetworkController.isEthernetDefault());
WifiIndicators wifiIndicators = new WifiIndicators(
mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn,
ssidPresent && mCurrentState.activityOut,
- wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
+ wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel, isDefault
);
callback.setWifiIndicators(wifiIndicators);
}
@@ -156,7 +154,8 @@
statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
dataContentDescriptionHtml, description,
- mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true
+ mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true,
+ /* isDefault= */ qsIcon != null
);
callback.setMobileDataIndicators(mobileDataIndicators);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index f03c313..9c6f933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -112,13 +112,14 @@
else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId)
}
}
-
+ val isSecure = entry.sbn.isContentSecure
val shouldProtectNotification = screenshareNotificationHiding() &&
sensitiveNotificationProtectionController.shouldProtectNotification(entry)
- val needsRedaction = lockscreenUserManager.needsRedaction(entry)
+ val needsRedaction = isSecure || lockscreenUserManager.needsRedaction(entry)
val isSensitive = userPublic && needsRedaction
- entry.setSensitive(isSensitive || shouldProtectNotification, deviceSensitive)
+ entry.setSensitive(isSensitive || shouldProtectNotification, isSecure || deviceSensitive)
+ entry.row.setForceHideContents(isSecure)
if (screenshareNotificationHiding()) {
entry.row?.setPublicExpanderVisible(!shouldProtectNotification)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 0b9d19d..46a774a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -122,7 +122,7 @@
isConversation = entry.ranking.isConversation,
isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
isMinimized = isEntryMinimized(entry),
- needsRedaction = lockscreenUserManager.needsRedaction(entry),
+ needsRedaction = entry.sbn.isContentSecure || lockscreenUserManager.needsRedaction(entry),
isChildInGroup = entry.sbn.isAppOrSystemGroupChild,
isGroupSummary = entry.sbn.isAppOrSystemGroupSummary,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index c5b55c7..1e19778 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -258,7 +258,8 @@
// inflated to avoid any latency associated with reinflating all notification views when
// screen share starts and stops
if (screenshareNotificationHiding()
- || mNotificationLockscreenUserManager.needsRedaction(entry)) {
+ || mNotificationLockscreenUserManager.needsRedaction(entry)
+ || entry.getSbn().getIsContentSecure()) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1bb5a77..6e6f5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -400,6 +400,8 @@
private float mBottomRoundnessDuringLaunchAnimation;
private float mSmallRoundness;
+ private boolean mForceHideContents = false;
+
public NotificationContentView[] getLayouts() {
return Arrays.copyOf(mLayouts, mLayouts.length);
}
@@ -2370,12 +2372,15 @@
private void updateChildrenVisibility() {
boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null
&& mGuts.isExposed();
- mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren
+ mPrivateLayout.setVisibility(!mForceHideContents
+ && !mShowingPublic
+ && !mIsSummaryWithChildren
&& !hideContentWhileLaunching ? VISIBLE : INVISIBLE);
if (mChildrenContainer != null) {
- mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren
- && !hideContentWhileLaunching ? VISIBLE
- : INVISIBLE);
+ mChildrenContainer.setVisibility(!mForceHideContents
+ && !mShowingPublic
+ && mIsSummaryWithChildren
+ && !hideContentWhileLaunching ? VISIBLE : INVISIBLE);
}
// The limits might have changed if the view suddenly became a group or vice versa
updateLimits();
@@ -2551,6 +2556,7 @@
}
public boolean isExpandable() {
+ if (mForceHideContents) return false;
if (mIsSummaryWithChildren && !shouldShowPublic()) {
return !mChildrenExpanded;
}
@@ -2697,6 +2703,7 @@
@Override
public int getIntrinsicHeight() {
+ if (mForceHideContents) return getCollapsedHeight();
if (isUserLocked()) {
return getActualHeight();
} else if (mGuts != null && mGuts.isExposed()) {
@@ -2926,7 +2933,7 @@
mChildrenContainer.animate().cancel();
}
resetAllContentAlphas();
- mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
+ mPublicLayout.setVisibility((mShowingPublic || mForceHideContents) ? VISIBLE : INVISIBLE);
updateChildrenVisibility();
if (NotificationContentAlphaOptimization.isEnabled()) {
// We want to set the old alpha to the now-showing layout to avoid breaking an
@@ -3008,7 +3015,7 @@
}
private boolean shouldShowPublic() {
- return mSensitive && mHideSensitiveForIntrinsicHeight;
+ return mForceHideContents || (mSensitive && mHideSensitiveForIntrinsicHeight);
}
public void makeActionsVisibile() {
@@ -3654,6 +3661,13 @@
}
}
+ public void setForceHideContents(boolean forceHide) {
+ if (mForceHideContents == forceHide) return;
+ mForceHideContents = forceHide;
+ updateChildrenVisibility();
+ onNotificationUpdated();
+ }
+
private static class NotificationViewState extends ExpandableViewState {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 302bdcc..21fcc07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -429,7 +429,7 @@
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
- mCarrierLabel.setTextAppearance(theme);
+ mCarrierLabel.setTextAppearance(R.style.TextAppearance_StatusBar_Clock);
mBatteryView.updatePercentView();
TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d975009..cc6a753 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -481,6 +481,28 @@
if (mBluetooth.isBluetoothConnected()
&& (mBluetooth.isBluetoothAudioActive()
|| !mBluetooth.isBluetoothAudioProfileOnly())) {
+ int batteryLevel = mBluetooth.getBatteryLevel();
+ if (batteryLevel == 100) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_9;
+ } else if (batteryLevel >= 90) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_8;
+ } else if (batteryLevel >= 80) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_7;
+ } else if (batteryLevel >= 70) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_6;
+ } else if (batteryLevel >= 60) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_5;
+ } else if (batteryLevel >= 50) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_4;
+ } else if (batteryLevel >= 40) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_3;
+ } else if (batteryLevel >= 30) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_2;
+ } else if (batteryLevel >= 20) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_1;
+ } else if (batteryLevel >= 10) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_0;
+ }
contentDescription = mResources.getString(
R.string.accessibility_bluetooth_connected);
bluetoothVisible = mBluetooth.isBluetoothEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index d2e36b8..b645e18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -36,6 +36,7 @@
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
+import android.view.CrossWindowBlurListeners;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
@@ -191,6 +192,8 @@
*/
public static final float BUSY_SCRIM_ALPHA = 1f;
+ public static float QS_CLIP_SCRIM_ALPHA = 0.80f;
+
/**
* Scrim opacity that can have text on top.
*/
@@ -224,6 +227,7 @@
private final KeyguardInteractor mKeyguardInteractor;
private GradientColors mColors;
+ private GradientColors mBehindColors;
private boolean mNeedsDrawableColorUpdate;
private float mAdditionalScrimBehindAlphaKeyguard = 0f;
@@ -347,6 +351,8 @@
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
+ CrossWindowBlurListeners mBlurSupport = CrossWindowBlurListeners.getInstance();
+ QS_CLIP_SCRIM_ALPHA = mBlurSupport.isCrossWindowBlurEnabled() ? 0.8f : 1.0f;
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -390,6 +396,7 @@
mKeyguardInteractor = keyguardInteractor;
mWallpaperRepository = wallpaperRepository;
mMainDispatcher = mainDispatcher;
+ mBehindColors = new GradientColors();
}
@Override
@@ -425,6 +432,7 @@
states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
+ states[i].setQSClipScrimAlpha(QS_CLIP_SCRIM_ALPHA);
}
mTransparentScrimBackground = notificationsScrim.getResources()
@@ -948,8 +956,8 @@
} else if (mClipsQsScrim) {
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mBehindAlpha = 1;
- mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
+ mBehindAlpha = QS_CLIP_SCRIM_ALPHA;
+ mNotificationsAlpha = behindFraction * QS_CLIP_SCRIM_ALPHA;
} else {
mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha(
mPanelExpansionFraction * mDefaultScrimAlpha);
@@ -996,8 +1004,8 @@
if (mClipsQsScrim) {
mNotificationsAlpha = behindAlpha;
mNotificationsTint = behindTint;
- mBehindAlpha = 1;
- mBehindTint = Color.BLACK;
+ mBehindAlpha = QS_CLIP_SCRIM_ALPHA;
+ mBehindTint = Color.TRANSPARENT;
} else {
mBehindAlpha = behindAlpha;
if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) {
@@ -1055,7 +1063,7 @@
float behindAlpha;
int behindTint = state.getBehindTint();
if (mDarkenWhileDragging) {
- behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
+ behindAlpha = MathUtils.lerp(QS_CLIP_SCRIM_ALPHA, stateBehind,
interpolatedFract);
} else {
behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
@@ -1071,7 +1079,7 @@
}
}
if (mQsExpansion > 0) {
- behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
+ behindAlpha = MathUtils.lerp(behindAlpha, QS_CLIP_SCRIM_ALPHA, mQsExpansion);
float tintProgress = mQsExpansion;
if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
// this is case of - on lockscreen - going from expanded QS to bouncer.
@@ -1191,7 +1199,7 @@
&& !mBlankScreen;
mScrimInFront.setColors(mColors, animateScrimInFront);
- mScrimBehind.setColors(mColors, animateBehindScrim);
+ mScrimBehind.setColors(mBehindColors, animateBehindScrim);
mNotificationsScrim.setColors(mColors, animateScrimNotifications);
dispatchBackScrimState(mScrimBehind.getViewAlpha());
@@ -1556,6 +1564,8 @@
if (mScrimBehind == null) return;
int background = Utils.getColorAttr(mScrimBehind.getContext(),
com.android.internal.R.attr.materialColorSurfaceDim).getDefaultColor();
+ int surfaceBackground = Utils.getColorAttr(mScrimBehind.getContext(),
+ com.android.internal.R.attr.colorSurfaceHeader).getDefaultColor();
int accent = Utils.getColorAttr(mScrimBehind.getContext(),
com.android.internal.R.attr.materialColorPrimary).getDefaultColor();
mColors.setMainColor(background);
@@ -1569,6 +1579,11 @@
state.setSurfaceColor(surface);
}
+ mBehindColors.setMainColor(surfaceBackground);
+ mBehindColors.setSecondaryColor(accent);
+ mBehindColors.setSupportsDarkText(
+ ColorUtils.calculateContrast(mBehindColors.getMainColor(), Color.WHITE) > 4.5);
+
mNeedsDrawableColorUpdate = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index f2a649b..dc9ada2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -77,13 +77,13 @@
}
mFrontTint = mBackgroundColor;
mBehindTint = mBackgroundColor;
- mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT;
+ mNotifTint = Color.TRANSPARENT;
mFrontAlpha = 0;
- mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
+ mBehindAlpha = mClipQsScrim ? mQSClipScrimAlpha : mScrimBehindAlphaKeyguard;
mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -122,9 +122,9 @@
BOUNCER {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
- mBehindTint = mClipQsScrim ? mBackgroundColor : mSurfaceColor;
- mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
+ mBehindAlpha = mClipQsScrim ? mQSClipScrimAlpha : mDefaultScrimAlpha;
+ mBehindTint = Color.TRANSPARENT;
+ mNotifAlpha = mClipQsScrim ? mQSClipScrimAlpha : 0;
mNotifTint = Color.TRANSPARENT;
mFrontAlpha = 0f;
}
@@ -152,13 +152,13 @@
SHADE_LOCKED {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
- mNotifAlpha = 1f;
+ mBehindAlpha = mClipQsScrim ? mQSClipScrimAlpha : mDefaultScrimAlpha;
+ mNotifAlpha = mClipQsScrim ? mQSClipScrimAlpha : mDefaultScrimAlpha;
mFrontAlpha = 0f;
- mBehindTint = mClipQsScrim ? Color.TRANSPARENT : mBackgroundColor;
+ mBehindTint = Color.TRANSPARENT;
if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -243,7 +243,7 @@
@Override
public void prepare(ScrimState previousState) {
// State that UI will sync to.
- mBehindAlpha = mClipQsScrim ? 1 : 0;
+ mBehindAlpha = mClipQsScrim ? mQSClipScrimAlpha : 0;
mNotifAlpha = 0;
mFrontAlpha = 0;
mAnimationDuration = mKeyguardFadingAway
@@ -259,22 +259,22 @@
&& !fromAod;
mFrontTint = Color.TRANSPARENT;
- mBehindTint = mBackgroundColor;
+ mBehindTint = Color.TRANSPARENT;
mBlankScreen = false;
if (mDisplayRequiresBlanking && previousState == ScrimState.AOD) {
// Set all scrims black, before they fade transparent.
updateScrimColor(mScrimInFront, 1f /* alpha */, mBackgroundColor /* tint */);
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor /* tint */);
+ updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT /* tint */);
// Scrims should still be black at the end of the transition.
mFrontTint = mBackgroundColor;
- mBehindTint = mBackgroundColor;
+ mBehindTint = Color.TRANSPARENT;
mBlankScreen = true;
}
if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -328,6 +328,7 @@
float mScrimBehindAlphaKeyguard;
float mDefaultScrimAlpha;
+ float mQSClipScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
@@ -439,6 +440,10 @@
mSurfaceColor = surfaceColor;
}
+ public void setQSClipScrimAlpha(float qsClipScrimAlpha) {
+ mQSClipScrimAlpha = qsClipScrimAlpha;
+ }
+
public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index d7cbe5d..5086e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -17,6 +17,7 @@
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_BINDABLE;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_NETWORK_TRAFFIC;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
import android.annotation.Nullable;
@@ -51,6 +52,7 @@
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
+import com.android.systemui.statusbar.policy.NetworkTrafficSB;
import com.android.systemui.util.Assert;
import java.util.ArrayList;
@@ -198,8 +200,10 @@
@Override
public void onSetIcon(int viewIndex, StatusBarIcon icon) {
- super.onSetIcon(viewIndex, icon);
- mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
+ View view = mGroup.getChildAt(viewIndex);
+ if (view instanceof StatusBarIconView) {
+ ((StatusBarIconView) view).set(icon);
+ }
}
@Override
@@ -439,6 +443,9 @@
case TYPE_BINDABLE:
// Safe cast, since only BindableIconHolders can set this tag on themselves
return addBindableIcon((BindableIconHolder) holder, index);
+
+ case TYPE_NETWORK_TRAFFIC:
+ return addNetworkTraffic(index, slot);
}
return null;
@@ -453,6 +460,12 @@
return view;
}
+ protected NetworkTrafficSB addNetworkTraffic(int index, String slot) {
+ NetworkTrafficSB view = onCreateNetworkTraffic(slot);
+ mGroup.addView(view, index, onCreateLayoutParams());
+ return view;
+ }
+
/**
* ModernStatusBarViews can be created and bound, and thus do not need to update their
* drawable by sending multiple calls to setIcon. Instead, by using a bindable
@@ -505,6 +518,12 @@
return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel);
}
+ private NetworkTrafficSB onCreateNetworkTraffic(String slot) {
+ NetworkTrafficSB view = new NetworkTrafficSB(mContext);
+ view.setPadding(2, 0, 2, 0);
+ return view;
+ }
+
private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
String slot, int subId) {
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
@@ -538,8 +557,10 @@
}
public void onSetIcon(int viewIndex, StatusBarIcon icon) {
- StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
- view.set(icon);
+ View view = mGroup.getChildAt(viewIndex);
+ if (view instanceof StatusBarIconView) {
+ ((StatusBarIconView) view).set(icon);
+ }
}
public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
index bef0b28..d484c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
@@ -48,7 +48,8 @@
// this is effectively an unused return value.
TYPE_BINDABLE,
TYPE_MOBILE_NEW,
- TYPE_WIFI_NEW -> true
+ TYPE_WIFI_NEW,
+ TYPE_NETWORK_TRAFFIC -> true
else -> true
}
set(visible) {
@@ -100,6 +101,8 @@
/** Only applicable to [BindableIconHolder] */
const val TYPE_BINDABLE = 5
+ const val TYPE_NETWORK_TRAFFIC = 6
+
/** Returns a human-readable string representing the given type. */
fun getTypeString(@IconType type: Int): String {
return when (type) {
@@ -159,6 +162,13 @@
holder.tag = state.subId
return holder
}
+
+ @JvmStatic
+ fun fromNetworkTraffic(): StatusBarIconHolder {
+ val holder = StatusBarIconHolder()
+ holder.type = TYPE_NETWORK_TRAFFIC
+ return holder
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 565481a..0313fc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -29,6 +29,8 @@
import java.util.List;
import java.util.stream.Collectors;
+import com.android.systemui.statusbar.policy.NetworkTrafficSB;
+
/** A class holding the list of all the system icons that could be shown in the status bar. */
public class StatusBarIconList {
private final ArrayList<Slot> mSlots = new ArrayList<>();
@@ -39,6 +41,9 @@
for (int i = 0; i < N; i++) {
mSlots.add(new Slot(slots[i], null));
}
+
+ // Network traffic slot
+ mSlots.add(0, new Slot(NetworkTrafficSB.SLOT, StatusBarIconHolder.fromNetworkTraffic()));
}
/** Returns the list of current slots. */
@@ -121,9 +126,9 @@
return i;
}
}
- // Auto insert new items at the beginning.
- mSlots.add(0, new Slot(slot, null));
- return 0;
+ // Auto insert new items behind network traffic
+ mSlots.add(1, new Slot(slot, null));
+ return 1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 8e9c038..b8feb69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -315,7 +315,7 @@
@Override
public boolean suppressAwakeInterruptions(NotificationEntry entry) {
- return isDeviceInVrMode();
+ return isDeviceInVrMode() || entry.getSbn().getIsContentSecure();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 703b3c6..a0ed36ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -52,6 +52,7 @@
private final String mSlotVpn;
private final String mSlotNoCalling;
private final String mSlotCallStrength;
+ private final String mSlotRoaming = "roaming";
private final Context mContext;
private final StatusBarIconController mIconController;
@@ -65,6 +66,8 @@
private boolean mHideMobile;
private boolean mHideEthernet;
private boolean mActivityEnabled;
+ private boolean mHideVpn;
+ private boolean mHideRoaming;
// Track as little state as possible, and only for padding purposes
private boolean mIsAirplaneMode = false;
@@ -117,7 +120,7 @@
}
private void updateVpn() {
- boolean vpnVisible = mSecurityController.isVpnEnabled();
+ boolean vpnVisible = mSecurityController.isVpnEnabled() && !mHideVpn;
int vpnIconId = currentVpnIconId(
mSecurityController.isVpnBranded(),
mSecurityController.isVpnValidated());
@@ -156,12 +159,17 @@
boolean hideAirplane = hideList.contains(mSlotAirplane);
boolean hideMobile = hideList.contains(mSlotMobile);
boolean hideEthernet = hideList.contains(mSlotEthernet);
+ boolean hideVpn = hideList.contains(mSlotVpn);
+ boolean hideRoaming = hideList.contains(mSlotRoaming);
if (hideAirplane != mHideAirplane || hideMobile != mHideMobile
- || hideEthernet != mHideEthernet) {
+ || hideEthernet != mHideEthernet || hideVpn != mHideVpn
+ || hideRoaming != mHideRoaming) {
mHideAirplane = hideAirplane;
mHideMobile = hideMobile;
mHideEthernet = hideEthernet;
+ mHideVpn = hideVpn;
+ mHideRoaming = hideRoaming;
// Re-register to get new callbacks.
mNetworkController.removeCallback(this);
mNetworkController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
index fbd074d..98edf65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
@@ -45,6 +45,10 @@
"viewString=${super.toString()}"
}
+ fun updateTextAppearance(resId: Int) {
+ requireViewById<AutoMarqueeTextView>(R.id.mobile_carrier_text).setTextAppearance(resId)
+ }
+
companion object {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index aab3509..ebb8b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -47,6 +47,8 @@
void removeOnMetadataChangedListener(CachedBluetoothDevice device,
BluetoothAdapter.OnMetadataChangedListener listener);
+ int getBatteryLevel();
+
public interface Callback {
void onBluetoothStateChange(boolean enabled);
void onBluetoothDevicesChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index c089092..159b579 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -79,6 +79,7 @@
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
private boolean mAudioProfileOnly;
private boolean mIsActive;
+ private int mBatteryLevel;
private final H mHandler;
private int mState;
@@ -134,6 +135,7 @@
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mConnectionState="); pw.println(connectionStateToString(mConnectionState));
pw.print(" mAudioProfileOnly="); pw.println(mAudioProfileOnly);
+ pw.print(" mBatteryLevel="); pw.println(mBatteryLevel);
pw.print(" mIsActive="); pw.println(mIsActive);
pw.print(" mConnectedDevices="); pw.println(getConnectedDevices());
pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
@@ -285,6 +287,7 @@
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
updateAudioProfile();
+ updateBattery();
}
private void updateActive() {
@@ -334,6 +337,21 @@
}
@Override
+ public int getBatteryLevel() {
+ if (!mConnectedDevices.isEmpty()) {
+ return mConnectedDevices.get(0).getBatteryLevel();
+ }
+ return -1;
+ }
+
+ private void updateBattery() {
+ int batteryLevel = getBatteryLevel();
+ if (batteryLevel != mBatteryLevel) {
+ mBatteryLevel = batteryLevel;
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+ }
+ }
+
public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
mLogger.logStateChange(BluetoothAdapter.nameForState(bluetoothState));
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
@@ -497,4 +515,4 @@
cb.onBluetoothStateChange(mEnabled);
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 13f76fe..56e64cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -19,18 +19,23 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.systemui.Dependency;
import com.android.systemui.res.R;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.ToggleSlider;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.tuner.TunerService;
import java.util.Objects;
import java.util.function.Consumer;
@@ -41,6 +46,9 @@
public class BrightnessMirrorController
implements CallbackController<BrightnessMirrorController.BrightnessMirrorListener> {
+ private static final String QS_SHOW_AUTO_BRIGHTNESS =
+ Settings.Secure.QS_SHOW_AUTO_BRIGHTNESS;
+
private final NotificationShadeWindowView mStatusBarWindow;
private final Consumer<Boolean> mVisibilityCallback;
private final ShadeViewController mNotificationPanel;
@@ -52,6 +60,9 @@
private FrameLayout mBrightnessMirror;
private int mBrightnessMirrorBackgroundPadding;
private int mLastBrightnessSliderWidth = -1;
+ private boolean mShouldShowAutoBrightness;
+ private boolean mIsAutomaticBrightnessAvailable;
+ private ImageView mIcon;
public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow,
ShadeViewController shadeViewController,
@@ -69,6 +80,17 @@
});
mVisibilityCallback = visibilityCallback;
updateResources();
+
+ TunerService.Tunable tunable = (key, newValue) -> {
+ if (QS_SHOW_AUTO_BRIGHTNESS.equals(key)) {
+ mShouldShowAutoBrightness = TunerService.parseIntegerSwitch(newValue, true);
+ updateIcon();
+ }
+ };
+ Dependency.get(TunerService.class).addTunable(tunable, QS_SHOW_AUTO_BRIGHTNESS);
+
+ mIsAutomaticBrightnessAvailable = mBrightnessMirror.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available);
}
public void showMirror() {
@@ -76,6 +98,7 @@
mVisibilityCallback.accept(true);
mNotificationPanel.setAlpha(0, true /* animate */);
mDepthController.setBrightnessMirrorVisible(true);
+ updateIcon();
}
public void hideMirror() {
@@ -144,6 +167,7 @@
mBrightnessMirror.addView(controller.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
+ mIcon = mBrightnessMirror.findViewById(R.id.brightness_icon);
return controller;
}
@@ -180,4 +204,21 @@
public interface BrightnessMirrorListener {
void onBrightnessMirrorReinflated(View brightnessMirror);
}
+
+ private void updateIcon() {
+ if (mIsAutomaticBrightnessAvailable && mShouldShowAutoBrightness) {
+ int automatic = Settings.System.getIntForUser(mBrightnessMirror.getContext()
+ .getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+ UserHandle.USER_CURRENT);
+ boolean isAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ mIcon.setImageResource(isAutomatic
+ ? com.android.systemui.R.drawable.ic_qs_brightness_auto_on
+ : com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
+ mIcon.setVisibility(View.VISIBLE);
+ } else {
+ mIcon.setVisibility(View.GONE);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 149c8fa..fe3a2e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -167,7 +167,8 @@
int statusCode = route.getStatusCode();
if (statusCode == RouteInfo.STATUS_CONNECTING) {
device.state = CastDevice.STATE_CONNECTING;
- } else if (route.isSelected() || statusCode == RouteInfo.STATUS_CONNECTED) {
+ } else if (route.isSelected() && statusCode != RouteInfo.STATUS_NOT_AVAILABLE
+ || statusCode == RouteInfo.STATUS_CONNECTED) {
device.state = CastDevice.STATE_CONNECTED;
} else {
device.state = CastDevice.STATE_DISCONNECTED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 20d1fff..e91310d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -39,13 +39,11 @@
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.res.R;
@@ -370,13 +368,6 @@
setTextColor(mNonAdaptedColor);
}
- // Update text color based when shade scrim changes color.
- public void onColorsChanged(boolean lightTheme) {
- final Context context = new ContextThemeWrapper(mContext,
- lightTheme ? R.style.Theme_SystemUI_LightWallpaper : R.style.Theme_SystemUI);
- setTextColor(Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor));
- }
-
@Override
public void onDensityOrFontScaleChanged() {
reloadDimens();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java
new file mode 100644
index 0000000..749bb11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java
@@ -0,0 +1,367 @@
+package com.android.systemui.statusbar.policy;
+
+import java.text.DecimalFormat;
+
+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.graphics.drawable.Drawable;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.view.Gravity;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.TrafficStats;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.Spanned;
+import android.text.SpannableString;
+import android.text.style.RelativeSizeSpan;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/*
+*
+* Seeing how an Integer object in java requires at least 16 Bytes, it seemed awfully wasteful
+* to only use it for a single boolean. 32-bits is plenty of room for what we need it to do.
+*
+*/
+public class NetworkTraffic extends TextView {
+
+ private static final int INTERVAL = 1500; //ms
+ private static final int KB = 1024;
+ private static final int MB = KB * KB;
+ private static final int GB = MB * KB;
+ private static final String symbol = "/S";
+
+ private final int mWidth;
+
+ protected boolean mIsEnabled;
+ private boolean mAttached;
+ private long totalRxBytes;
+ private long totalTxBytes;
+ private long lastUpdateTime;
+ private int mAutoHideThreshold;
+ protected int mTintColor;
+
+ private boolean mScreenOn = true;
+ protected boolean mVisible = true;
+ private ConnectivityManager mConnectivityManager;
+
+ private Handler mTrafficHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ long timeDelta = SystemClock.elapsedRealtime() - lastUpdateTime;
+
+ if (timeDelta < INTERVAL * .95) {
+ if (msg.what != 1) {
+ // we just updated the view, nothing further to do
+ return;
+ }
+ if (timeDelta < 1) {
+ // Can't div by 0 so make sure the value displayed is minimal
+ timeDelta = Long.MAX_VALUE;
+ }
+ }
+ lastUpdateTime = SystemClock.elapsedRealtime();
+
+ // Calculate the data rate from the change in total bytes and time
+ long newTotalRxBytes = TrafficStats.getTotalRxBytes();
+ long newTotalTxBytes = TrafficStats.getTotalTxBytes();
+ long rxData = newTotalRxBytes - totalRxBytes;
+ long txData = newTotalTxBytes - totalTxBytes;
+
+ if (shouldHide(rxData, txData, timeDelta)) {
+ setText("");
+ setVisibility(View.INVISIBLE);
+ mVisible = false;
+ } else if (shouldShowUpload(rxData, txData, timeDelta)) {
+ // Show information for uplink if it's called for
+ CharSequence output = formatOutput(timeDelta, txData, symbol);
+
+ // Update view if there's anything new to show
+ if (output != getText()) {
+ setText(output);
+ }
+ makeVisible();
+ } else {
+ // Add information for downlink if it's called for
+ CharSequence output = formatOutput(timeDelta, rxData, symbol);
+
+ // Update view if there's anything new to show
+ if (output != getText()) {
+ setText(output);
+ }
+ makeVisible();
+ }
+
+ // Post delayed message to refresh in ~1000ms
+ totalRxBytes = newTotalRxBytes;
+ totalTxBytes = newTotalTxBytes;
+ clearHandlerCallbacks();
+ mTrafficHandler.postDelayed(mRunnable, INTERVAL);
+ }
+
+ private CharSequence formatOutput(long timeDelta, long data, String symbol) {
+ long speed = (long)(data / (timeDelta / 1000F));
+
+ return formatDecimal(speed);
+ }
+
+ private CharSequence formatDecimal(long speed) {
+ DecimalFormat decimalFormat;
+ String unit;
+ String formatSpeed;
+ SpannableString spanUnitString;
+ SpannableString spanSpeedString;
+
+ if (speed >= 1000 * MB) {
+ unit = "GB";
+ decimalFormat = new DecimalFormat("0.00");
+ formatSpeed = decimalFormat.format(speed / (float)GB);
+ } else if (speed >= 100 * MB) {
+ decimalFormat = new DecimalFormat("000");
+ unit = "MB";
+ formatSpeed = decimalFormat.format(speed / (float)MB);
+ } else if (speed >= 10 * MB) {
+ decimalFormat = new DecimalFormat("00.0");
+ unit = "MB";
+ formatSpeed = decimalFormat.format(speed / (float)MB);
+ } else if (speed >= 1000 * KB) {
+ decimalFormat = new DecimalFormat("0.00");
+ unit = "MB";
+ formatSpeed = decimalFormat.format(speed / (float)MB);
+ } else if (speed >= 100 * KB) {
+ decimalFormat = new DecimalFormat("000");
+ unit = "KB";
+ formatSpeed = decimalFormat.format(speed / (float)KB);
+ } else if (speed >= 10 * KB) {
+ decimalFormat = new DecimalFormat("00.0");
+ unit = "KB";
+ formatSpeed = decimalFormat.format(speed / (float)KB);
+ } else {
+ decimalFormat = new DecimalFormat("0.00");
+ unit = "KB";
+ formatSpeed = decimalFormat.format(speed / (float)KB);
+ }
+ spanSpeedString = new SpannableString(formatSpeed);
+ spanSpeedString.setSpan(getSpeedRelativeSizeSpan(), 0, (formatSpeed).length(),
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ spanUnitString = new SpannableString(unit + symbol);
+ spanUnitString.setSpan(getUnitRelativeSizeSpan(), 0, (unit + symbol).length(),
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ return TextUtils.concat(spanSpeedString, "\n", spanUnitString);
+ }
+
+ private boolean shouldHide(long rxData, long txData, long timeDelta) {
+ long speedRxKB = (long)(rxData / (timeDelta / 1000f)) / KB;
+ long speedTxKB = (long)(txData / (timeDelta / 1000f)) / KB;
+ return !getConnectAvailable() ||
+ (speedRxKB < mAutoHideThreshold &&
+ speedTxKB < mAutoHideThreshold);
+ }
+
+ private boolean shouldShowUpload(long rxData, long txData, long timeDelta) {
+ long speedRxKB = (long)(rxData / (timeDelta / 1000f)) / KB;
+ long speedTxKB = (long)(txData / (timeDelta / 1000f)) / KB;
+
+ return (speedTxKB > speedRxKB);
+ }
+ };
+
+ protected boolean restoreViewQuickly() {
+ return getConnectAvailable() && mAutoHideThreshold == 0;
+ }
+
+ protected void makeVisible() {
+ setVisibility(View.VISIBLE);
+ mVisible = true;
+ }
+
+ /*
+ * @hide
+ */
+ public NetworkTraffic(Context context) {
+ this(context, null);
+ }
+
+ /*
+ * @hide
+ */
+ public NetworkTraffic(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /*
+ * @hide
+ */
+ public NetworkTraffic(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ final Resources resources = getResources();
+ mTintColor = getCurrentTextColor();
+ mWidth = resources.getDimensionPixelSize(R.dimen.network_traffic_width);
+ setMode();
+ Handler mHandler = new Handler();
+ mConnectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.observe();
+ update();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (!mAttached) {
+ mAttached = true;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiver(mIntentReceiver, filter, null, getHandler());
+ }
+ update();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mAttached) {
+ mContext.unregisterReceiver(mIntentReceiver);
+ mAttached = false;
+ }
+ }
+
+ protected RelativeSizeSpan getSpeedRelativeSizeSpan() {
+ return new RelativeSizeSpan(0.78f);
+ }
+
+ protected RelativeSizeSpan getUnitRelativeSizeSpan() {
+ return new RelativeSizeSpan(0.70f);
+ }
+
+ private Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mTrafficHandler.sendEmptyMessage(0);
+ }
+ };
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.System
+ .getUriFor(Settings.System.NETWORK_TRAFFIC_STATE), false,
+ this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System
+ .getUriFor(Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD), false,
+ this, UserHandle.USER_ALL);
+ }
+
+ /*
+ * @hide
+ */
+ @Override
+ public void onChange(boolean selfChange) {
+ setMode();
+ update();
+ }
+ }
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) return;
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) && mScreenOn) {
+ update();
+ } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ mScreenOn = true;
+ update();
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ mScreenOn = false;
+ clearHandlerCallbacks();
+ }
+ }
+ };
+
+ private boolean getConnectAvailable() {
+ NetworkInfo network = (mConnectivityManager != null) ? mConnectivityManager.getActiveNetworkInfo() : null;
+ return network != null;
+ }
+
+ protected void update() {
+ if (mIsEnabled) {
+ if (mAttached) {
+ totalRxBytes = TrafficStats.getTotalRxBytes();
+ totalTxBytes = TrafficStats.getTotalTxBytes();
+ mTrafficHandler.sendEmptyMessage(1);
+ }
+ if (mAutoHideThreshold == 0)
+ makeVisible();
+ return;
+ }
+ clearHandlerCallbacks();
+ setVisibility(View.GONE);
+ mVisible = false;
+ }
+
+ protected void setMode() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mIsEnabled = Settings.System.getIntForUser(resolver,
+ Settings.System.NETWORK_TRAFFIC_STATE, 0,
+ UserHandle.USER_CURRENT) == 1;
+ mAutoHideThreshold = Settings.System.getIntForUser(resolver,
+ Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD, 1,
+ UserHandle.USER_CURRENT);
+ setGravity(Gravity.CENTER);
+ setMaxLines(2);
+ setSpacingAndFonts();
+ updateTrafficDrawable();
+ setWidth(mWidth);
+ }
+
+ private void clearHandlerCallbacks() {
+ mTrafficHandler.removeCallbacks(mRunnable);
+ mTrafficHandler.removeMessages(0);
+ mTrafficHandler.removeMessages(1);
+ }
+
+ protected void updateTrafficDrawable() {
+ setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ setTextColor(mTintColor);
+ }
+
+ protected void setSpacingAndFonts() {
+ setTextAppearance(R.style.TextAppearance_QS_Status);
+ setLineSpacing(0.88f, 0.88f);
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ setSpacingAndFonts();
+ update();
+ }
+
+ public void setTintColor(int color) {
+ mTintColor = color;
+ updateTrafficDrawable();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java
new file mode 100644
index 0000000..2604c67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java
@@ -0,0 +1,158 @@
+package com.android.systemui.statusbar.policy;
+
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.text.style.RelativeSizeSpan;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.StatusIconDisplayable;
+
+import java.util.ArrayList;
+
+public class NetworkTrafficSB extends NetworkTraffic implements DarkReceiver, StatusIconDisplayable {
+
+ public static final String SLOT = "networktraffic";
+ private int mVisibleState = -1;
+ private boolean mSystemIconVisible = true;
+
+ /*
+ * @hide
+ */
+ public NetworkTrafficSB(Context context) {
+ this(context, null);
+ }
+
+ /*
+ * @hide
+ */
+ public NetworkTrafficSB(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /*
+ * @hide
+ */
+ public NetworkTrafficSB(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void setMode() {
+ super.setMode();
+ mIsEnabled = mIsEnabled;
+ }
+
+ @Override
+ protected void setSpacingAndFonts() {
+ setTextAppearance(R.style.TextAppearance_QS_Status);
+ setLineSpacing(0.83f, 0.83f);
+ }
+
+ @Override
+ protected RelativeSizeSpan getSpeedRelativeSizeSpan() {
+ return new RelativeSizeSpan(0.70f);
+ }
+
+ @Override
+ protected RelativeSizeSpan getUnitRelativeSizeSpan() {
+ return new RelativeSizeSpan(0.60f);
+ }
+
+ @Override
+ public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
+ if (!mIsEnabled) return;
+ mTintColor = DarkIconDispatcher.getTint(areas, this, tint);
+ setTextColor(mTintColor);
+ updateTrafficDrawable();
+ }
+
+ @Override
+ public String getSlot() {
+ return SLOT;
+ }
+
+ @Override
+ public boolean isIconVisible() {
+ return mIsEnabled;
+ }
+
+ @Override
+ public int getVisibleState() {
+ return mVisibleState;
+ }
+
+ @Override
+ public void setVisibleState(int state, boolean mIsEnabled) {
+ if (state == mVisibleState) {
+ return;
+ }
+ mVisibleState = state;
+
+ switch (state) {
+ case STATE_ICON:
+ mSystemIconVisible = true;
+ break;
+ case STATE_DOT:
+ case STATE_HIDDEN:
+ default:
+ mSystemIconVisible = false;
+ break;
+ }
+ update();
+ }
+
+ @Override
+ protected void makeVisible() {
+ boolean show = mSystemIconVisible;
+ setVisibility(show ? View.VISIBLE
+ : View.GONE);
+ mVisible = show;
+ }
+
+ @Override
+ public void setStaticDrawableColor(int color) {
+ mTintColor = color;
+ setTextColor(mTintColor);
+ updateTrafficDrawable();
+ }
+
+ @Override
+ public void setDecorColor(int color) {
+ setTintColor(color);
+ }
+
+ private void maybeRestoreVisibility() {
+ if (!mVisible && mIsEnabled && mSystemIconVisible
+ && restoreViewQuickly()) {
+ setVisibility(View.VISIBLE);
+ mVisible = true;
+ // then let the traffic handler do its checks
+ update();
+ }
+ }
+
+ public void setTintColor(int color) {
+ mTintColor = color;
+ updateTrafficDrawable();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 147e158..b46b05e 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -112,6 +112,11 @@
static final String OVERLAY_CATEGORY_ICON_THEME_PICKER =
"android.theme.customization.icon_pack.themepicker";
+ static final String OVERLAY_BRIGHTNESS_SLIDER_FILLED =
+ "com.android.systemui.brightness_slider.filled";
+ static final String OVERLAY_BRIGHTNESS_SLIDER_THIN =
+ "com.android.systemui.brightness_slider.thin";
+
/*
* All theme customization categories used by the system, in order that they should be applied,
* starts with launcher and grouped by target package.
@@ -139,6 +144,12 @@
OVERLAY_CATEGORY_ICON_ANDROID,
OVERLAY_CATEGORY_ICON_SYSUI);
+ /* Brightness slider overlays */
+ static final List<String> BRIGHTNESS_SLIDER_OVERLAYS = Lists.newArrayList(
+ "",
+ OVERLAY_BRIGHTNESS_SLIDER_FILLED,
+ OVERLAY_BRIGHTNESS_SLIDER_THIN);
+
/* Allowed overlay categories for each target package. */
private final Map<String, Set<String>> mTargetPackageToCategories = new ArrayMap<>();
/* Target package for each overlay category. */
@@ -261,6 +272,32 @@
});
}
+ /* Enable overlays */
+ public void enableOverlay(String overlayName, boolean enable) {
+ mBgExecutor.execute(() -> {
+ try {
+ mOverlayManager.setEnabled(overlayName, enable, UserHandle.SYSTEM);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.e(TAG, "Failed to enable overlay", e);
+ }
+ });
+ }
+
+ /* Set brightness slider styles */
+ public void setBrightnessSliderStyle(int brightnessSliderStyle) {
+ mBgExecutor.execute(() -> {
+ try {
+ for (int i = 1; i < BRIGHTNESS_SLIDER_OVERLAYS.size(); i++) {
+ String overlay = BRIGHTNESS_SLIDER_OVERLAYS.get(i);
+ boolean enable = (i == brightnessSliderStyle);
+ mOverlayManager.setEnabled(overlay, enable, UserHandle.SYSTEM);
+ }
+ } catch (SecurityException | IllegalStateException e) {
+ Log.e(TAG, "Failed to set brightness slider style", e);
+ }
+ });
+ }
+
@VisibleForTesting
protected OverlayManagerTransaction.Builder getTransactionBuilder() {
return new OverlayManagerTransaction.Builder();
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 44c684c..5c7f0c9 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -31,6 +31,9 @@
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
+import static com.android.systemui.util.qs.QSStyleUtils.QS_STYLE_ROUND_OVERLAY;
+import static com.android.systemui.util.qs.QSStyleUtils.isRoundQSSetting;
+import static com.android.systemui.util.qs.QSStyleUtils.setRoundQS;
import android.app.ActivityManager;
import android.app.UiModeManager;
@@ -80,6 +83,8 @@
import com.android.systemui.monet.Style;
import com.android.systemui.monet.TonalPalette;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -124,6 +129,8 @@
@SysUISingleton
public class ThemeOverlayController implements CoreStartable, Dumpable {
protected static final String TAG = "ThemeOverlayController";
+ protected static final String OVERLAY_BERRY_BLACK_THEME =
+ "org.leafos.overlay.customization.blacktheme";
private static final boolean DEBUG = true;
private final ThemeOverlayApplier mThemeManager;
@@ -137,6 +144,7 @@
private final boolean mIsMonetEnabled;
private final boolean mIsFidelityEnabled;
private final UserTracker mUserTracker;
+ private final ConfigurationController mConfigurationController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Resources mResources;
// Current wallpaper colors associated to a user.
@@ -178,6 +186,15 @@
// Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes.
private boolean mSkipSettingChange;
+ private final ConfigurationListener mConfigurationListener =
+ new ConfigurationListener() {
+ @Override
+ public void onUiModeChanged() {
+ Log.i(TAG, "Re-applying theme on UI change");
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ };
+
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@Override
@@ -418,10 +435,12 @@
JavaAdapter javaAdapter,
KeyguardTransitionInteractor keyguardTransitionInteractor,
UiModeManager uiModeManager,
- ActivityManager activityManager) {
+ ActivityManager activityManager,
+ ConfigurationController configurationController) {
mContext = context;
mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY);
+ mConfigurationController = configurationController;
mDeviceProvisionedController = deviceProvisionedController;
mBroadcastDispatcher = broadcastDispatcher;
mUserManager = userManager;
@@ -481,12 +500,141 @@
reevaluateSystemTheme(true /* forceReload */);
});
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.BERRY_BLACK_THEME),
+ false,
+ new ContentObserver(mBgHandler) {
+ @Override
+ public void onChange(boolean selfChange, Collection<Uri> collection, int flags,
+ int userId) {
+ if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
+ if (mUserTracker.getUserId() != userId) {
+ return;
+ }
+ if (!mDeviceProvisionedController.isUserSetup(userId)) {
+ Log.i(TAG, "Theme application deferred when setting changed.");
+ mDeferredThemeEvaluation = true;
+ return;
+ }
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ },
+ UserHandle.USER_ALL);
+
+ ContentObserver qsSettingsObserver = new ContentObserver(mBgHandler) {
+ @Override
+ public void onChange(boolean selfChange, Collection<Uri> collection, int flags,
+ int userId) {
+ if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
+ if (mUserTracker.getUserId() != userId) {
+ return;
+ }
+ if (!mDeviceProvisionedController.isUserSetup(userId)) {
+ Log.i(TAG, "Theme application deferred when setting changed.");
+ mDeferredThemeEvaluation = true;
+ return;
+ }
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ };
+
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QS_TILE_SHAPE),
+ false,
+ qsSettingsObserver,
+ UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QS_NUM_COLUMNS),
+ false,
+ qsSettingsObserver,
+ UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QQS_NUM_COLUMNS),
+ false,
+ qsSettingsObserver,
+ UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QS_NUM_COLUMNS_LANDSCAPE),
+ false,
+ qsSettingsObserver,
+ UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QQS_NUM_COLUMNS_LANDSCAPE),
+ false,
+ qsSettingsObserver,
+ UserHandle.USER_ALL);
+
+ boolean isRoundQS = isRoundQSSetting(mContext);
+ setRoundQS(isRoundQS);
+ mThemeManager.enableOverlay(QS_STYLE_ROUND_OVERLAY, isRoundQS);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QS_STYLE_ROUND),
+ false,
+ new ContentObserver(mBgHandler) {
+ @Override
+ public void onChange(boolean selfChange, Collection<Uri> collection, int flags,
+ int userId) {
+ boolean isRoundQS = isRoundQSSetting(mContext);
+ setRoundQS(isRoundQS);
+ mThemeManager.enableOverlay(QS_STYLE_ROUND_OVERLAY, isRoundQS);
+
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ },
+ UserHandle.USER_ALL);
+
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.QS_BRIGHTNESS_SLIDER_POSITION),
+ false,
+ new ContentObserver(mBgHandler) {
+ @Override
+ public void onChange(boolean selfChange, Collection<Uri> collection, int flags,
+ int userId) {
+ if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
+ if (mUserTracker.getUserId() != userId) {
+ return;
+ }
+ if (!mDeviceProvisionedController.isUserSetup(userId)) {
+ Log.i(TAG, "Theme application deferred when setting changed.");
+ mDeferredThemeEvaluation = true;
+ return;
+ }
+ reevaluateSystemTheme(true /* forceReload */);
+ }
+ },
+ UserHandle.USER_ALL);
+
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(Settings.Secure.BRIGHTNESS_SLIDER_STYLE),
+ false, new ContentObserver(mBgHandler) {
+ @Override
+ public void onChange(
+ boolean selfChange, Collection<Uri> collection, int flags, int userId) {
+ if (DEBUG)
+ Log.d(TAG, "Overlay changed for user: " + userId);
+ if (mUserTracker.getUserId() != userId) {
+ return;
+ }
+ if (!mDeviceProvisionedController.isUserSetup(userId)) {
+ Log.i(TAG, "Theme application deferred when setting changed.");
+ mDeferredThemeEvaluation = true;
+ return;
+ }
+ int brightnessSliderStyle =
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.BRIGHTNESS_SLIDER_STYLE, 0,
+ UserHandle.USER_CURRENT);
+ mThemeManager.setBrightnessSliderStyle(brightnessSliderStyle);
+ }
+ }, UserHandle.USER_ALL);
+
if (!mIsMonetEnabled) {
return;
}
mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
+ mConfigurationController.addCallback(mConfigurationListener);
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
// All wallpaper color and keyguard logic only applies when Monet is enabled.
@@ -804,6 +952,14 @@
categoryToPackage.put(OVERLAY_CATEGORY_DYNAMIC_COLOR, mDynamicOverlay.getIdentifier());
}
+ boolean isBlackMode = (Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.BERRY_BLACK_THEME,
+ 0, currentUser) == 1) && isNightMode();
+ if (categoryToPackage.containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE) && isBlackMode) {
+ OverlayIdentifier blackTheme = new OverlayIdentifier(OVERLAY_BERRY_BLACK_THEME);
+ categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, blackTheme);
+ }
+
Set<UserHandle> managedProfiles = new HashSet<>();
for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
if (userInfo.isProfile()) {
@@ -818,12 +974,6 @@
mActivityManager.setThemeOverlayReady(currentUser);
};
- if (colorSchemeIsApplied(managedProfiles)) {
- Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
- onCompleteCallback.run();
- return;
- }
-
if (DEBUG) {
Log.d(TAG, "Applying overlays: " + categoryToPackage.keySet().stream()
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java
new file mode 100644
index 0000000..b3bb734
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 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.systemui.tuner;
+
+import android.os.Bundle;
+import android.os.UserHandle;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.SwitchPreference;
+import android.provider.Settings;
+import android.view.MenuItem;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+
+public class StatusBarTuner extends PreferenceFragment {
+ private static final String SHOW_FOURG = "show_fourg";
+ private static final String NETWORK_TRAFFIC = "network_traffic_settings";
+
+ private SwitchPreference mShowFourG;
+ private SwitchPreference mNetMonitor;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setHasOptionsMenu(true);
+ mShowFourG = (SwitchPreference) findPreference(SHOW_FOURG);
+ mNetMonitor = (SwitchPreference) findPreference(NETWORK_TRAFFIC);
+ mShowFourG.setChecked(Settings.System.getIntForUser(getActivity().getContentResolver(),
+ Settings.System.SHOW_FOURG_ICON, 0,
+ UserHandle.USER_CURRENT) == 1);
+ mNetMonitor.setChecked(Settings.System.getIntForUser(getActivity().getContentResolver(),
+ Settings.System.NETWORK_TRAFFIC_STATE, 0,
+ UserHandle.USER_CURRENT) == 1);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.status_bar_prefs);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ MetricsLogger.visibility(getContext(), MetricsEvent.TUNER, true);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ MetricsLogger.visibility(getContext(), MetricsEvent.TUNER, false);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ getActivity().onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ if (preference == mShowFourG) {
+ boolean checked = ((SwitchPreference)preference).isChecked();
+ Settings.System.putIntForUser(getActivity().getContentResolver(),
+ Settings.System.SHOW_FOURG_ICON, checked ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ return true;
+ } else if (preference == mNetMonitor) {
+ boolean checked = ((SwitchPreference)preference).isChecked();
+ Settings.System.putIntForUser(getActivity().getContentResolver(),
+ Settings.System.NETWORK_TRAFFIC_STATE, checked ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ return true;
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 14d7281..f72a07b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -23,7 +23,6 @@
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.Toolbar;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
@@ -37,7 +36,9 @@
import javax.inject.Inject;
-public class TunerActivity extends Activity implements
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class TunerActivity extends CollapsingToolbarBaseActivity implements
PreferenceFragment.OnPreferenceStartFragmentCallback,
PreferenceFragment.OnPreferenceStartScreenCallback {
@@ -60,24 +61,23 @@
}
protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
- setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.tuner_activity);
- Toolbar toolbar = findViewById(R.id.action_bar);
- if (toolbar != null) {
- setActionBar(toolbar);
- }
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
final String action = getIntent().getAction();
- boolean showDemoMode = action != null && action.equals(
- "com.android.settings.action.DEMO_MODE");
- final PreferenceFragment fragment = showDemoMode
- ? new DemoModeFragment(mDemoModeController, mGlobalSettings)
- : new TunerFragment(mTunerService);
+ final Fragment fragment;
+ if ("com.android.settings.action.DEMO_MODE".equals(action)) {
+ fragment = new DemoModeFragment(mDemoModeController, mGlobalSettings);
+ } else if ("com.android.settings.action.STATUS_BAR_TUNER".equals(action)) {
+ fragment = new StatusBarTuner();
+ } else {
+ fragment = new TunerFragment(mTunerService);
+ }
+
getFragmentManager().beginTransaction().replace(R.id.content_frame,
fragment, TAG_TUNER).commit();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 873b6d5..5f460b0 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -73,12 +73,6 @@
setHasOptionsMenu(true);
}
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
// aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so
// explicitly declare references per usage in `R.xml.tuner_prefs`. See b/120445169.
@UsesReflection({
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index a501e87..022e85d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -80,4 +80,12 @@
return defaultValue;
}
}
+
+ public static int parseInteger(String value, int defaultValue) {
+ try {
+ return value != null ? Integer.parseInt(value) : defaultValue;
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/qs/QSStyleUtils.java b/packages/SystemUI/src/com/android/systemui/util/qs/QSStyleUtils.java
new file mode 100644
index 0000000..a35d15a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/qs/QSStyleUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The LeafOS 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.systemui.util.qs;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.systemui.R;
+
+public class QSStyleUtils {
+ public static final String QS_STYLE_ROUND = Settings.Secure.QS_STYLE_ROUND;
+ public static final String QS_STYLE_ROUND_OVERLAY = "com.android.systemui.qs_style.round";
+
+ private static boolean mIsRoundQS;
+
+ public static void setRoundQS(boolean enable) {
+ mIsRoundQS = enable;
+ }
+
+ public static boolean isRoundQS() {
+ return mIsRoundQS;
+ }
+
+ public static boolean isRoundQSSetting(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ QS_STYLE_ROUND, 1, UserHandle.USER_CURRENT) == 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4045630..29a2c39 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -23,11 +23,13 @@
import static android.media.AudioManager.STREAM_ACCESSIBILITY;
import static android.media.AudioManager.STREAM_ALARM;
import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
import static android.media.AudioManager.STREAM_RING;
import static android.media.AudioManager.STREAM_VOICE_CALL;
import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
+import static android.view.View.LAYOUT_DIRECTION_LTR;
import static android.view.View.LAYOUT_DIRECTION_RTL;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -47,6 +49,8 @@
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.KeyguardManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -66,6 +70,9 @@
import android.graphics.drawable.RotateDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
@@ -76,6 +83,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -96,6 +104,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -127,6 +136,7 @@
import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -256,6 +266,8 @@
private CaptionsToggleImageButton mODICaptionsIcon;
private View mSettingsView;
private ImageButton mSettingsIcon;
+ private View mExpandRowsView;
+ private ExpandableIndicator mExpandRows;
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -288,6 +300,9 @@
private ViewStub mODICaptionsTooltipViewStub;
@VisibleForTesting View mODICaptionsTooltipView = null;
+ // Volume panel placement left or right
+ private boolean mVolumePanelOnLeft;
+
private final boolean mUseBackgroundBlur;
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
private BackgroundBlurDrawable mDialogRowsViewBackground;
@@ -310,6 +325,17 @@
private final VibratorHelper mVibratorHelper;
private final com.android.systemui.util.time.SystemClock mSystemClock;
+ // Variable to track the default row with which the panel is initially shown
+ private VolumeRow mDefaultRow = null;
+
+ // Volume panel expand state
+ private boolean mExpanded;
+
+ // Number of animating rows
+ private int mAnimatingRows = 0;
+
+ private FrameLayout mRoundedBorderBottom;
+
public VolumeDialogImpl(
Context context,
VolumeDialogController volumeDialogController,
@@ -375,6 +401,10 @@
};
}
+ if (!mShowActiveStreamOnly) {
+ mVolumePanelOnLeft = mContext.getResources().getBoolean(R.bool.config_audioPanelOnLeftSide);;
+ }
+
initDimens();
mOrientation = mContext.getResources().getConfiguration().orientation;
@@ -465,27 +495,56 @@
final int[] locInWindow = new int[2];
view.getLocationInWindow(locInWindow);
- float x = locInWindow[0];
- float y = locInWindow[1];
+ float xExtraSize = 0;
+ float yExtraSize = 0;
// The ringer and rows container has extra height at the top to fit the expanded ringer
// drawer. This area should not be touchable unless the ringer drawer is open.
// In landscape the ringer expands to the left and it has to be ensured that if there
// are multiple rows they are touchable.
- if (view == mTopContainer && !mIsRingerDrawerOpen) {
+ // The invisible expandable rows reserve space if the panel is not expanded, this space
+ // needs to be touchable.
+ if (view == mTopContainer) {
if (!isLandscape()) {
- y += getRingerDrawerOpenExtraSize();
- } else if (getRingerDrawerOpenExtraSize() > getVisibleRowsExtraSize()) {
- x += (getRingerDrawerOpenExtraSize() - getVisibleRowsExtraSize());
+ if (!mIsRingerDrawerOpen) {
+ yExtraSize = getRingerDrawerOpenExtraSize();
+ }
+ if (!mExpanded) {
+ xExtraSize = getExpandableRowsExtraSize();
+ }
+ } else {
+ if (!mIsRingerDrawerOpen && !mExpanded) {
+ xExtraSize =
+ Math.max(getRingerDrawerOpenExtraSize(), getExpandableRowsExtraSize());
+ } else if (!mIsRingerDrawerOpen) {
+ if (getRingerDrawerOpenExtraSize() > getVisibleRowsExtraSize()) {
+ xExtraSize = getRingerDrawerOpenExtraSize() - getVisibleRowsExtraSize();
+ }
+ } else if (!mExpanded) {
+ if ((getVisibleRowsExtraSize() + getExpandableRowsExtraSize())
+ > getRingerDrawerOpenExtraSize()) {
+ xExtraSize = (getVisibleRowsExtraSize() + getExpandableRowsExtraSize())
+ - getRingerDrawerOpenExtraSize();
+ }
+ }
}
}
- mTouchableRegion.op(
- (int) x,
- (int) y,
- locInWindow[0] + view.getWidth(),
- locInWindow[1] + view.getHeight(),
- Region.Op.UNION);
+ if (mVolumePanelOnLeft) {
+ mTouchableRegion.op(
+ locInWindow[0],
+ locInWindow[1] + (int) yExtraSize,
+ locInWindow[0] + view.getWidth() - (int) xExtraSize,
+ locInWindow[1] + view.getHeight(),
+ Region.Op.UNION);
+ } else {
+ mTouchableRegion.op(
+ locInWindow[0] + (int) xExtraSize,
+ locInWindow[1] + (int) yExtraSize,
+ locInWindow[0] + view.getWidth(),
+ locInWindow[1] + view.getHeight(),
+ Region.Op.UNION);
+ }
}
private void initDialog(int lockTaskModeState) {
@@ -496,6 +555,7 @@
mConfigurableTexts = new ConfigurableTexts(mContext);
mHovering = false;
mShowing = false;
+ mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
@@ -515,6 +575,10 @@
lp.windowAnimations = -1;
mOriginalGravity = mContext.getResources().getInteger(R.integer.volume_dialog_gravity);
+ if (!mShowActiveStreamOnly) {
+ mOriginalGravity &= ~(Gravity.LEFT | Gravity.RIGHT);
+ mOriginalGravity |= mVolumePanelOnLeft ? Gravity.LEFT : Gravity.RIGHT;
+ }
mWindowGravity = Gravity.getAbsoluteGravity(mOriginalGravity,
mContext.getResources().getConfiguration().getLayoutDirection());
lp.gravity = mWindowGravity;
@@ -526,6 +590,8 @@
mDialogView.setAlpha(0);
mDialogTimeoutMillis = mSecureSettings.get().getInt(
Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, DIALOG_TIMEOUT_MILLIS);
+ mDialogView.setLayoutDirection(
+ mVolumePanelOnLeft ? LAYOUT_DIRECTION_LTR : LAYOUT_DIRECTION_RTL);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -627,6 +693,9 @@
updateBackgroundForDrawerClosedAmount();
setTopContainerBackgroundDrawable();
+
+ // Rows need to be updated after mRingerAndDrawerContainerBackground is set
+ updateRowsH(getActiveRow());
}
});
}
@@ -670,6 +739,41 @@
mSettingsView = mDialog.findViewById(R.id.settings_container);
mSettingsIcon = mDialog.findViewById(R.id.settings);
+ mExpandRowsView = mDialog.findViewById(R.id.expandable_indicator_container);
+ mExpandRows = mDialog.findViewById(R.id.expandable_indicator);
+
+ mRoundedBorderBottom = mDialog.findViewById(R.id.rounded_border_bottom);
+
+ if (mVolumePanelOnLeft) {
+ if (mRingerAndDrawerContainer != null) {
+ mRingerAndDrawerContainer.setLayoutDirection(LAYOUT_DIRECTION_RTL);
+ }
+
+ ViewGroup container = mDialog.findViewById(R.id.volume_dialog_container);
+ setGravity(container, Gravity.LEFT);
+ setLayoutGravity(container, Gravity.LEFT);
+
+ setGravity(mDialogView, Gravity.LEFT);
+ setLayoutGravity(mDialogView, Gravity.LEFT);
+
+ setGravity((ViewGroup) mTopContainer, Gravity.LEFT);
+
+ setLayoutGravity(mSelectedRingerContainer, Gravity.BOTTOM | Gravity.LEFT);
+
+ setLayoutGravity(mRingerDrawerNewSelectionBg, Gravity.BOTTOM | Gravity.LEFT);
+
+ setGravity(mRinger, Gravity.LEFT);
+ setLayoutGravity(mRinger, Gravity.BOTTOM | Gravity.LEFT);
+
+ setGravity(mDialogRowsViewContainer, Gravity.LEFT);
+ setLayoutGravity(mDialogRowsViewContainer, Gravity.LEFT);
+
+ setGravity(mODICaptionsView, Gravity.LEFT);
+ setLayoutGravity(mODICaptionsView, Gravity.LEFT);
+
+ mExpandRows.setRotation(-90);
+ }
+
if (mRows.isEmpty()) {
if (!AudioSystem.isSingleVolume(mContext)) {
addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
@@ -723,6 +827,25 @@
mRingerCount = mShowVibrate ? 3 : 2;
}
+ // Helper to set gravity.
+ private void setGravity(ViewGroup viewGroup, int gravity) {
+ if (viewGroup instanceof LinearLayout) {
+ ((LinearLayout) viewGroup).setGravity(gravity);
+ }
+ }
+
+ // Helper to set layout gravity.
+ private void setLayoutGravity(ViewGroup viewGroup, int gravity) {
+ if (viewGroup != null) {
+ Object obj = viewGroup.getLayoutParams();
+ if (obj instanceof FrameLayout.LayoutParams) {
+ ((FrameLayout.LayoutParams) obj).gravity = gravity;
+ } else if (obj instanceof LinearLayout.LayoutParams) {
+ ((LinearLayout.LayoutParams) obj).gravity = gravity;
+ }
+ }
+ }
+
protected ViewGroup getDialogView() {
return mDialogView;
}
@@ -735,7 +858,7 @@
}
private boolean shouldSlideInVolumeTray() {
- return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION;
+ return mContext.getDisplay().getRotation() != RotationPolicy.getNaturalRotation();
}
private boolean isLandscape() {
@@ -744,8 +867,7 @@
}
private boolean isRtl() {
- return mContext.getResources().getConfiguration().getLayoutDirection()
- == LAYOUT_DIRECTION_RTL;
+ return mDialogView.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
}
public void setStreamImportant(int stream, boolean important) {
@@ -940,6 +1062,12 @@
mDialogView.getPaddingTop(),
mDialogView.getPaddingRight(),
mDialogView.getPaddingBottom() + getRingerDrawerOpenExtraSize());
+ } else if (mVolumePanelOnLeft) {
+ mDialogView.setPadding(
+ mDialogView.getPaddingLeft(),
+ mDialogView.getPaddingTop(),
+ mDialogView.getPaddingRight() + getRingerDrawerOpenExtraSize(),
+ mDialogView.getPaddingBottom());
} else {
mDialogView.setPadding(
mDialogView.getPaddingLeft() + getRingerDrawerOpenExtraSize(),
@@ -1010,15 +1138,22 @@
}
/**
- * Translation to apply form the origin (either top or left) to overlap the selection background
- * with the given mode in the drawer.
+ * Translation to apply form the origin (either top or left/right) to overlap the selection
+ * background with the given mode in the drawer.
*/
private float getTranslationInDrawerForRingerMode(int mode) {
- return mode == RINGER_MODE_VIBRATE
+ return (mode == RINGER_MODE_VIBRATE
? -mRingerDrawerItemSize * 2
: mode == RINGER_MODE_SILENT
? -mRingerDrawerItemSize
- : 0;
+ : 0)
+ * (isLandscape()
+ ? getTranslationForPanelLocation()
+ : 1);
+ }
+
+ private float getTranslationForPanelLocation() {
+ return mVolumePanelOnLeft ? -1 : 1;
}
@VisibleForTesting String getSelectedRingerContainerDescription() {
@@ -1062,12 +1197,13 @@
getTranslationInDrawerForRingerMode(mState.ringerModeInternal));
}
- // Move the drawer so that the top/rightmost ringer choice overlaps with the selected ringer
+ // Move the drawer so that the top/outmost ringer choice overlaps with the selected ringer
// icon.
if (!isLandscape()) {
mRingerDrawerContainer.setTranslationY(mRingerDrawerItemSize * (mRingerCount - 1));
} else {
- mRingerDrawerContainer.setTranslationX(mRingerDrawerItemSize * (mRingerCount - 1));
+ mRingerDrawerContainer.setTranslationX(
+ getTranslationForPanelLocation() * mRingerDrawerItemSize * (mRingerCount - 1));
}
mRingerDrawerContainer.setAlpha(0f);
mRingerDrawerContainer.setVisibility(VISIBLE);
@@ -1144,7 +1280,7 @@
.start();
} else {
mRingerDrawerContainer.animate()
- .translationX(mRingerDrawerItemSize * 2)
+ .translationX(getTranslationForPanelLocation() * mRingerDrawerItemSize * 2)
.start();
}
@@ -1162,6 +1298,70 @@
mIsRingerDrawerOpen = false;
}
+ /**
+ * Returns a {@link MediaController} that state is playing and type is local playback,
+ * and also have active sessions.
+ */
+ @Nullable
+ private MediaController getActiveLocalMediaController() {
+ MediaSessionManager mediaSessionManager =
+ mContext.getSystemService(MediaSessionManager.class);
+ MediaController localController = null;
+ final List<String> remoteMediaSessionLists = new ArrayList<>();
+ for (MediaController controller : mediaSessionManager.getActiveSessions(null)) {
+ final MediaController.PlaybackInfo pi = controller.getPlaybackInfo();
+ if (pi == null) {
+ // do nothing
+ continue;
+ }
+ final PlaybackState playbackState = controller.getPlaybackState();
+ if (playbackState == null) {
+ // do nothing
+ continue;
+ }
+ if (D.BUG) {
+ Log.d(TAG, "getActiveLocalMediaController() package name : "
+ + controller.getPackageName()
+ + ", play back type : " + pi.getPlaybackType()
+ + ", play back state : " + playbackState.getState());
+ }
+ if (playbackState.getState() != PlaybackState.STATE_PLAYING) {
+ // do nothing
+ continue;
+ }
+ if (pi.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ if (localController != null
+ && TextUtils.equals(
+ localController.getPackageName(), controller.getPackageName())) {
+ localController = null;
+ }
+ if (!remoteMediaSessionLists.contains(controller.getPackageName())) {
+ remoteMediaSessionLists.add(controller.getPackageName());
+ }
+ continue;
+ }
+ if (pi.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+ if (localController == null
+ && !remoteMediaSessionLists.contains(controller.getPackageName())) {
+ localController = controller;
+ }
+ }
+ }
+ return localController;
+ }
+
+ private boolean isBluetoothA2dpConnected() {
+ final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() &&
+ mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP)
+ == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ private boolean isMediaControllerAvailable() {
+ final MediaController mediaController = getActiveLocalMediaController();
+ return mediaController != null &&
+ !TextUtils.isEmpty(mediaController.getPackageName());
+ }
/**
* @param open false to set the description when drawer is closed
@@ -1190,18 +1390,37 @@
}
private void initSettingsH(int lockTaskModeState) {
+ if (mRoundedBorderBottom != null){
+ mRoundedBorderBottom.setVisibility(!mDeviceProvisionedController.isCurrentUserSetup() ||
+ mActivityManager.getLockTaskModeState() != LOCK_TASK_MODE_NONE
+ ? VISIBLE : GONE);
+ }
if (mSettingsView != null) {
mSettingsView.setVisibility(
mDeviceProvisionedController.isCurrentUserSetup() &&
+ (isMediaControllerAvailable() || isBluetoothA2dpConnected()) &&
lockTaskModeState == LOCK_TASK_MODE_NONE ? VISIBLE : GONE);
}
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
+ String packageName = isMediaControllerAvailable()
+ ? getActiveLocalMediaController().getPackageName() : "";
+ mMediaOutputDialogFactory.create(packageName, true, mDialogView);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
- mMediaOutputDialogFactory.dismiss();
- mVolumeNavigator.openVolumePanel(
- mVolumePanelNavigationInteractor.getVolumePanelRoute());
+ });
+ }
+
+ if (mExpandRowsView != null) {
+ mExpandRowsView.setVisibility(
+ mDeviceProvisionedController.isCurrentUserSetup() &&
+ lockTaskModeState == LOCK_TASK_MODE_NONE ? VISIBLE : GONE);
+ }
+ if (mExpandRows != null) {
+ mExpandRows.setOnClickListener(v -> {
+ mExpanded = !mExpanded;
+ updateRowsH(mDefaultRow, true);
+ mExpandRows.setExpanded(mExpanded);
});
}
}
@@ -1479,6 +1698,10 @@
mConfigChanged = false;
}
+ if (mDefaultRow == null) {
+ mDefaultRow = getActiveRow();
+ }
+
initSettingsH(lockTaskModeState);
mShowing = true;
mIsAnimatingDismiss = false;
@@ -1588,9 +1811,16 @@
mDialog.dismiss();
}
tryToRemoveCaptionsTooltip();
+ mExpanded = false;
+ if (mExpandRows != null) {
+ mExpandRows.setExpanded(mExpanded);
+ }
+ mAnimatingRows = 0;
+ mDefaultRow = null;
mIsAnimatingDismiss = false;
hideRingerDrawer();
+ mController.notifyVisible(false);
}, 50));
if (!shouldSlideInVolumeTray()) {
animator.translationX(
@@ -1615,6 +1845,13 @@
|| mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
}
+ private boolean isExpandableRowH(VolumeRow row) {
+ return row != null && row != mDefaultRow && !row.defaultStream
+ && (row.stream == STREAM_RING
+ || row.stream == STREAM_ALARM
+ || row.stream == STREAM_MUSIC);
+ }
+
private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
boolean isActive = row.stream == activeRow.stream;
@@ -1634,10 +1871,18 @@
return true;
}
- if (row.defaultStream) {
+ if (mExpanded && isExpandableRowH(row)) {
+ return true;
+ }
+
+ // if the row is the default stream or the row with which this panel was created,
+ // show it additonally to the active row if it is one of the following streams
+ if (row.defaultStream || mDefaultRow == row) {
return activeRow.stream == STREAM_RING
+ || activeRow.stream == STREAM_NOTIFICATION
|| activeRow.stream == STREAM_ALARM
|| activeRow.stream == STREAM_VOICE_CALL
+ || activeRow.stream == STREAM_MUSIC
|| activeRow.stream == STREAM_ACCESSIBILITY
|| mDynamic.get(activeRow.stream);
}
@@ -1653,29 +1898,39 @@
private void updateRowsH(final VolumeRow activeRow) {
Trace.beginSection("VolumeDialogImpl#updateRowsH");
+ updateRowsH(activeRow, false);
+ }
+
+ private void updateRowsH(final VolumeRow activeRow, boolean animate) {
if (D.BUG) Log.d(TAG, "updateRowsH");
if (!mShowing) {
trimObsoleteH();
}
+ boolean isOutmostIndexMax = mVolumePanelOnLeft ? isRtl() : !isRtl();
+
// Index of the last row that is actually visible.
- int rightmostVisibleRowIndex = !isRtl() ? -1 : Short.MAX_VALUE;
+ int outmostVisibleRowIndex = isOutmostIndexMax ? -1 : Short.MAX_VALUE;
// apply changes to all rows
for (final VolumeRow row : mRows) {
final boolean isActive = row == activeRow;
+ final boolean isExpandableRow = isExpandableRowH(row);
final boolean shouldBeVisible = shouldBeVisibleH(row, activeRow);
- Util.setVisOrGone(row.view, shouldBeVisible);
- if (shouldBeVisible && mRingerAndDrawerContainerBackground != null) {
- // For RTL, the rightmost row has the lowest index since child views are laid out
+ if (!isExpandableRow) {
+ Util.setVisOrGone(row.view, shouldBeVisible);
+ } else if (!mExpanded) {
+ row.view.setVisibility(View.INVISIBLE);
+ }
+
+ if ((shouldBeVisible || isExpandableRow)
+ && mRingerAndDrawerContainerBackground != null) {
+ // For RTL, the outmost row has the lowest index since child views are laid out
// from right to left.
- rightmostVisibleRowIndex =
- !isRtl()
- ? Math.max(rightmostVisibleRowIndex,
- mDialogRowsView.indexOfChild(row.view))
- : Math.min(rightmostVisibleRowIndex,
- mDialogRowsView.indexOfChild(row.view));
+ outmostVisibleRowIndex = isOutmostIndexMax
+ ? Math.max(outmostVisibleRowIndex, mDialogRowsView.indexOfChild(row.view))
+ : Math.min(outmostVisibleRowIndex, mDialogRowsView.indexOfChild(row.view));
// Add spacing between each of the visible rows - we'll remove the spacing from the
// last row after the loop.
@@ -1683,12 +1938,13 @@
if (layoutParams instanceof LinearLayout.LayoutParams) {
final LinearLayout.LayoutParams linearLayoutParams =
((LinearLayout.LayoutParams) layoutParams);
- if (!isRtl()) {
+ if (isOutmostIndexMax) {
linearLayoutParams.setMarginEnd(mRingerRowsPadding);
} else {
linearLayoutParams.setMarginStart(mRingerRowsPadding);
}
}
+ row.view.setLayoutParams(layoutParams);
// Set the background on each of the rows. We'll remove this from the last row after
// the loop, since the last row's background is drawn by the main volume container.
@@ -1696,13 +1952,13 @@
mContext.getDrawable(R.drawable.volume_row_rounded_background));
}
- if (row.view.isShown()) {
+ if (row.view.isShown() || isExpandableRow) {
updateVolumeRowTintH(row, isActive);
}
}
- if (rightmostVisibleRowIndex > -1 && rightmostVisibleRowIndex < Short.MAX_VALUE) {
- final View lastVisibleChild = mDialogRowsView.getChildAt(rightmostVisibleRowIndex);
+ if (outmostVisibleRowIndex > -1 && outmostVisibleRowIndex < Short.MAX_VALUE) {
+ final View lastVisibleChild = mDialogRowsView.getChildAt(outmostVisibleRowIndex);
final ViewGroup.LayoutParams layoutParams = lastVisibleChild.getLayoutParams();
// Remove the spacing on the last row, and remove its background since the container is
// drawing a background for this row.
@@ -1711,8 +1967,106 @@
((LinearLayout.LayoutParams) layoutParams);
linearLayoutParams.setMarginStart(0);
linearLayoutParams.setMarginEnd(0);
+ lastVisibleChild.setLayoutParams(linearLayoutParams);
lastVisibleChild.setBackgroundColor(Color.TRANSPARENT);
}
+
+ int elevationCount = 0;
+ if (animate) {
+ // Increase the elevation of the outmost row so that other rows animate behind it.
+ lastVisibleChild.setElevation(1f / ++elevationCount);
+
+ // Add a solid background to the outmost row temporary so that other rows animate
+ // behind it
+ lastVisibleChild.setBackgroundDrawable(
+ mContext.getDrawable(R.drawable.volume_background));
+ }
+
+ int[] lastVisibleChildLocation = new int[2];
+ lastVisibleChild.getLocationInWindow(lastVisibleChildLocation);
+
+ // Track previous rows to calculate translations
+ int visibleRowsCount = 0;
+ int hiddenRowsCount = 0;
+ int rowWidth = mDialogWidth + mRingerRowsPadding;
+
+ for (final VolumeRow row : mRows) {
+ final boolean isExpandableRow = isExpandableRowH(row);
+ final boolean shouldBeVisible = shouldBeVisibleH(row, activeRow);
+
+ if (shouldBeVisible) {
+ visibleRowsCount++;
+ } else if (isExpandableRow) {
+ hiddenRowsCount++;
+ }
+
+ // Only move rows that are either expandable or visible and not the default stream
+ if ((!isExpandableRow && !shouldBeVisible) || row.defaultStream) {
+ continue;
+ }
+
+ // Cancel any ongoing animations
+ row.view.animate().cancel();
+
+ // Expandable rows need to animate behind the last visible row, an additional
+ // always visible stream animates next to the default stream
+ float translationX = getTranslationForPanelLocation() * hiddenRowsCount * rowWidth;
+
+ if (animate) {
+ // If the translation is not set and the panel is expanding start the
+ // animation behind the main row
+ if (isExpandableRow && mExpanded && row.view.getTranslationX() == 0f) {
+ row.view.setTranslationX(
+ getTranslationForPanelLocation() * visibleRowsCount * rowWidth);
+ }
+
+ // The elevation should decrease from the outmost row to the inner rows, so that
+ // every row animates behind the outer rows
+ row.view.setElevation(1f / ++elevationCount);
+
+ row.view.setVisibility(View.VISIBLE);
+
+ // Track how many rows are animating to avoid running animation end actions
+ // if there is still a row animating
+ mAnimatingRows++;
+ row.view.animate()
+ .translationX(translationX)
+ .setDuration(mExpanded ? mDialogShowAnimationDurationMs
+ : mDialogHideAnimationDurationMs)
+ .setInterpolator(mExpanded
+ ? new SystemUIInterpolators.LogDecelerateInterpolator()
+ : new SystemUIInterpolators.LogAccelerateInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mAnimatingRows--;
+ animation.removeAllListeners();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ row.view.setElevation(0);
+ if (!shouldBeVisible) {
+ row.view.setVisibility(View.INVISIBLE);
+ }
+ mAnimatingRows--;
+ if (mAnimatingRows == 0) {
+ // Restore the elevation and background
+ lastVisibleChild.setElevation(0);
+ lastVisibleChild.setBackgroundColor(Color.TRANSPARENT);
+ // Set the active stream to ensure the volume keys change
+ // the volume of the tinted row. The tint was set before
+ // already, but setting the active row cancels ongoing
+ // animations.
+ mController.setActiveStream(activeRow.stream);
+ }
+ }
+ });
+ } else {
+ row.view.setTranslationX(translationX);
+ row.view.setVisibility(shouldBeVisible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
}
updateBackgroundForDrawerClosedAmount();
@@ -2015,13 +2369,14 @@
}
final ColorStateList colorTint = useActiveColoring
? Utils.getColorAccent(mContext)
- : Utils.getColorAttr(mContext, com.android.internal.R.attr.colorAccentSecondary);
+ : Utils.getColorAttr(mContext, com.android.internal.R.attr.colorAccentPrimary);
final int alpha = useActiveColoring
? Color.alpha(colorTint.getDefaultColor())
: getAlphaAttr(android.R.attr.secondaryContentAlpha);
- final ColorStateList bgTint = Utils.getColorAttr(
- mContext, android.R.attr.colorBackgroundFloating);
+ final ColorStateList bgTint = useActiveColoring
+ ? Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
+ : Utils.getColorAttr(mContext, com.android.internal.R.attr.textColorOnAccent);
final ColorStateList inverseTextTint = Utils.getColorAttr(
mContext, com.android.internal.R.attr.textColorOnAccent);
@@ -2221,6 +2576,21 @@
return (visibleRows - 1) * (mDialogWidth + mRingerRowsPadding);
}
+ /**
+ * Return the size of invisible rows.
+ * Expandable rows are invisible while the panel is not expanded.
+ */
+ private int getExpandableRowsExtraSize() {
+ VolumeRow activeRow = getActiveRow();
+ int expandableRows = 0;
+ for (final VolumeRow row : mRows) {
+ if (isExpandableRowH(row)) {
+ expandableRows++;
+ }
+ }
+ return expandableRows * (mDialogWidth + mRingerRowsPadding);
+ }
+
private void updateBackgroundForDrawerClosedAmount() {
if (mRingerAndDrawerContainerBackground == null) {
return;
@@ -2229,6 +2599,9 @@
final Rect bounds = mRingerAndDrawerContainerBackground.copyBounds();
if (!isLandscape()) {
bounds.top = (int) (mRingerDrawerClosedAmount * getRingerDrawerOpenExtraSize());
+ } else if (mVolumePanelOnLeft) {
+ bounds.right = (int) ((mDialogCornerRadius / 2) + mRingerDrawerItemSize
+ + (1f - mRingerDrawerClosedAmount) * getRingerDrawerOpenExtraSize());
} else {
bounds.left = (int) (mRingerDrawerClosedAmount * getRingerDrawerOpenExtraSize());
}
@@ -2236,7 +2609,7 @@
}
/*
- * The top container is responsible for drawing the solid color background behind the rightmost
+ * The top container is responsible for drawing the solid color background behind the outmost
* (primary) volume row. This is because the volume drawer animates in from below, initially
* overlapping the primary row. We need the drawer to draw below the row's SeekBar, since it
* looks strange to overlap it, but above the row's background color, since otherwise it will be
@@ -2270,8 +2643,9 @@
? mDialogRowsViewContainer.getTop()
: mDialogRowsViewContainer.getTop() - mDialogCornerRadius);
- // Set gravity to top-right, since additional rows will be added on the left.
- background.setLayerGravity(0, Gravity.TOP | Gravity.RIGHT);
+ // Set gravity to top and opposite side where additional rows will be added.
+ background.setLayerGravity(
+ 0, mVolumePanelOnLeft ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT);
// In landscape, the ringer drawer animates out to the left (instead of down). Since the
// drawer comes from the right (beyond the bounds of the dialog), we should clip it so it
@@ -2320,7 +2694,6 @@
@Override
public void onLayoutDirectionChanged(int layoutDirection) {
- mDialogView.setLayoutDirection(layoutDirection);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index df75ea7..51c749c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -144,7 +144,7 @@
assumeFalse(enableTaskbarNavbarUnification());
// Large screens may be using taskbar and the logic is different
- mNavigationBarController.mIsLargeScreen = false;
+ mNavigationBarController.mTaskbarShowing = false;
doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
mNavigationBarController.createNavigationBars(true, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 2c14308..9750ed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -58,6 +58,7 @@
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var tile: QSTile
@Mock private lateinit var tileLayout: TileLayout
+ @Mock private lateinit var quickQsBrightnessController: QuickQSBrightnessController
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
private val uiEventLogger = UiEventLoggerFake()
@@ -89,7 +90,8 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager
+ dumpManager,
+ quickQsBrightnessController
)
controller.init()
@@ -147,6 +149,10 @@
verify(mediaHost).expansion = MediaHostState.EXPANDED
}
+ fun testBrightnessVisibilityRefreshedWhenConfigurationChanged() {
+ verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
+ }
+
class TestQuickQSPanelController(
view: QuickQSPanel,
qsHost: QSHost,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index f809259..4907ccb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -165,7 +165,7 @@
.layout(left2.capture(), top2.capture(), right2.capture(), bottom2.capture());
// We assume two tiles will always fit side-by-side.
- assertTrue(mSpyContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1);
+ assertTrue(mSpyContext.getResources().getInteger(R.integer.quick_qs_panel_max_tiles) > 1);
// left <= right, top <= bottom
assertTrue(left1.getValue() <= right1.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index efbfb4f..053b969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -138,7 +138,7 @@
WifiIndicators indicators = new WifiIndicators(
false, mock(IconState.class),
qsIcon, false, false, "",
- false, "");
+ false, "", true);
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -152,7 +152,7 @@
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
qsIcon, false, false, "",
- false, "");
+ false, "", true);
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -164,7 +164,7 @@
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
qsIcon, false, false, "",
- false, "");
+ false, "", true);
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 288facc..5544b48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -157,7 +157,8 @@
/* activityOut= */ false,
/* description= */ null,
/* isTransient= */ false,
- /* statusLabel= */ null
+ /* statusLabel= */ null,
+ /* isDefault= */ true
);
mTile.mSignalCallback.setWifiIndicators(wifiIndicators);
IconState state = new IconState(true, 0, "");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index e7056c7..6d7af01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -300,7 +300,7 @@
MobileDataIndicators indicators = new MobileDataIndicators(
mock(IconState.class),
mock(IconState.class),
- 0, 0, true, true, "", "", "", 0, true, true);
+ 0, 0, true, true, "", "", "", 0, true, true, true);
mSignalCallback.setMobileDataIndicators(indicators);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 44e3bb4..5f3b6aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -85,7 +85,7 @@
String description = "Test";
String secondaryLabel = "Secondary label";
WifiIndicators indicators = new WifiIndicators(
- enabled, status, qs, in, out, description, true, secondaryLabel);
+ enabled, status, qs, in, out, description, true, secondaryLabel, true);
mHandler.setWifiIndicators(indicators);
waitForCallbacks();
@@ -120,7 +120,7 @@
boolean roaming = true;
MobileDataIndicators indicators = new MobileDataIndicators(
status, qs, type, qsType, in, out, typeDescription,
- typeDescriptionHtml, description, subId, roaming, true);
+ typeDescriptionHtml, description, subId, roaming, true, true);
mHandler.setMobileDataIndicators(indicators);
waitForCallbacks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
index 4708350..69ecf12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
@@ -111,7 +111,7 @@
private fun createSbn(notification: Notification) = StatusBarNotification(
PACKAGE, "opPkg", 0, "tag", 0, 0,
- notification, UserHandle(USER_ID), "", 0
+ notification, UserHandle(USER_ID), "", 0, false /* isContentSecure */
)
private fun createNotificationEntry(sbn: StatusBarNotification) =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index a6dd3cd..2d0e86c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -50,6 +50,9 @@
override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
_isAutoConfirmFeatureEnabled.asStateFlow()
+ private val _patternSize = MutableStateFlow(LockPatternUtils.PATTERN_SIZE_DEFAULT)
+ override val patternSize: StateFlow<Byte> = _patternSize.asStateFlow()
+
private val _authenticationMethod =
MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD)
override val authenticationMethod: StateFlow<AuthenticationMethodModel> =
@@ -219,7 +222,8 @@
isPattern ->
credential.contentEquals(
LockPatternUtils.patternToByteArray(
- expectedCredential as List<LockPatternView.Cell>
+ expectedCredential as List<LockPatternView.Cell>,
+ LockPatternUtils.PATTERN_SIZE_DEFAULT
)
)
else -> error("Unsupported credential type $type!")
@@ -227,7 +231,8 @@
}
private fun List<AuthenticationPatternCoordinate>.toCells(): List<LockPatternView.Cell> {
- return map { coordinate -> LockPatternView.Cell.of(coordinate.y, coordinate.x) }
+ return map { coordinate -> LockPatternView.Cell.of(
+ coordinate.y, coordinate.x, LockPatternUtils.PATTERN_SIZE_DEFAULT) }
}
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index e685d4f..9bb5a5b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -96,6 +96,10 @@
@Override
public void removeOnMetadataChangedListener(CachedBluetoothDevice device,
BluetoothAdapter.OnMetadataChangedListener listener) {
+ }
+ @Override
+ public int getBatteryLevel() {
+ return 0;
}
}
diff --git a/services/Android.bp b/services/Android.bp
index 8709692..857a9e5 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -108,6 +108,7 @@
filegroup {
name: "services-non-updatable-sources",
srcs: [
+ ":services.applock-sources",
":services.core-sources",
":services.core-sources-am-wm",
"core/java/com/android/server/am/package.html",
@@ -199,6 +200,7 @@
// The convention is to name each service module 'services.$(module_name)'
static_libs: [
+ "services.applock",
"services.core",
"services.accessibility",
"services.appprediction",
diff --git a/services/applock/Android.bp b/services/applock/Android.bp
new file mode 100644
index 0000000..a935f28
--- /dev/null
+++ b/services/applock/Android.bp
@@ -0,0 +1,29 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.applock-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt"
+ ],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.applock",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.applock-sources"],
+ libs: ["services.core"],
+ static_libs: [
+ "kotlinx_coroutines"
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+}
diff --git a/services/applock/jarjar-rules.txt b/services/applock/jarjar-rules.txt
new file mode 100644
index 0000000..c99a1c4
--- /dev/null
+++ b/services/applock/jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule kotlin.** com.android.server.jarjar.@0
+rule kotlinx.** com.android.server.jarjar.@0
diff --git a/services/applock/java/com/android/server/app/AppLockConfig.kt b/services/applock/java/com/android/server/app/AppLockConfig.kt
new file mode 100644
index 0000000..c203602
--- /dev/null
+++ b/services/applock/java/com/android/server/app/AppLockConfig.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.app
+
+import android.app.AppLockData
+import android.app.AppLockManager.DEFAULT_BIOMETRICS_ALLOWED
+import android.app.AppLockManager.DEFAULT_HIDE_IN_LAUNCHER
+import android.app.AppLockManager.DEFAULT_REDACT_NOTIFICATION
+import android.app.AppLockManager.DEFAULT_TIMEOUT
+import android.os.FileUtils
+import android.os.FileUtils.S_IRWXU
+import android.os.FileUtils.S_IRWXG
+import android.util.ArrayMap
+import android.util.Slog
+
+import java.io.File
+import java.io.IOException
+
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+
+private const val APP_LOCK_DIR_NAME = "app_lock"
+private const val APP_LOCK_CONFIG_FILE = "app_lock_config.json"
+
+private const val CURRENT_VERSION = 2
+
+// Only in version 0
+private const val KEY_PACKAGES = "packages"
+private const val KEY_SECURE_NOTIFICATION = "secure_notification"
+
+// From version 1 and up. Non existent version key
+// is considered as version 0
+private const val KEY_VERSION = "version"
+private const val KEY_TIMEOUT = "timeout"
+private const val KEY_APP_LOCK_DATA_LIST = "app_lock_data_list"
+private const val KEY_PACKAGE_NAME = "package_name"
+private const val KEY_REDACT_NOTIFICATION = "redact_notification"
+private const val KEY_BIOMETRICS_ALLOWED = "biometrics_allowed"
+
+// From version 2 and up.
+private const val KEY_HIDE_FROM_LAUNCHER = "hide_from_launcher"
+
+/**
+ * Container for app lock configuration. Also handles logic of reading
+ * and writing configuration to disk, serialized as a JSON file.
+ * All operations must be synchronized with an external lock.
+ *
+ * @hide
+ */
+internal class AppLockConfig(dataDir: File) {
+
+ private val appLockDir = File(dataDir, APP_LOCK_DIR_NAME)
+ private val appLockConfigFile = File(appLockDir, APP_LOCK_CONFIG_FILE)
+
+ private val appLockDataMap = ArrayMap<String, AppLockData>()
+
+ var appLockTimeout: Long = DEFAULT_TIMEOUT
+ var biometricsAllowed = DEFAULT_BIOMETRICS_ALLOWED
+
+ init {
+ appLockDir.mkdirs()
+ FileUtils.setPermissions(appLockDir, S_IRWXU or S_IRWXG, -1, -1)
+ }
+
+ /**
+ * Add an application to [appLockDataMap].
+ *
+ * @param packageName the package name of the application.
+ * @return true if package was added, false if already exists.
+ */
+ fun addPackage(packageName: String): Boolean {
+ return if (!isPackageProtected(packageName)) {
+ appLockDataMap[packageName] =
+ AppLockData(
+ packageName,
+ DEFAULT_REDACT_NOTIFICATION,
+ DEFAULT_HIDE_IN_LAUNCHER
+ )
+ true
+ } else {
+ false
+ }
+ }
+
+ /**
+ * Remove an application from [appLockDataMap].
+ *
+ * @param packageName the package name of the application.
+ * @return true if package was removed, false otherwise.
+ */
+ fun removePackage(packageName: String): Boolean {
+ return appLockDataMap.remove(packageName) != null
+ }
+
+ /**
+ * Get all the packages protected with app lock.
+ *
+ * @return a unique list of package names.
+ */
+ fun getAppLockDataList(): List<AppLockData> {
+ return appLockDataMap.values.toList()
+ }
+
+ /**
+ * Check whether a package is protected with app lock.
+ *
+ * @return true if package is protected, false otherwise.
+ */
+ fun isPackageProtected(packageName: String): Boolean {
+ return appLockDataMap.containsKey(packageName)
+ }
+
+ /**
+ * Set notifications as protected or not for an application
+ * in [appLockDataMap].
+ *
+ * @param packageName the package name of the application.
+ * @param shouldRedactNotification whether to redact notification or not.
+ * @return true if config was changed, false otherwise.
+ */
+ fun setShouldRedactNotification(packageName: String, shouldRedactNotification: Boolean): Boolean {
+ return appLockDataMap[packageName]?.let {
+ if (it.shouldRedactNotification != shouldRedactNotification) {
+ appLockDataMap[packageName] = AppLockData(
+ it.packageName,
+ shouldRedactNotification,
+ it.hideFromLauncher
+ )
+ true
+ } else {
+ false
+ }
+ } ?: run {
+ Slog.e(TAG, "Attempt to redact notifications for package $packageName that is not in list")
+ false
+ }
+ }
+
+ /**
+ * Check whether notifications are protected or not for an application
+ * in [appLockDataMap].
+ *
+ * @param packageName the package name of the application.
+ * @return true if notification contents are redacted in app locked state,
+ * false otherwise.
+ */
+ fun shouldRedactNotification(packageName: String): Boolean {
+ return appLockDataMap[packageName]?.shouldRedactNotification == true
+ }
+
+ /**
+ * Mark an application as hidden from launcher in [appLockDataMap].
+ *
+ * @param packageName the package name of the application.
+ * @param hide the parameter value in [AppLockData].
+ * @return true if hidden state was changed, false otherwise.
+ */
+ fun hidePackage(packageName: String, hide: Boolean): Boolean {
+ return appLockDataMap[packageName]?.let {
+ if (it.hideFromLauncher != hide) {
+ appLockDataMap[packageName] = AppLockData(
+ it.packageName,
+ it.shouldRedactNotification,
+ hide
+ )
+ true
+ } else {
+ false
+ }
+ } ?: run {
+ Slog.e(TAG, "Attempt to hide package that is not in list")
+ false
+ }
+ }
+
+ /**
+ * Parse contents from [appLockConfigFile].
+ */
+ fun read() {
+ reset()
+ if (!appLockConfigFile.isFile) {
+ Slog.i(TAG, "No configuration saved")
+ return
+ }
+ try {
+ appLockConfigFile.inputStream().bufferedReader().use {
+ val rootObject = JSONObject(it.readText())
+
+ val version = rootObject.optInt(KEY_VERSION, 0)
+ if (version != CURRENT_VERSION) {
+ migrateData(rootObject, version)
+ }
+
+ appLockTimeout = rootObject.optLong(KEY_TIMEOUT, DEFAULT_TIMEOUT)
+ biometricsAllowed = rootObject.optBoolean(KEY_BIOMETRICS_ALLOWED, DEFAULT_BIOMETRICS_ALLOWED)
+ val appLockDataList = rootObject.optJSONArray(KEY_APP_LOCK_DATA_LIST) ?: return@use
+ for (i in 0 until appLockDataList.length()) {
+ val appLockData = appLockDataList.getJSONObject(i)
+ val packageName = appLockData.getString(KEY_PACKAGE_NAME)
+ appLockDataMap[packageName] = AppLockData(
+ packageName,
+ appLockData.getBoolean(KEY_REDACT_NOTIFICATION),
+ appLockData.getBoolean(KEY_HIDE_FROM_LAUNCHER)
+ )
+ }
+ }
+ } catch(e: IOException) {
+ Slog.wtf(TAG, "Failed to read config file", e)
+ } catch(e: JSONException) {
+ Slog.wtf(TAG, "Failed to parse config file", e)
+ }
+ logD {
+ "readConfig: data = $appLockDataMap, " +
+ "timeout = $appLockTimeout, " +
+ "biometricsAllowed = $biometricsAllowed"
+ }
+ }
+
+ private fun reset() {
+ appLockDataMap.clear()
+ appLockTimeout = DEFAULT_TIMEOUT
+ biometricsAllowed = DEFAULT_BIOMETRICS_ALLOWED
+ }
+
+ private fun migrateData(jsonData: JSONObject, dataVersion: Int) {
+ Slog.i(TAG, "Migrating data from version $dataVersion")
+ when (dataVersion) {
+ 0 -> {
+ val packageObject = jsonData.remove(KEY_PACKAGES) as? JSONObject
+ if (packageObject != null) {
+ val appLockDataList = JSONArray()
+ packageObject.keys().forEach { pkg ->
+ val isSecure = packageObject.getJSONObject(pkg)
+ .optBoolean(KEY_SECURE_NOTIFICATION, DEFAULT_REDACT_NOTIFICATION)
+ appLockDataList.put(
+ JSONObject()
+ .put(KEY_PACKAGE_NAME, pkg)
+ .put(KEY_REDACT_NOTIFICATION, isSecure)
+ )
+ }
+ jsonData.put(KEY_APP_LOCK_DATA_LIST, appLockDataList)
+ }
+ }
+ 1 -> {
+ val appLockDataList = jsonData.optJSONArray(KEY_APP_LOCK_DATA_LIST)
+ if (appLockDataList != null) {
+ val size = appLockDataList.length()
+ if (size > 0) {
+ val backupList = mutableListOf<JSONObject>()
+ for (i in (size - 1)..0) {
+ val appData = appLockDataList.getJSONObject(i)
+ .put(KEY_HIDE_FROM_LAUNCHER, DEFAULT_HIDE_IN_LAUNCHER)
+ backupList.add(appData)
+ appLockDataList.remove(i)
+ }
+ backupList.forEach {
+ appLockDataList.put(it)
+ }
+ jsonData.put(KEY_APP_LOCK_DATA_LIST, appLockDataList)
+ }
+ }
+ }
+ else -> throw IllegalArgumentException("Unknown data version $dataVersion")
+ }
+ val nextVersion = dataVersion + 1
+ if (nextVersion != CURRENT_VERSION) {
+ migrateData(jsonData, nextVersion)
+ }
+ }
+
+ /**
+ * Write contents to [appLockConfigFile].
+ */
+ fun write() {
+ logD {
+ "Writing data to file"
+ }
+ val rootObject = JSONObject()
+ try {
+ rootObject.put(KEY_VERSION, CURRENT_VERSION)
+ rootObject.put(KEY_TIMEOUT, appLockTimeout)
+ rootObject.put(KEY_BIOMETRICS_ALLOWED, biometricsAllowed)
+ rootObject.put(
+ KEY_APP_LOCK_DATA_LIST,
+ JSONArray(
+ appLockDataMap.values.map {
+ JSONObject().apply {
+ put(KEY_PACKAGE_NAME, it.packageName)
+ put(KEY_REDACT_NOTIFICATION, it.shouldRedactNotification)
+ put(KEY_HIDE_FROM_LAUNCHER, it.hideFromLauncher)
+ }
+ }
+ )
+ )
+ } catch(e: JSONException) {
+ Slog.wtf(TAG, "Failed to create json configuration", e)
+ return
+ }
+ try {
+ appLockConfigFile.outputStream().bufferedWriter().use {
+ val flattenedString = rootObject.toString(4)
+ logD {
+ "flattenedString = $flattenedString"
+ }
+ it.write(flattenedString, 0, flattenedString.length)
+ it.flush()
+ }
+ } catch(e: IOException) {
+ Slog.wtf(TAG, "Failed to write config to file", e)
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/applock/java/com/android/server/app/AppLockManagerService.kt b/services/applock/java/com/android/server/app/AppLockManagerService.kt
new file mode 100644
index 0000000..c6752a2
--- /dev/null
+++ b/services/applock/java/com/android/server/app/AppLockManagerService.kt
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.app
+
+import android.Manifest
+import android.annotation.RequiresPermission
+import android.app.Activity
+import android.app.ActivityManager
+import android.app.ActivityManagerInternal
+import android.app.ActivityOptions
+import android.app.ActivityTaskManager
+import android.app.AlarmManager
+import android.app.AppLockData
+import android.app.AppLockManager
+import android.app.IAppLockManagerService
+import android.app.KeyguardManager
+import android.app.PendingIntent
+import android.app.TaskStackListener
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.IntentSender
+import android.content.pm.PackageManager
+import android.content.pm.PackageManagerInternal
+import android.os.Binder
+import android.os.Environment
+import android.os.Process
+import android.os.RemoteException
+import android.os.SystemClock
+import android.os.UserHandle
+import android.util.ArrayMap
+import android.util.ArraySet
+import android.util.Log
+import android.util.Slog
+
+import com.android.internal.R
+import com.android.internal.annotations.GuardedBy
+import com.android.server.LocalServices
+import com.android.server.SystemService
+import com.android.server.app.AppLockManagerServiceInternal
+import com.android.server.notification.NotificationManagerInternal
+import com.android.server.pm.UserManagerInternal
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo
+import com.android.server.wm.ActivityTaskManagerInternal
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+
+internal val TAG = AppLockManagerService::class.simpleName
+
+private const val ACTION_APP_LOCK_TIMEOUT = "com.android.server.app.AppLockManagerService.APP_LOCK_TIMEOUT"
+private const val SETTINGS_PACKAGE = "com.android.settings"
+
+internal inline fun logD(crossinline msg: () -> String) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, msg())
+ }
+}
+
+/**
+ * Service to manage per app lock.
+ *
+ * @hide
+ */
+class AppLockManagerService(
+ private val context: Context
+) : IAppLockManagerService.Stub() {
+
+ private val localService = LocalService()
+ private val serviceScope = CoroutineScope(Dispatchers.Default)
+
+ private val currentUserId: Int
+ get() = activityManagerInternal.currentUserId
+
+ private var isDeviceSecure = false
+
+ private val mutex = Mutex()
+
+ @GuardedBy("mutex")
+ private val userConfigMap = ArrayMap<Int, AppLockConfig>()
+
+ @GuardedBy("mutex")
+ private val topPackages = ArraySet<String>()
+
+ @GuardedBy("mutex")
+ private val unlockedPackages = ArraySet<String>()
+
+ private val biometricUnlocker: BiometricUnlocker by lazy {
+ BiometricUnlocker(context)
+ }
+
+ private val atmInternal: ActivityTaskManagerInternal by lazy {
+ LocalServices.getService(ActivityTaskManagerInternal::class.java)
+ }
+
+ private val notificationManagerInternal: NotificationManagerInternal by lazy {
+ LocalServices.getService(NotificationManagerInternal::class.java)
+ }
+
+ private val keyguardManager: KeyguardManager by lazy {
+ context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
+ }
+
+ private val alarmManager: AlarmManager by lazy {
+ context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ }
+
+ private val userManagerInternal: UserManagerInternal by lazy {
+ LocalServices.getService(UserManagerInternal::class.java)
+ }
+
+ private val activityManagerInternal: ActivityManagerInternal by lazy {
+ LocalServices.getService(ActivityManagerInternal::class.java)
+ }
+
+ private val packageManager: PackageManager by lazy {
+ context.packageManager
+ }
+
+ private val pmInternal: PackageManagerInternal by lazy {
+ LocalServices.getService(PackageManagerInternal::class.java)
+ }
+
+ private var deviceLocked = false
+
+ private val alarmsMutex = Mutex()
+
+ @GuardedBy("alarmsMutex")
+ private val scheduledAlarms = ArrayMap<String, PendingIntent>()
+
+ private val whiteListedSystemApps: List<String> by lazy {
+ val systemPackages = pmInternal.getInstalledApplications(
+ PackageManager.MATCH_SYSTEM_ONLY.toLong(),
+ currentUserId,
+ Process.myUid()
+ ).map { it.packageName }
+ context.resources.getStringArray(R.array.config_appLockAllowedSystemApps).filter {
+ systemPackages.contains(it)
+ }
+ }
+
+ private val packageChangeReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action != Intent.ACTION_PACKAGE_REMOVED) return
+ val userId = getSendingUserId()
+ if (userId != currentUserId) {
+ logD {
+ "Ignoring package removal broadcast from user $userId"
+ }
+ return
+ }
+ val isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false /* defaultValue */)
+ if (isReplacing) {
+ logD {
+ "Ignoring package update broadcast"
+ }
+ return
+ }
+ val packageName = intent.data?.schemeSpecificPart ?: run {
+ Slog.e(TAG, "Failed to get package name")
+ return
+ }
+ serviceScope.launch {
+ val config = mutex.withLock {
+ userConfigMap[userId] ?: run {
+ Slog.e(TAG, "Config unavailable for user $userId")
+ return@launch
+ }
+ }
+ mutex.withLock {
+ if (!config.isPackageProtected(packageName)) {
+ logD {
+ "Package $packageName not in the list, ignoring"
+ }
+ return@launch
+ }
+ }
+ logD {
+ "Package $packageName uninstalled, cleaning up"
+ }
+ alarmsMutex.withLock {
+ scheduledAlarms.remove(packageName)?.let {
+ alarmManager.cancel(it)
+ }
+ }
+ mutex.withLock {
+ unlockedPackages.remove(packageName)
+ if (config.removePackage(packageName)) {
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private val taskStackListener = object : TaskStackListener() {
+ override fun onTaskStackChanged() {
+ logD {
+ "onTaskStackChanged"
+ }
+ serviceScope.launch {
+ val currentTopPackages = atmInternal.topVisibleActivities.map {
+ it.activityToken
+ }.filter {
+ atmInternal.isVisibleActivity(it)
+ }.map {
+ atmInternal.getActivityName(it)?.packageName
+ }.filterNotNull().toSet()
+ logD {
+ "currentTopPackages = $currentTopPackages"
+ }
+ // We should return early if current top packages
+ // are empty to avoid doing anything absurd.
+ if (currentTopPackages.isEmpty()) return@launch
+ val packagesToLock = mutex.withLock {
+ logD {
+ "topPackages = $topPackages"
+ }
+ val packages = topPackages.filter {
+ !currentTopPackages.contains(it) && unlockedPackages.contains(it)
+ }.toSet()
+ topPackages.clear()
+ topPackages.addAll(currentTopPackages)
+ return@withLock packages
+ }
+ packagesToLock.forEach {
+ scheduleLockAlarm(it)
+ }
+ alarmsMutex.withLock {
+ currentTopPackages.forEach { pkg ->
+ scheduledAlarms.remove(pkg)?.let {
+ logD {
+ "Cancelling timeout alarm for $pkg"
+ }
+ alarmManager.cancel(it)
+ }
+ }
+ }
+ currentTopPackages.forEach {
+ checkAndUnlockPackage(it)
+ }
+ }
+ }
+
+ override fun onActivityUnpinned() {
+ logD {
+ "onActivityUnpinned"
+ }
+ onTaskStackChanged()
+ }
+ }
+
+ private fun scheduleLockAlarm(pkg: String) {
+ logD {
+ "scheduleLockAlarm, package = $pkg"
+ }
+ serviceScope.launch {
+ alarmsMutex.withLock {
+ if (scheduledAlarms.containsKey(pkg)) {
+ logD {
+ "Alarm already scheduled for package $pkg"
+ }
+ return@launch
+ }
+ }
+ val timeout = mutex.withLock {
+ userConfigMap[currentUserId]?.appLockTimeout
+ } ?: run {
+ Slog.e(TAG, "Failed to retrieve user config for $currentUserId")
+ return@launch
+ }
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ pkg.hashCode(),
+ Intent(ACTION_APP_LOCK_TIMEOUT).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, pkg)
+ },
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + timeout,
+ pendingIntent
+ )
+ alarmsMutex.withLock {
+ scheduledAlarms[pkg] = pendingIntent
+ }
+ }
+ }
+
+ private fun checkAndUnlockPackage(pkg: String) {
+ if (!isDeviceSecure) return
+ serviceScope.launch {
+ mutex.withLock {
+ if (unlockedPackages.contains(pkg)) return@launch
+ val config = userConfigMap[currentUserId] ?: run {
+ Slog.e(TAG, "Config unavailable for user $currentUserId")
+ return@launch
+ }
+ if (!config.isPackageProtected(pkg)) return@launch
+ }
+ logD {
+ "$pkg is locked out, asking user to unlock"
+ }
+ unlockInternal(pkg, currentUserId,
+ onSuccess = {
+ serviceScope.launch {
+ mutex.withLock {
+ unlockedPackages.add(pkg)
+ }
+ }
+ },
+ onCancel = {
+ // Send user to home on cancel
+ context.mainExecutor.execute {
+ atmInternal.startHomeActivity(currentUserId,
+ "unlockInternal#onCancel")
+ }
+ }
+ )
+ }
+ }
+
+ private val lockAlarmReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action != ACTION_APP_LOCK_TIMEOUT) return
+ logD {
+ "Lock alarm received"
+ }
+ val packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ?: return
+ logD {
+ "$packageName timed out"
+ }
+ serviceScope.launch {
+ mutex.withLock {
+ if (topPackages.contains(packageName)) {
+ logD {
+ "$packageName is currently in foreground, skipping lock"
+ }
+ // Mark it as unlocked, since it actually is
+ unlockedPackages.add(packageName)
+ return@withLock
+ }
+ unlockedPackages.remove(packageName)
+ }
+ alarmsMutex.withLock {
+ scheduledAlarms.remove(packageName)
+ }
+ val isContentSecure = mutex.withLock {
+ userConfigMap[currentUserId]?.shouldRedactNotification(packageName) ?: run {
+ Slog.e(TAG, "Config unavailable for user $currentUserId")
+ return@launch
+ }
+ }
+ notificationManagerInternal.updateSecureNotifications(
+ packageName,
+ isContentSecure,
+ true /* isBubbleUpSuppressed */,
+ currentUserId
+ )
+ }
+ }
+ }
+
+ private fun getActualUserId(userId: Int, tag: String): Int {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false /* allowAll */,
+ true /* requireFull */, tag, AppLockManagerService::class.qualifiedName)
+ }
+
+ private inline fun <R> clearAndExecute(crossinline block: () -> R): R {
+ val ident = Binder.clearCallingIdentity()
+ try {
+ return block()
+ } finally {
+ Binder.restoreCallingIdentity(ident)
+ }
+ }
+
+ private fun unlockInternal(
+ pkg: String,
+ userId: Int,
+ onSuccess: () -> Unit,
+ onCancel: () -> Unit,
+ ) {
+ clearAndExecute {
+ if (!biometricUnlocker.canUnlock()) {
+ Slog.e(TAG, "Application cannot be unlocked with biometrics or device credentials")
+ return@clearAndExecute
+ }
+ biometricUnlocker.unlock(getLabelForPackage(pkg, userId), onSuccess, onCancel)
+ }
+ }
+
+ private fun getLabelForPackage(pkg: String, userId: Int): String? =
+ try {
+ pmInternal.getApplicationInfo(
+ pkg,
+ PackageManager.MATCH_ALL.toLong(),
+ Process.myUid(),
+ userId,
+ ).loadLabel(packageManager).toString()
+ } catch(e: PackageManager.NameNotFoundException) {
+ Slog.e(TAG, "Package $pkg not found")
+ null
+ }
+
+ /**
+ * Add an application to be protected.
+ *
+ * @param packageName the package name of the app to add.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ * @throws [IllegalArgumentException] if package is a system app that
+ * is not whitelisted in [R.array.config_appLockAllowedSystemApps],
+ * or if package is not installed.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun addPackage(packageName: String, userId: Int) {
+ logD {
+ "addPackage: packageName = $packageName, userId = $userId"
+ }
+ enforceCallingPermission("addPackage")
+ checkPackage(packageName, userId)
+ val actualUserId = getActualUserId(userId, "addPackage")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "addPackage requested by unknown user id $actualUserId")
+ return@withLock
+ }
+ if (!config.addPackage(packageName)) return@withLock
+ // Collapse any active notifications or bubbles for the app.
+ if (!topPackages.contains(packageName)) {
+ notificationManagerInternal.updateSecureNotifications(
+ packageName,
+ true /* isContentSecure */,
+ true /* isBubbleUpSuppressed */,
+ actualUserId
+ )
+ }
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+
+ private fun checkPackage(pkg: String, userId: Int) {
+ try {
+ val aInfo = pmInternal.getApplicationInfo(
+ pkg,
+ PackageManager.MATCH_ALL.toLong(),
+ Process.myUid(),
+ userId
+ )
+ if (!aInfo.isSystemApp()) return
+ if (!whiteListedSystemApps.contains(pkg))
+ throw IllegalArgumentException("System package $pkg is not whitelisted")
+ } catch(e: PackageManager.NameNotFoundException) {
+ throw IllegalArgumentException("Package $pkg is not installed")
+ }
+ }
+
+ /**
+ * Remove an application from the protected packages list.
+ *
+ * @param packageName the package name of the app to remove.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun removePackage(packageName: String, userId: Int) {
+ logD {
+ "removePackage: packageName = $packageName, userId = $userId"
+ }
+ enforceCallingPermission("removePackage")
+ val actualUserId = getActualUserId(userId, "removePackage")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "removePackage requested by unknown user id $actualUserId")
+ return@withLock
+ }
+ if (!config.removePackage(packageName)) return@withLock
+ // Let active notifications be expanded since the app
+ // is no longer protected.
+ notificationManagerInternal.updateSecureNotifications(
+ packageName,
+ false /* isContentSecure */,
+ false /* isBubbleUpSuppressed */,
+ actualUserId
+ )
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the current auto lock timeout.
+ *
+ * @param userId the user id of the caller.
+ * @return the timeout in milliseconds if configuration for
+ * current user exists, -1 otherwise.
+ */
+ override fun getTimeout(userId: Int): Long {
+ logD {
+ "getTimeout: userId = $userId"
+ }
+ val actualUserId = getActualUserId(userId, "getTimeout")
+ return runBlocking {
+ mutex.withLock {
+ userConfigMap[actualUserId]?.let { it.appLockTimeout } ?: run {
+ Slog.e(TAG, "getTimeout requested by unknown user id $actualUserId")
+ -1L
+ }
+ }
+ }
+ }
+
+ /**
+ * Set auto lock timeout.
+ *
+ * @param timeout the timeout in milliseconds. Must be >= 5.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun setTimeout(timeout: Long, userId: Int) {
+ logD {
+ "setTimeout: timeout = $timeout, userId = $userId"
+ }
+ if (timeout < 5L) {
+ throw IllegalArgumentException("Timeout must be greater than or equal to 5")
+ }
+ enforceCallingPermission("setTimeout")
+ val actualUserId = getActualUserId(userId, "setTimeout")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "setTimeout requested by unknown user id $actualUserId")
+ return@withLock
+ }
+ if (config.appLockTimeout == timeout) return@withLock
+ config.appLockTimeout = timeout
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+
+ /**
+ * Get all the packages protected with app lock.
+ *
+ * @param userId the user id of the caller.
+ * @return list of [AppLockData] of the protected apps.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun getPackageData(userId: Int): List<AppLockData> {
+ logD {
+ "getPackages: userId = $userId"
+ }
+ enforceCallingPermission("getPackages")
+ val actualUserId = getActualUserId(userId, "getPackages")
+ return runBlocking {
+ mutex.withLock {
+ userConfigMap[actualUserId]?.getAppLockDataList() ?: run {
+ Slog.e(TAG, "getPackages requested by unknown user id $actualUserId")
+ emptyList()
+ }
+ }
+ }
+ }
+
+ /**
+ * Set whether notification content should be redacted for a package
+ * in locked state.
+ *
+ * @param packageName the package name.
+ * @param shouldRedactNotification true to hide notification content.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun setShouldRedactNotification(
+ packageName: String,
+ shouldRedactNotification: Boolean,
+ userId: Int,
+ ) {
+ logD {
+ "setShouldRedactNotification: packageName = $packageName, userId = $userId"
+ }
+ enforceCallingPermission("setShouldRedactNotification")
+ val actualUserId = getActualUserId(userId, "setShouldRedactNotification")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "setShouldRedactNotification requested by unknown " +
+ "user id $actualUserId")
+ return@withLock
+ }
+ if (!config.setShouldRedactNotification(packageName, shouldRedactNotification)) {
+ return@withLock
+ }
+ val isLocked = !unlockedPackages.contains(packageName)
+ && !topPackages.contains(packageName)
+ val shouldSecureContent = shouldRedactNotification && isLocked
+ notificationManagerInternal.updateSecureNotifications(
+ packageName,
+ shouldSecureContent,
+ isLocked /* isBubbleUpSuppressed */,
+ actualUserId
+ )
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+
+ /**
+ * Set whether to allow unlocking with biometrics.
+ *
+ * @param biometricsAllowed whether to use biometrics.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun setBiometricsAllowed(biometricsAllowed: Boolean, userId: Int) {
+ logD {
+ "setBiometricsAllowed: biometricsAllowed = $biometricsAllowed, userId = $userId"
+ }
+ enforceCallingPermission("setBiometricsAllowed")
+ val actualUserId = getActualUserId(userId, "setBiometricsAllowed")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "setBiometricsAllowed requested by unknown user id $actualUserId")
+ return@withLock
+ }
+ if (config.biometricsAllowed == biometricsAllowed) return@withLock
+ config.biometricsAllowed = biometricsAllowed
+ biometricUnlocker.biometricsAllowed = biometricsAllowed
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether biometrics is allowed for unlocking.
+ *
+ * @return true if biometrics will be used for unlocking, false otheriwse.
+ */
+ override fun isBiometricsAllowed(userId: Int): Boolean {
+ logD {
+ "isBiometricsAllowed: userId = $userId"
+ }
+ val actualUserId = getActualUserId(userId, "isBiometricsAllowed")
+ return runBlocking {
+ mutex.withLock {
+ userConfigMap[actualUserId]?.let { it.biometricsAllowed } ?: run {
+ Slog.e(TAG, "isBiometricsAllowed requested by unknown user id $actualUserId")
+ AppLockManager.DEFAULT_BIOMETRICS_ALLOWED
+ }
+ }
+ }
+ }
+
+ /**
+ * Unlock a package following authentication with credentials.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the name of the package to unlock.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun unlockPackage(packageName: String, userId: Int) {
+ logD {
+ "unlockPackage: packageName = $packageName, userId = $userId"
+ }
+ enforceCallingPermission("unlockPackage")
+ val actualUserId = getActualUserId(userId, "unlockPackage")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "unlockPackage requested by unknown user id $actualUserId")
+ return@launch
+ }
+ if (!config.isPackageProtected(packageName)) {
+ Slog.w(TAG, "Unlock requested for package $packageName " +
+ "that is not in list")
+ return@launch
+ }
+ unlockedPackages.add(packageName)
+ }
+ notificationManagerInternal.updateSecureNotifications(
+ packageName,
+ false /* isContentSecure */,
+ false /* isBubbleUpSuppressed */,
+ actualUserId
+ )
+ }
+ }
+
+ /**
+ * Hide or unhide an application from launcher.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param packageName the name of the package to hide or unhide.
+ * @param hide whether to hide or not.
+ * @param userId the user id of the caller.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun setPackageHidden(packageName: String, hide: Boolean, userId: Int) {
+ logD {
+ "setPackageHidden: packageName = $packageName, hide = $hide, userId = $userId"
+ }
+ enforceCallingPermission("setPackageHidden")
+ val actualUserId = getActualUserId(userId, "setPackageHidden")
+ serviceScope.launch {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "setPackageHidden requested by unknown user id $userId")
+ return@withLock
+ }
+ if (!config.isPackageProtected(packageName)) {
+ Slog.w(TAG, "Hide requested for package $packageName " +
+ "that is not in list")
+ return@withLock
+ }
+ if (config.hidePackage(packageName, hide)) {
+ withContext(Dispatchers.IO) {
+ config.write()
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the list of applications hidden from launcher.
+ * Caller must hold {@link com.android.permission.MANAGE_APP_LOCK}.
+ *
+ * @param userId the user id of the caller.
+ * @return list of package names of the hidden apps.
+ * @throws [SecurityException] if caller does not have permission
+ * [Manifest.permissions.MANAGE_APP_LOCK].
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK)
+ override fun getHiddenPackages(userId: Int): List<String> {
+ enforceCallingPermission("getHiddenPackages")
+ return localService.getHiddenPackages(userId).toList()
+ }
+
+ private fun enforceCallingPermission(msg: String) {
+ context.enforceCallingPermission(Manifest.permission.MANAGE_APP_LOCK, msg)
+ }
+
+ private fun onStart() {
+ LocalServices.addService(AppLockManagerServiceInternal::class.java, localService)
+ }
+
+ private fun onBootCompleted() {
+ Slog.i(TAG, "onBootCompleted")
+ context.registerReceiverAsUser(
+ lockAlarmReceiver,
+ UserHandle.SYSTEM,
+ IntentFilter(ACTION_APP_LOCK_TIMEOUT),
+ null /* broadcastPermission */,
+ null /* scheduler */,
+ Context.RECEIVER_NOT_EXPORTED
+ )
+
+ context.registerReceiverForAllUsers(
+ packageChangeReceiver,
+ IntentFilter(Intent.ACTION_PACKAGE_REMOVED).apply {
+ addDataScheme(IntentFilter.SCHEME_PACKAGE)
+ },
+ null /* broadcastPermission */,
+ null /* scheduler */,
+ Context.RECEIVER_NOT_EXPORTED
+ )
+
+ ActivityTaskManager.getService().registerTaskStackListener(taskStackListener)
+ }
+
+ private fun onUserStarting(userId: Int) {
+ Slog.i(TAG, "onUserStarting: userId = $userId")
+ isDeviceSecure = keyguardManager.isDeviceSecure(userId)
+ logD {
+ "isDeviceSecure = $isDeviceSecure"
+ }
+ serviceScope.launch {
+ mutex.withLock {
+ if (userConfigMap.containsKey(userId)) return@withLock
+ withContext(Dispatchers.IO) {
+ val config = AppLockConfig(Environment.getDataSystemDeDirectory(userId))
+ userConfigMap[userId] = config
+ config.read()
+ biometricUnlocker.biometricsAllowed = config.biometricsAllowed
+ verifyPackagesLocked(config)
+ }
+ }
+ }
+ }
+
+ private fun verifyPackagesLocked(config: AppLockConfig) {
+ val currentPackages = config.getAppLockDataList().map { it.packageName }
+ var size = currentPackages.size
+ if (size == 0) return
+ val installedPackages = pmInternal.getInstalledApplications(
+ PackageManager.MATCH_ALL.toLong(),
+ currentUserId,
+ Process.myUid()
+ ).map { it.packageName }
+ var changed = false
+ logD {
+ "Current packages = $currentPackages"
+ }
+ for (i in 0 until size) {
+ val pkg = currentPackages[i]
+ if (!installedPackages.contains(pkg)) {
+ config.removePackage(pkg)
+ size--
+ changed = true
+ }
+ }
+ logD {
+ val filteredPackages = config.getAppLockDataList().map { it.packageName }
+ "Filtered packages = $filteredPackages"
+ }
+ if (changed) {
+ config.write()
+ }
+ }
+
+ private fun onUserStopping(userId: Int): Job {
+ Slog.i(TAG, "onUserStopping: userId = $userId")
+ return serviceScope.launch {
+ mutex.withLock {
+ unlockedPackages.clear()
+ userConfigMap[userId]?.let {
+ withContext(Dispatchers.IO) {
+ it.write()
+ }
+ }
+ }
+ }
+ }
+
+ private fun onUserSwitching(oldUserId: Int, newUserId: Int) {
+ Slog.i(TAG, "onUserSwitching: oldUserId = $oldUserId, newUserId = $newUserId")
+ serviceScope.launch {
+ if (oldUserId != UserHandle.USER_NULL) {
+ onUserStopping(oldUserId).join()
+ }
+ onUserStarting(newUserId)
+ }
+ }
+
+ private inner class LocalService : AppLockManagerServiceInternal {
+ /**
+ * Check whether user is valid and device is secure
+ */
+ private fun checkUserAndDeviceStatus(userId: Int): Boolean {
+ if (userId < 0) {
+ logD {
+ "Ignoring requireUnlock call for special user $userId"
+ }
+ return false
+ }
+ if (!isDeviceSecure) {
+ logD {
+ "Device is not secure, app does not require unlock"
+ }
+ return false
+ }
+ val isManaged = clearAndExecute {
+ userManagerInternal.isUserManaged(userId)
+ }
+ if (isManaged) {
+ logD {
+ "User id $userId belongs to a work profile, ignoring requireUnlock"
+ }
+ }
+ return !isManaged
+ }
+
+ override fun requireUnlock(packageName: String, userId: Int): Boolean {
+ return requireUnlockInternal(packageName, userId, false /* ignoreLockState */)
+ }
+
+ private fun requireUnlockInternal(
+ packageName: String,
+ userId: Int,
+ ignoreLockState: Boolean,
+ ) : Boolean {
+ if (!checkUserAndDeviceStatus(userId)) return false
+ val isLocked = clearAndExecute {
+ // If device is locked then there is no point in proceeding.
+ !ignoreLockState && keyguardManager.isDeviceLocked()
+ }
+ if (isLocked) {
+ logD {
+ "Device is locked, app does not require unlock"
+ }
+ return false
+ }
+ logD {
+ "requireUnlock: packageName = $packageName"
+ }
+ val actualUserId = getActualUserId(userId, "requireUnlock")
+ return runBlocking {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "requireUnlock queried by unknown user id $actualUserId")
+ return@withLock false
+ }
+ val requireUnlock = config.isPackageProtected(packageName) &&
+ !unlockedPackages.contains(packageName)
+ logD {
+ "requireUnlock = $requireUnlock"
+ }
+ return@withLock requireUnlock
+ }
+ }
+ }
+
+ override fun reportPasswordChanged(userId: Int) {
+ logD {
+ "reportPasswordChanged: userId = $userId"
+ }
+ if (userId != currentUserId) {
+ logD {
+ "Ignoring password change event for user $userId"
+ }
+ return
+ }
+ isDeviceSecure = keyguardManager.isDeviceSecure(userId)
+ logD {
+ "isDeviceSecure = $isDeviceSecure"
+ }
+ }
+
+ override fun shouldRedactNotification(
+ packageName: String,
+ userId: Int,
+ ) : Boolean {
+ if (!checkUserAndDeviceStatus(userId)) return false
+ logD {
+ "shouldRedactNotification: packageName = $packageName, userId = $userId"
+ }
+ val actualUserId = getActualUserId(userId, "shouldRedactNotification")
+ if (!requireUnlockInternal(packageName, userId, true /* ignoreLockState */)) return false
+ return runBlocking {
+ mutex.withLock {
+ val config = userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "shouldRedactNotification queried by " +
+ "unknown user id $actualUserId")
+ return@withLock false
+ }
+ val secure = config.shouldRedactNotification(packageName)
+ logD {
+ "Secure = $secure"
+ }
+ return@withLock secure
+ }
+ }
+ }
+
+ override fun notifyDeviceLocked(locked: Boolean, userId: Int) {
+ logD {
+ "Device locked = $locked for user $userId"
+ }
+ if (userId != currentUserId ||
+ !isDeviceSecure ||
+ deviceLocked == locked) return
+ deviceLocked = locked
+ serviceScope.launch {
+ val config = mutex.withLock {
+ userConfigMap[currentUserId] ?: run {
+ Slog.e(TAG, "Config unavailable for user $currentUserId")
+ return@launch
+ }
+ }
+ if (deviceLocked) {
+ mutex.withLock {
+ if (unlockedPackages.isEmpty()) return@withLock
+ logD {
+ "Locking all packages"
+ }
+ unlockedPackages.clear()
+ }
+ alarmsMutex.withLock {
+ if (scheduledAlarms.isEmpty()) return@withLock
+ scheduledAlarms.values.forEach {
+ alarmManager.cancel(it)
+ }
+ scheduledAlarms.clear()
+ }
+ } else {
+ mutex.withLock {
+ if (topPackages.isEmpty()) return@withLock
+ // If device is locked with an app in the foreground,
+ // even if it is removed from [unlockedPackages], it will
+ // still be shown when unlocked, so we need to start home
+ // activity as soon as such a condition is detected on unlock.
+ val shouldGoToHome = topPackages.any {
+ config.isPackageProtected(it) &&
+ !unlockedPackages.contains(it)
+ }
+ if (!shouldGoToHome) return@withLock
+ logD {
+ "Locking foreground package"
+ }
+ context.mainExecutor.execute {
+ atmInternal.startHomeActivity(currentUserId,
+ "Locked package in foreground")
+ }
+ }
+ }
+ }
+ }
+
+ override fun interceptActivity(info: ActivityInterceptorInfo): Intent? {
+ val packageName = info.activityInfo.packageName
+ logD {
+ "interceptActivity, pkg = $packageName"
+ }
+ if (!requireUnlock(packageName, info.userId)) return null
+ val target = IntentSender(
+ atmInternal.getIntentSender(
+ ActivityManager.INTENT_SENDER_ACTIVITY,
+ info.callingPackage,
+ info.callingFeatureId,
+ info.callingPid,
+ info.userId,
+ null /* token */,
+ null /* resultCode */,
+ 0 /* requestCode */,
+ arrayOf(info.intent),
+ arrayOf(info.resolvedType),
+ PendingIntent.FLAG_CANCEL_CURRENT or
+ PendingIntent.FLAG_ONE_SHOT or
+ PendingIntent.FLAG_IMMUTABLE,
+ ActivityOptions.makeBasic().toBundle()
+ )
+ )
+ val intent = Intent(AppLockManager.ACTION_UNLOCK_APP)
+ .setPackage(SETTINGS_PACKAGE)
+ .apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ putExtra(Intent.EXTRA_INTENT, target)
+ putExtra(Intent.EXTRA_USER_ID, info.userId)
+ putExtra(AppLockManager.EXTRA_PACKAGE_LABEL, info.activityInfo.loadLabel(packageManager))
+ putExtra(AppLockManager.EXTRA_ALLOW_BIOMETRICS, isBiometricsAllowed(info.userId))
+ }
+ return intent
+ }
+
+ override fun getHiddenPackages(userId: Int): Set<String> {
+ logD {
+ "getHiddenPackages: userId = $userId"
+ }
+ return runBlocking {
+ val actualUserId = getActualUserId(userId, "getHiddenPackages")
+ val config = mutex.withLock {
+ userConfigMap[actualUserId] ?: run {
+ Slog.e(TAG, "Config unavailable for user $userId")
+ return@runBlocking emptySet()
+ }
+ }
+ val list = config.getAppLockDataList()
+ logD {
+ "data list = $list"
+ }
+ list.filter { it.hideFromLauncher }
+ .map { it.packageName }
+ .toSet()
+ }
+ }
+ }
+
+ class Lifecycle(context: Context) : SystemService(context) {
+ private val service = AppLockManagerService(context)
+
+ override fun onStart() {
+ publishBinderService(Context.APP_LOCK_SERVICE, service)
+ service.onStart()
+ }
+
+ override fun onBootPhase(phase: Int) {
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ service.onBootCompleted()
+ }
+ }
+
+ override fun onUserStarting(user: TargetUser) {
+ service.onUserStarting(user.userIdentifier)
+ }
+
+ override fun onUserStopping(user: TargetUser) {
+ service.onUserStopping(user.userIdentifier)
+ }
+
+ override fun onUserSwitching(from: TargetUser?, to: TargetUser) {
+ service.onUserSwitching(
+ from?.userIdentifier ?: UserHandle.USER_NULL,
+ to.userIdentifier
+ )
+ }
+ }
+}
diff --git a/services/applock/java/com/android/server/app/BiometricUnlocker.kt b/services/applock/java/com/android/server/app/BiometricUnlocker.kt
new file mode 100644
index 0000000..ed595c07
--- /dev/null
+++ b/services/applock/java/com/android/server/app/BiometricUnlocker.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.app
+
+import android.content.Context
+import android.hardware.biometrics.BiometricConstants
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricManager.Authenticators
+import android.hardware.biometrics.BiometricPrompt
+import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResult
+import android.os.CancellationSignal
+import android.util.Slog
+
+import com.android.internal.R
+
+/**
+ * Handles logic of unlocking an app with biometrics or device credentials.
+ *
+ * @hide
+ */
+internal class BiometricUnlocker(private val context: Context) {
+
+ private val biometricManager = context.getSystemService(BiometricManager::class.java)
+
+ // Set operation must be externally synchronized
+ var biometricsAllowed = false
+
+ /**
+ * Determine whether biometrics or device credentials can be used for
+ * unlocking operation.
+ */
+ fun canUnlock(): Boolean =
+ biometricManager?.canAuthenticate(
+ Authenticators.BIOMETRIC_WEAK or
+ Authenticators.DEVICE_CREDENTIAL
+ ) == BiometricManager.BIOMETRIC_SUCCESS
+
+ /**
+ * Unlock an application. Should call this method only if
+ * [canUnlock] returned true.
+ *
+ * @param title the title of the dialog prompt.
+ * @param onSuccess the callback invoked on successfull authentication.
+ * @param onCancel the callback invoked when authentication is cancelled.
+ */
+ fun unlock(
+ packageLabel: String?,
+ onSuccess: () -> Unit,
+ onCancel: () -> Unit,
+ ) {
+ val callback = object : AuthenticationCallback() {
+ override fun onAuthenticationSucceeded(result: AuthenticationResult) {
+ logD {
+ "onAuthenticationSucceeded"
+ }
+ onSuccess()
+ }
+
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ Slog.i(TAG, "onAuthenticationError, errorCode = " +
+ "$errorCode, errString = $errString")
+ if (errorCode == BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
+ onCancel()
+ }
+ }
+ }
+ showCredentialsPrompt(
+ context.getString(R.string.unlock_application, packageLabel),
+ callback
+ )
+ }
+
+ private fun showCredentialsPrompt(
+ title: String,
+ callback: AuthenticationCallback,
+ ) {
+ var authenticators = Authenticators.DEVICE_CREDENTIAL
+ if (biometricsAllowed) {
+ authenticators = authenticators or Authenticators.BIOMETRIC_STRONG
+ }
+ val prompt = BiometricPrompt.Builder(context)
+ .setTitle(title)
+ .setAllowedAuthenticators(authenticators)
+ .setAllowBackgroundAuthentication(true)
+ .build()
+ prompt.authenticateUser(
+ CancellationSignal(),
+ context.mainExecutor,
+ callback,
+ context.userId,
+ )
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5fe4be7..03dfc59 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -238,7 +238,6 @@
"android.security.aaid_aidl-java",
"netd-client",
"overlayable_policy_aidl-java",
- "SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
"securebox",
"apache-commons-math",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 7979936..c94e98d 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -164,6 +164,8 @@
* included in the ACTION_BATTERY_CHANGED intent extras.
*/
private int mLastChargingPolicy;
+ private int mLastBatteryFullCharge;
+ private int mLastBatteryFullChargeDesign;
private int mSequence = 1;
@@ -554,6 +556,9 @@
|| mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
|| mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
|| mInvalidCharger != mLastInvalidCharger
+ || mHealthInfo.batteryFullChargeUah != mLastBatteryFullCharge
+ || mHealthInfo.batteryFullChargeDesignCapacityUah !=
+ mLastBatteryFullChargeDesign
|| mHealthInfo.batteryCycleCount != mLastBatteryCycleCount
|| mHealthInfo.chargingState != mLastCharingState)) {
@@ -739,6 +744,8 @@
mLastInvalidCharger = mInvalidCharger;
mLastBatteryCycleCount = mHealthInfo.batteryCycleCount;
mLastCharingState = mHealthInfo.chargingState;
+ mLastBatteryFullCharge = mHealthInfo.batteryFullChargeUah;
+ mLastBatteryFullChargeDesign = mHealthInfo.batteryFullChargeDesignCapacityUah;
}
}
@@ -772,6 +779,10 @@
intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
intent.putExtra(BatteryManager.EXTRA_CYCLE_COUNT, mHealthInfo.batteryCycleCount);
intent.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, mHealthInfo.chargingState);
+ intent.putExtra(BatteryManager.EXTRA_MAXIMUM_CAPACITY, mHealthInfo.batteryFullChargeUah);
+ intent.putExtra(
+ BatteryManager.EXTRA_DESIGN_CAPACITY,
+ mHealthInfo.batteryFullChargeDesignCapacityUah);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ ", info:" + mHealthInfo.toString());
@@ -811,6 +822,10 @@
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
event.putInt(BatteryManager.EXTRA_CYCLE_COUNT, mHealthInfo.batteryCycleCount);
event.putInt(BatteryManager.EXTRA_CHARGING_STATUS, mHealthInfo.chargingState);
+ event.putInt(BatteryManager.EXTRA_MAXIMUM_CAPACITY, mHealthInfo.batteryFullChargeUah);
+ event.putInt(
+ BatteryManager.EXTRA_DESIGN_CAPACITY,
+ mHealthInfo.batteryFullChargeDesignCapacityUah);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
mBatteryLevelsEventQueue.add(event);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e7fae24..9ec2327 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1623,6 +1623,8 @@
// public API requirement of being in a stable location.
if (vol.disk.isAdoptable()) {
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
+ } else if (vol.disk.isSd()) {
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
vol.mountUserId = mCurrentUserId;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7f5a865..71e4eac 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1744,6 +1744,10 @@
static final HostingRecord sNullHostingRecord =
new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY);
+
+ final SwipeToScreenshotObserver mSwipeToScreenshotObserver;
+ private boolean mIsSwipeToScreenshotEnabled;
+
/**
* Used to notify activity lifecycle events.
*/
@@ -2560,6 +2564,7 @@
mEnableModernQueue = false;
mBroadcastQueues = injector.getBroadcastQueues(this);
mComponentAliasResolver = new ComponentAliasResolver(this);
+ mSwipeToScreenshotObserver = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2669,6 +2674,7 @@
mPendingStartActivityUids = new PendingStartActivityUids();
mTraceErrorLogger = new TraceErrorLogger();
mComponentAliasResolver = new ComponentAliasResolver(this);
+ mSwipeToScreenshotObserver = new SwipeToScreenshotObserver(mHandler, mContext);
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -8808,6 +8814,7 @@
com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers,
delayUserDataLocking);
+ mSwipeToScreenshotObserver.registerObserver();
}
mAppErrors.loadAppsNotReportingCrashesFromConfig(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
@@ -20723,6 +20730,32 @@
}
}
+ private class SwipeToScreenshotObserver extends ContentObserver {
+
+ private final Context mContext;
+
+ public SwipeToScreenshotObserver(Handler handler, Context context) {
+ super(handler);
+ mContext = context;
+ }
+
+ public void registerObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.THREE_FINGER_GESTURE),
+ false, this, UserHandle.USER_ALL);
+ update();
+ }
+
+ private void update() {
+ mIsSwipeToScreenshotEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.THREE_FINGER_GESTURE, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
+ public void onChange(boolean selfChange) {
+ update();
+ }
+ }
+
@Override
public boolean isAppFreezerSupported() {
final long token = Binder.clearCallingIdentity();
@@ -20872,5 +20905,12 @@
app = mPidsSelfLocked.get(debugPid);
}
mOomAdjuster.mCachedAppOptimizer.binderError(debugPid, app, code, flags, err);
+
+ }
+
+ public boolean isSwipeToScreenshotGestureActive() {
+ synchronized (this) {
+ return mIsSwipeToScreenshotEnabled && SystemProperties.getBoolean("sys.android.screenshot", false);
+ }
}
}
diff --git a/services/core/java/com/android/server/app/AppLockManagerServiceInternal.java b/services/core/java/com/android/server/app/AppLockManagerServiceInternal.java
new file mode 100644
index 0000000..106b72f
--- /dev/null
+++ b/services/core/java/com/android/server/app/AppLockManagerServiceInternal.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2023 The LibreMobileOS Foundation
+ *
+ * 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.server.app;
+
+import android.content.Intent;
+
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo;
+
+import java.util.Set;
+
+/**
+ * Internal class for system server to manage app lock.
+ *
+ * @hide
+ */
+public interface AppLockManagerServiceInternal {
+
+ /**
+ * Whether user has to unlock this application in order to
+ * open it.
+ *
+ * @param packageName the package name of the app to check.
+ * @param userId the user id given by the caller.
+ * @return true if user has to unlock, false otherwise.
+ */
+ boolean requireUnlock(String packageName, int userId);
+
+ /**
+ * Report that password for user has changed.
+ *
+ * @param userId the user for which password has changed.
+ */
+ void reportPasswordChanged(int userId);
+
+ /**
+ * Check whether notification content should be hidden for a package.
+ *
+ * @param packageName the package to check for.
+ * @param userId the user id given by the caller.
+ * @return true if notification should be hidden, false otherwise.
+ */
+ boolean shouldRedactNotification(String packageName, int userId);
+
+ /**
+ * Notify that the device is locked for current user.
+ */
+ void notifyDeviceLocked(boolean locked, int userId);
+
+ /**
+ * Whether to intercept the activity launch from a package. Used
+ * to show confirm credentials prompt.
+ *
+ * @param info [ActivityInterceptorInfo] of intercepted activity.
+ * @return [Intent] which will be fired. Return null if activity
+ * shouldn't be intercepted.
+ */
+ Intent interceptActivity(ActivityInterceptorInfo info);
+
+ /**
+ * Get the list of applications hidden from launcher.
+ *
+ * @param userId the user id given of the caller.
+ * @return a hash set of package names.
+ */
+ Set<String> getHiddenPackages(int userId);
+}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 48bf9f4..0687752 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -32,6 +32,8 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider.getWorkaroundSensorProps;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -1044,6 +1046,10 @@
final int[] udfpsProps = getContext().getResources().getIntArray(
com.android.internal.R.array.config_udfps_sensor_props);
+ // Non-empty workaroundLocations indicates that the sensor is SFPS.
+ final List<SensorLocationInternal> workaroundLocations =
+ getWorkaroundSensorProps(getContext());
+
final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps);
// config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor.
@@ -1073,6 +1079,12 @@
resetLockoutRequiresHardwareAuthToken,
List.of(new SensorLocationInternal("" /* display */, udfpsProps[0],
udfpsProps[1], udfpsProps[2])));
+ } else if (!workaroundLocations.isEmpty()) {
+ return new FingerprintSensorPropertiesInternal(sensorId,
+ Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
+ componentInfo, sensorType, true /* halControlsIllumination */,
+ resetLockoutRequiresHardwareAuthToken,
+ workaroundLocations);
} else {
return new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index c04c47e..158179d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -971,7 +971,7 @@
// TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL)
// reads values via an overlay instead of querying the HAL
@NonNull
- private List<SensorLocationInternal> getWorkaroundSensorProps(@NonNull Context context) {
+ public static List<SensorLocationInternal> getWorkaroundSensorProps(@NonNull Context context) {
final List<SensorLocationInternal> sensorLocations = new ArrayList<>();
final TypedArray sfpsProps = context.getResources().obtainTypedArray(
@@ -992,7 +992,7 @@
}
@Nullable
- private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
+ private static SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
if (array == null) {
return null;
}
@@ -1004,7 +1004,7 @@
array.getInt(2, 0),
array.getInt(3, 0));
} catch (Exception e) {
- Slog.w(getTag(), "malformed sensor location", e);
+ Slog.w(TAG, "malformed sensor location", e);
}
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 33e448b..f0b4db9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -144,6 +144,8 @@
private final boolean mIsPowerbuttonFps;
private AidlSession mSession;
+ private boolean mCleanup;
+
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
@@ -375,6 +377,8 @@
Slog.d(TAG, "Initializing AuthenticationStatsCollector");
mAuthenticationStatsCollector = collector;
});
+ mCleanup = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_cleanupUnusedFingerprints);
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
@@ -1007,6 +1011,9 @@
private void scheduleInternalCleanup(int userId,
@Nullable ClientMonitorCallback callback) {
+ if (!mCleanup) {
+ return;
+ }
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 8f937fc..1cfb69b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -224,6 +224,19 @@
@Override
public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) {
- // Unsupported in HIDL.
+ try {
+ switch (event) {
+ case FingerprintManager.UDFPS_UI_OVERLAY_SHOWN:
+ getListener().onUdfpsOverlayShown();
+ break;
+ case FingerprintManager.UDFPS_UI_READY:
+ // Unsupported in HIDL.
+ break;
+ default:
+ Slog.w(TAG, "No matching event for onUdfpsUiEvent");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send onUdfpsUiEvent", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 0e05a79..f54ed05 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -29,6 +29,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -70,6 +71,7 @@
void onLockoutReset(int userId);
}
+ private final Context mContext;
private final LockoutResetCallback mLockoutResetCallback;
private final SparseBooleanArray mTimedLockoutCleared;
private final SparseIntArray mFailedAttempts;
@@ -100,6 +102,7 @@
@NonNull LockoutResetCallback lockoutResetCallback,
@NonNull Function<Integer, PendingIntent> lockoutResetIntent,
@Nullable Handler handler) {
+ mContext = context;
mLockoutResetCallback = lockoutResetCallback;
mTimedLockoutCleared = new SparseBooleanArray();
mFailedAttempts = new SparseIntArray();
@@ -133,6 +136,10 @@
@Override
public void addFailedAttemptForUser(int userId) {
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.FINGERPRINT_LOCKOUT, 0) == 1) {
+ return;
+ }
mFailedAttempts.put(userId, mFailedAttempts.get(userId, 0) + 1);
mTimedLockoutCleared.put(userId, false);
@@ -144,6 +151,10 @@
@Override
@LockoutMode
public int getLockoutModeForUser(int userId) {
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.FINGERPRINT_LOCKOUT, 0) == 1) {
+ return LOCKOUT_NONE;
+ }
final int failedAttempts = mFailedAttempts.get(userId, 0);
if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return LOCKOUT_PERMANENT;
diff --git a/services/core/java/com/android/server/gmscompat/AttestationService.java b/services/core/java/com/android/server/gmscompat/AttestationService.java
new file mode 100644
index 0000000..37b9268
--- /dev/null
+++ b/services/core/java/com/android/server/gmscompat/AttestationService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The LeafOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package com.android.server.gmscompat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public final class AttestationService extends SystemService {
+ private static final String TAG = AttestationService.class.getSimpleName();
+ private static final String API = "https://play.leafos.org";
+
+ private static final String DATA_FILE = "gms_certified_props.json";
+
+ private static final long INITIAL_DELAY = 0;
+ private static final long INTERVAL = 5;
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean SPOOF_GMS =
+ SystemProperties.getBoolean("persist.sys.spoof.gms", true);
+
+ private final Context mContext;
+ private final File mDataFile;
+ private final ScheduledExecutorService mScheduler;
+
+ public AttestationService(Context context) {
+ super(context);
+ mContext = context;
+ mDataFile = new File(Environment.getDataSystemDirectory(), DATA_FILE);
+ mScheduler = Executors.newSingleThreadScheduledExecutor();
+ }
+
+ @Override
+ public void onStart() {}
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SPOOF_GMS
+ && isAppInstalled("com.google.android.gms")
+ && phase == PHASE_BOOT_COMPLETED) {
+ Log.i(TAG, "Scheduling the service");
+ mScheduler.scheduleAtFixedRate(
+ new FetchGmsCertifiedProps(), INITIAL_DELAY, INTERVAL, TimeUnit.MINUTES);
+ }
+ }
+
+ private String readFromFile(File file) {
+ StringBuilder content = new StringBuilder();
+
+ if (file.exists()) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ content.append(line);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading from file", e);
+ }
+ }
+ return content.toString();
+ }
+
+ private void writeToFile(File file, String data) {
+ try (FileWriter writer = new FileWriter(file)) {
+ writer.write(data);
+ // Set -rw-r--r-- (644) permission to make it readable by others.
+ file.setReadable(true, false);
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing to file", e);
+ }
+ }
+
+ private String fetchProps() {
+ try {
+ URL url = new URI(API).toURL();
+ HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+
+ try {
+ urlConnection.setConnectTimeout(10000);
+ urlConnection.setReadTimeout(10000);
+
+ try (BufferedReader reader =
+ new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
+ StringBuilder response = new StringBuilder();
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+
+ return response.toString();
+ }
+ } finally {
+ urlConnection.disconnect();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error making an API request", e);
+ return null;
+ }
+ }
+
+ private boolean isInternetConnected() {
+ ConnectivityManager cm =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Network nw = cm.getActiveNetwork();
+ if (nw == null) return false;
+ NetworkCapabilities actNw = cm.getNetworkCapabilities(nw);
+ return actNw != null
+ && (actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ || actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ || actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
+ || actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH));
+ }
+
+ private boolean isAppInstalled(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.i(TAG, packageName + " is not installed");
+ return false;
+ }
+ }
+
+ private void dlog(String message) {
+ if (DEBUG) Log.d(TAG, message);
+ }
+
+ private class FetchGmsCertifiedProps implements Runnable {
+ @Override
+ public void run() {
+ try {
+ dlog("FetchGmsCertifiedProps started");
+
+ if (!isInternetConnected()) {
+ Log.e(TAG, "Internet unavailable");
+ return;
+ }
+
+ String savedProps = readFromFile(mDataFile);
+ String props = fetchProps();
+
+ if (props != null && !savedProps.equals(props)) {
+ dlog("Found new props");
+ writeToFile(mDataFile, props);
+ dlog("FetchGmsCertifiedProps completed");
+ } else {
+ dlog("No change in props");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in FetchGmsCertifiedProps", e);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/lineage/LineageBaseFeature.java b/services/core/java/com/android/server/lineage/LineageBaseFeature.java
new file mode 100644
index 0000000..c8a915e
--- /dev/null
+++ b/services/core/java/com/android/server/lineage/LineageBaseFeature.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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.server.lineage;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.server.lineage.common.UserContentObserver;
+
+import java.io.PrintWriter;
+
+import android.provider.Settings;
+
+public abstract class LineageBaseFeature {
+ protected final Context mContext;
+ protected final Handler mHandler;
+ protected SettingsObserver mSettingsObserver;
+
+ public LineageBaseFeature(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ public abstract void onStart();
+
+ protected abstract void onSettingsChanged(Uri uri);
+
+ public abstract void dump(PrintWriter pw);
+
+ public void start() {
+ if (mSettingsObserver == null) {
+ mSettingsObserver = new SettingsObserver(mHandler);
+ onStart();
+ }
+ }
+
+ protected final void registerSettings(Uri... settings) {
+ mSettingsObserver.register(settings);
+ }
+
+ protected final boolean getBoolean(String setting, boolean defaultValue) {
+ return Settings.System.getIntForUser(mContext.getContentResolver(),
+ setting, (defaultValue ? 1 : 0), UserHandle.USER_CURRENT) == 1;
+ }
+
+ protected final void putBoolean(String setting, boolean value) {
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ setting, (value ? 1 : 0), UserHandle.USER_CURRENT);
+ }
+
+ protected final int getInt(String setting, int defaultValue) {
+ return Settings.System.getIntForUser(mContext.getContentResolver(),
+ setting, defaultValue, UserHandle.USER_CURRENT);
+ }
+
+ protected final void putInt(String setting, int value) {
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ setting, value, UserHandle.USER_CURRENT);
+ }
+
+ protected final String getString(String setting) {
+ return Settings.System.getStringForUser(mContext.getContentResolver(),
+ setting, UserHandle.USER_CURRENT);
+ }
+
+ protected final void putString(String setting, String value) {
+ Settings.System.putStringForUser(mContext.getContentResolver(),
+ setting, value, UserHandle.USER_CURRENT);
+ }
+
+ public void onDestroy() {
+ mSettingsObserver.unregister();
+ }
+
+ final class SettingsObserver extends UserContentObserver {
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(Uri... uris) {
+ final ContentResolver cr = mContext.getContentResolver();
+ for (Uri uri : uris) {
+ cr.registerContentObserver(uri, false, this, UserHandle.USER_ALL);
+ }
+
+ observe();
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ unobserve();
+ }
+
+ @Override
+ protected void update() {
+ onSettingsChanged(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ onSettingsChanged(uri);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/lineage/common/UserContentObserver.java b/services/core/java/com/android/server/lineage/common/UserContentObserver.java
new file mode 100644
index 0000000..c25ef8b
--- /dev/null
+++ b/services/core/java/com/android/server/lineage/common/UserContentObserver.java
@@ -0,0 +1,98 @@
+/*
+ * 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.server.lineage.common;
+
+import android.app.ActivityManagerNative;
+import android.app.IUserSwitchObserver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Simple extension of ContentObserver that also listens for user switch events to call update
+ */
+public abstract class UserContentObserver extends ContentObserver {
+ private static final String TAG = "UserContentObserver";
+
+ private Runnable mUpdateRunnable;
+
+ private IUserSwitchObserver mUserSwitchObserver = new IUserSwitchObserver.Stub() {
+ @Override
+ public void onBeforeUserSwitching(int newUserId) {
+ }
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ }
+ @Override
+ public void onUserSwitchComplete(int newUserId) throws RemoteException {
+ mHandler.post(mUpdateRunnable);
+ }
+ @Override
+ public void onForegroundProfileSwitch(int newProfileId) {
+ }
+ @Override
+ public void onLockedBootComplete(int newUserId) {
+ }
+ };
+
+ private Handler mHandler;
+
+ /**
+ * Content observer that tracks user switches
+ * to allow clients to re-load settings for current user
+ */
+ public UserContentObserver(Handler handler) {
+ super(handler);
+ mHandler = handler;
+ mUpdateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ };
+ }
+
+ protected void observe() {
+ try {
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to register user switch observer!", e);
+ }
+ }
+
+ protected void unobserve() {
+ try {
+ mHandler.removeCallbacks(mUpdateRunnable);
+ ActivityManagerNative.getDefault().unregisterUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to unregister user switch observer!", e);
+ }
+ }
+
+ /**
+ * Called to notify of registered uri changes and user switches.
+ * Always invoked on the handler passed in at construction
+ */
+ protected abstract void update();
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update();
+ }
+}
diff --git a/services/core/java/com/android/server/lineage/health/ChargingControlController.java b/services/core/java/com/android/server/lineage/health/ChargingControlController.java
new file mode 100644
index 0000000..748d91c
--- /dev/null
+++ b/services/core/java/com/android/server/lineage/health/ChargingControlController.java
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2023 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.server.lineage.health;
+
+import static java.time.format.FormatStyle.SHORT;
+
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+
+import android.provider.Settings;
+
+import vendor.lineage.health.ChargingControlSupportedMode;
+import vendor.lineage.health.IChargingControl;
+
+import static com.android.internal.lineage.health.HealthInterface.MODE_NONE;
+import static com.android.internal.lineage.health.HealthInterface.MODE_AUTO;
+import static com.android.internal.lineage.health.HealthInterface.MODE_MANUAL;
+import static com.android.internal.lineage.health.HealthInterface.MODE_LIMIT;
+
+public class ChargingControlController extends LineageHealthFeature {
+ private final IChargingControl mChargingControl;
+ private final ContentResolver mContentResolver;
+ private ChargingControlNotification mChargingNotification;
+ private LineageHealthBatteryBroadcastReceiver mBattReceiver;
+
+ // Defaults
+ private boolean mDefaultEnabled = false;
+ private int mDefaultMode;
+ private int mDefaultLimit;
+ private int mDefaultStartTime;
+ private int mDefaultTargetTime;
+
+ // User configs
+ private boolean mConfigEnabled;
+ private int mConfigStartTime;
+ private int mConfigTargetTime;
+ private int mConfigMode = MODE_NONE;
+ private int mConfigLimit = 100;
+
+ // Settings uris
+ private final Uri MODE_URI = Settings.System.getUriFor(
+ Settings.System.CHARGING_CONTROL_MODE);
+ private final Uri LIMIT_URI = Settings.System.getUriFor(
+ Settings.System.CHARGING_CONTROL_LIMIT);
+ private final Uri ENABLED_URI = Settings.System.getUriFor(
+ Settings.System.CHARGING_CONTROL_ENABLED);
+ private final Uri START_TIME_URI = Settings.System.getUriFor(
+ Settings.System.CHARGING_CONTROL_START_TIME);
+ private final Uri TARGET_TIME_URI = Settings.System.getUriFor(
+ Settings.System.CHARGING_CONTROL_TARGET_TIME);
+
+ // Internal state
+ private float mBatteryPct;
+ private long mEstimatedFullTime;
+ private long mSavedAlarmTime;
+ private long mSavedTargetTime;
+ private boolean mIsPowerConnected;
+ private boolean mIsControlCancelledOnce;
+ private boolean mIsChargingToggleSupported;
+ private boolean mIsChargingBypassSupported;
+ private boolean mIsChargingDeadlineSupported;
+ private int mChargingStopReason;
+ private int mChargingTimeMargin;
+ private int mChargingLimitMargin;
+
+ private static final DateTimeFormatter mFormatter = DateTimeFormatter.ofLocalizedTime(SHORT);
+ private static final SimpleDateFormat mDateFormatter = new SimpleDateFormat("hh:mm:ss a");
+
+ // Only when the battery level is above this limit will the charging control be activated.
+ private static int CHARGE_CTRL_MIN_LEVEL = 80;
+ private static final String INTENT_PARTS =
+ "com.android.settings.lineage.health.CHARGING_CONTROL_SETTINGS";
+
+ private static class ChargingStopReason {
+ private static int BIT(int shift) {
+ return 1 << shift;
+ }
+
+ /**
+ * No stop charging
+ */
+ public static final int NONE = 0;
+
+ /**
+ * The charging stopped because it reaches limit
+ */
+ public static final int REACH_LIMIT = BIT(0);
+
+ /**
+ * The charging stopped because the battery level is decent, and we are waiting to resume
+ * charging when the time approaches the target time.
+ */
+ public static final int WAITING = BIT(1);
+ }
+
+ public ChargingControlController(Context context, Handler handler) {
+ super(context, handler);
+
+ mContentResolver = mContext.getContentResolver();
+ mChargingControl = IChargingControl.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(
+ IChargingControl.DESCRIPTOR + "/default"));
+
+ if (mChargingControl == null) {
+ Log.i(TAG, "Lineage Health HAL not found");
+ return;
+ }
+
+ mChargingNotification = new ChargingControlNotification(context);
+
+ mChargingTimeMargin = mContext.getResources().getInteger(
+ R.integer.config_chargingControlTimeMargin) * 60 * 1000;
+
+ mDefaultEnabled = mContext.getResources().getBoolean(
+ R.bool.config_chargingControlEnabled);
+ mDefaultMode = mContext.getResources().getInteger(
+ R.integer.config_defaultChargingControlMode);
+ mDefaultStartTime = mContext.getResources().getInteger(
+ R.integer.config_defaultChargingControlStartTime);
+ mDefaultTargetTime = mContext.getResources().getInteger(
+ R.integer.config_defaultChargingControlTargetTime);
+ mDefaultLimit = mContext.getResources().getInteger(
+ R.integer.config_defaultChargingControlLimit);
+
+ mIsChargingToggleSupported = isChargingModeSupported(ChargingControlSupportedMode.TOGGLE);
+ mIsChargingBypassSupported = isChargingModeSupported(ChargingControlSupportedMode.BYPASS);
+ mIsChargingDeadlineSupported = isChargingModeSupported(
+ ChargingControlSupportedMode.DEADLINE);
+
+ if (mIsChargingBypassSupported) {
+ // This is a workaround for devices that support charging bypass, but is not able to
+ // hold the charging current to 0mA, which causes battery to lose power very slowly.
+ // This will become a problem in limit mode because it will stop charge at limit and
+ // immediately resume charging at (limit - 1). So we add a small margin here.
+ mChargingLimitMargin = 1;
+ } else {
+ mChargingLimitMargin = mContext.getResources().getInteger(
+ R.integer.config_chargingControlBatteryRechargeMargin);
+ }
+ }
+
+ @Override
+ public boolean isSupported() {
+ return mChargingControl != null;
+ }
+
+ public boolean isEnabled() {
+ return mConfigEnabled;
+ }
+
+ public boolean setEnabled(boolean enabled) {
+ putBoolean(Settings.System.CHARGING_CONTROL_ENABLED, enabled);
+ return true;
+ }
+
+ public int getMode() {
+ return mConfigMode;
+ }
+
+ public boolean setMode(int mode) {
+ if (mode < MODE_NONE || mode > MODE_LIMIT) {
+ return false;
+ }
+
+ putInt(Settings.System.CHARGING_CONTROL_MODE, mode);
+ return true;
+ }
+
+ public int getStartTime() {
+ return mConfigStartTime;
+ }
+
+ public boolean setStartTime(int time) {
+ if (time < 0 || time > 24 * 60 * 60) {
+ return false;
+ }
+
+ putInt(Settings.System.CHARGING_CONTROL_START_TIME, time);
+ return true;
+ }
+
+ public int getTargetTime() {
+ return mConfigTargetTime;
+ }
+
+ public boolean setTargetTime(int time) {
+ if (time < 0 || time > 24 * 60 * 60) {
+ return false;
+ }
+
+ putInt(Settings.System.CHARGING_CONTROL_TARGET_TIME, time);
+ return true;
+ }
+
+ public int getLimit() {
+ return mConfigLimit;
+ }
+
+ public boolean setLimit(int limit) {
+ if (limit < 0 || limit > 100) {
+ return false;
+ }
+
+ putInt(Settings.System.CHARGING_CONTROL_LIMIT, limit);
+ return true;
+ }
+
+ public boolean reset() {
+ return setEnabled(mDefaultEnabled) && setMode(mDefaultMode) && setLimit(mDefaultLimit)
+ && setStartTime(mDefaultStartTime) && setTargetTime(mDefaultTargetTime);
+ }
+
+ public boolean isChargingModeSupported(int mode) {
+ try {
+ return isSupported() && (mChargingControl.getSupportedMode() & mode) != 0;
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ if (mChargingControl == null) {
+ return;
+ }
+
+ // Register setting observer
+ registerSettings(MODE_URI, LIMIT_URI, ENABLED_URI, START_TIME_URI, TARGET_TIME_URI);
+
+ // For devices that do not support bypass, we can only always listen to battery change
+ // because we can't distinguish between "unplugged" and "plugged in but not charging".
+ if (mIsChargingToggleSupported && !mIsChargingBypassSupported) {
+ mIsPowerConnected = true;
+ onPowerStatus(true);
+ handleSettingChange();
+ return;
+ }
+
+ // Start monitor battery status when power connected
+ IntentFilter connectedFilter = new IntentFilter(Intent.ACTION_POWER_CONNECTED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Power connected, start monitoring battery");
+ mIsPowerConnected = true;
+ onPowerStatus(true);
+ }
+ }, connectedFilter);
+
+ // Stop monitor battery status when power disconnected
+ IntentFilter disconnectedFilter = new IntentFilter(Intent.ACTION_POWER_DISCONNECTED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Power disconnected, stop monitoring battery");
+ mIsPowerConnected = false;
+ onPowerStatus(false);
+ }
+ }, disconnectedFilter);
+
+ // Initial monitor
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = mContext.registerReceiver(null, ifilter);
+ mIsPowerConnected = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
+ if (mIsPowerConnected) {
+ onPowerConnected();
+ }
+
+ // Restore settings
+ handleSettingChange();
+ }
+
+ private void resetInternalState() {
+ mSavedAlarmTime = 0;
+ mSavedTargetTime = 0;
+ mEstimatedFullTime = 0;
+ mChargingStopReason = 0;
+ mIsControlCancelledOnce = false;
+ mChargingNotification.cancel();
+ }
+
+ private void onPowerConnected() {
+ if (mBattReceiver == null) {
+ mBattReceiver = new LineageHealthBatteryBroadcastReceiver();
+ }
+ IntentFilter battFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ mContext.registerReceiver(mBattReceiver, battFilter);
+ }
+
+ private void onPowerDisconnected() {
+ if (mBattReceiver != null) {
+ mContext.unregisterReceiver(mBattReceiver);
+ }
+
+ // On disconnected, reset internal state
+ resetInternalState();
+ }
+
+ private void onPowerStatus(boolean enable) {
+ if (enable) {
+ onPowerConnected();
+ } else {
+ onPowerDisconnected();
+ }
+
+ updateChargeControl();
+ }
+
+ private void updateChargingReasonBitmask(int flag, boolean set) {
+ if (set) {
+ mChargingStopReason |= flag;
+ } else {
+ mChargingStopReason &= ~flag;
+ }
+ }
+
+ private boolean isChargingReasonSet(int flag) {
+ return (mChargingStopReason & flag) != 0;
+ }
+
+ private ChargeTime getChargeTime() {
+ // Get duration to target full time
+ final long currentTime = System.currentTimeMillis();
+ Log.i(TAG, "Current time is " + msToString(currentTime));
+ long targetTime = 0, startTime = currentTime;
+ if (mConfigMode == MODE_AUTO) {
+ // Use alarm as the target time. Maybe someday we can use a model.
+ AlarmManager m = mContext.getSystemService(AlarmManager.class);
+ if (m == null) {
+ Log.e(TAG, "Failed to get alarm service!");
+ mChargingNotification.cancel();
+ return null;
+ }
+ AlarmManager.AlarmClockInfo alarmClockInfo = m.getNextAlarmClock();
+ if (alarmClockInfo == null) {
+ // We didn't find an alarm. Clear waiting flags because we can't predict anyway
+ mChargingNotification.cancel();
+ return null;
+ }
+ targetTime = alarmClockInfo.getTriggerTime();
+ } else if (mConfigMode == MODE_MANUAL) {
+ // User manually controlled time
+ startTime = getTimeMillisFromSecondOfDay(mConfigStartTime);
+ targetTime = getTimeMillisFromSecondOfDay(mConfigTargetTime);
+
+ if (startTime > targetTime) {
+ if (currentTime > targetTime) {
+ targetTime += DateUtils.DAY_IN_MILLIS;
+ } else {
+ startTime -= DateUtils.DAY_IN_MILLIS;
+ }
+ }
+ } else {
+ Log.e(TAG, "invalid charging control mode " + mConfigMode);
+ return null;
+ }
+
+ Log.i(TAG, "Target time is " + msToString(targetTime));
+
+ return new ChargeTime(startTime, targetTime);
+ }
+
+ private void updateChargeControl() {
+ if (mIsChargingToggleSupported) {
+ updateChargeToggle();
+ } else if (mIsChargingDeadlineSupported) {
+ updateChargeDeadline();
+ }
+ }
+
+ private boolean shouldSetLimitFlag() {
+ if (mConfigMode != MODE_LIMIT) {
+ return false;
+ }
+
+ if (isChargingReasonSet(ChargingStopReason.REACH_LIMIT)) {
+ return mBatteryPct >= mConfigLimit - mChargingLimitMargin;
+ }
+
+ if (mBatteryPct >= mConfigLimit) {
+ mChargingNotification.post(null, true);
+ return true;
+ } else {
+ mChargingNotification.post(null, false);
+ return false;
+ }
+ }
+
+ private boolean shouldSetWaitFlag() {
+ if (mConfigMode != MODE_AUTO && mConfigMode != MODE_MANUAL) {
+ return false;
+ }
+
+ // Now it is time to see whether charging should be stopped. We make decisions in the
+ // following manner:
+ //
+ // 1. If STOP_REASON_WAITING is set, compare the remaining time with the saved estimated
+ // full time. Resume charging the remain time <= saved estimated time
+ // 2. If the system estimated remaining time already exceeds the target full time, continue
+ // 3. Otherwise, stop charging, save the estimated time, set stop reason to
+ // STOP_REASON_WAITING.
+
+ final ChargeTime t = getChargeTime();
+
+ if (t == null) {
+ mChargingNotification.cancel();
+ return false;
+ }
+
+ final long targetTime = t.getTargetTime();
+ final long startTime = t.getStartTime();
+ final long currentTime = System.currentTimeMillis();
+
+ Log.i(TAG, "Got target time " + msToString(targetTime) + ", start time " +
+ msToString(startTime) + ", current time " + msToString(currentTime));
+
+ if (mConfigMode == MODE_AUTO) {
+ if (mSavedAlarmTime != targetTime) {
+ mChargingNotification.cancel();
+
+ if (mSavedAlarmTime != 0 && mSavedAlarmTime < currentTime) {
+ Log.i(TAG, "Not fully charged when alarm goes off, continue charging.");
+ mIsControlCancelledOnce = true;
+ return false;
+ }
+
+ Log.i(TAG, "User changed alarm, reconstruct notification");
+ mSavedAlarmTime = targetTime;
+ }
+
+ // Don't activate if we are more than 9 hrs away from the target alarm
+ if (targetTime - currentTime >= 9 * 60 * 60 * 1000) {
+ mChargingNotification.cancel();
+ return false;
+ }
+ } else if (mConfigMode == MODE_MANUAL) {
+ if (startTime > currentTime) {
+ // Not yet entering user configured time frame
+ mChargingNotification.cancel();
+ return false;
+ }
+ }
+
+ if (mBatteryPct == 100) {
+ mChargingNotification.post(targetTime, true);
+ return true;
+ }
+
+ // Now we have the target time and current time, we can post a notification stating that
+ // the system will be charged by targetTime.
+ mChargingNotification.post(targetTime, false);
+
+ // If current battery level is less than the fast charge limit, don't set this flag
+ if (mBatteryPct < CHARGE_CTRL_MIN_LEVEL) {
+ return false;
+ }
+
+ long deltaTime = targetTime - currentTime;
+ Log.i(TAG, "Current time to target: " + msToString(deltaTime));
+
+ if (isChargingReasonSet(ChargingStopReason.WAITING)) {
+ Log.i(TAG, "Current saved estimation to full: " + msToString(mEstimatedFullTime));
+ if (deltaTime <= mEstimatedFullTime) {
+ Log.i(TAG, "Unset waiting flag");
+ return false;
+ }
+ return true;
+ }
+
+ final BatteryUsageStats batteryUsageStats = mContext.getSystemService(
+ BatteryStatsManager.class).getBatteryUsageStats();
+ if (batteryUsageStats == null) {
+ Log.e(TAG, "Failed to get battery usage stats");
+ return false;
+ }
+ long remaining = batteryUsageStats.getChargeTimeRemainingMs();
+ if (remaining == -1) {
+ Log.i(TAG, "not enough data for prediction for now, waiting for more data");
+ return false;
+ }
+
+ // Add margin here
+ remaining += mChargingTimeMargin;
+ Log.i(TAG, "Current estimated time to full: " + msToString(remaining));
+ if (deltaTime > remaining) {
+ Log.i(TAG, "Stop charging and wait, saving remaining time");
+ mEstimatedFullTime = remaining;
+ return true;
+ }
+
+ return false;
+ }
+
+ private void updateChargingStopReason() {
+ if (mIsControlCancelledOnce) {
+ mChargingStopReason = ChargingStopReason.NONE;
+ return;
+ }
+
+ if (!mConfigEnabled) {
+ mChargingStopReason = ChargingStopReason.NONE;
+ return;
+ }
+
+ if (!mIsPowerConnected) {
+ mChargingStopReason = ChargingStopReason.NONE;
+ return;
+ }
+
+ updateChargingReasonBitmask(ChargingStopReason.REACH_LIMIT, shouldSetLimitFlag());
+ updateChargingReasonBitmask(ChargingStopReason.WAITING, shouldSetWaitFlag());
+ }
+
+ private void updateChargeToggle() {
+ updateChargingStopReason();
+
+ Log.i(TAG, "Current mChargingStopReason: " + mChargingStopReason);
+ boolean isChargingEnabled = false;
+ try {
+ isChargingEnabled = mChargingControl.getChargingEnabled();
+ } catch (IllegalStateException | RemoteException | UnsupportedOperationException e) {
+ Log.e(TAG, "Failed to get charging enabled status!");
+ }
+ if (isChargingEnabled != (mChargingStopReason == 0)) {
+ try {
+ mChargingControl.setChargingEnabled(!isChargingEnabled);
+ } catch (IllegalStateException | RemoteException | UnsupportedOperationException e) {
+ Log.e(TAG, "Failed to set charging status");
+ }
+ }
+ }
+
+ private void updateChargeDeadline() {
+ if (!mIsPowerConnected) {
+ return;
+ }
+
+ long deadline = 0;
+ final long targetTime;
+ final ChargeTime t = getChargeTime();
+
+ if (!mConfigEnabled || t == null || mIsControlCancelledOnce) {
+ deadline = -1;
+ targetTime = 0;
+ Log.i(TAG, "Canceling charge deadline");
+ } else {
+ if (t.getTargetTime() == mSavedTargetTime) {
+ return;
+ }
+ targetTime = t.getTargetTime();
+ final long currentTime = System.currentTimeMillis();
+ deadline = (targetTime - currentTime) / 1000;
+ Log.i(TAG, "Setting charge deadline: Current time: " + msToString(currentTime));
+ Log.i(TAG, "Setting charge deadline: Target time: " + msToString(targetTime));
+ Log.i(TAG, "Setting charge deadline: Deadline (seconds): " + deadline);
+ }
+
+ try {
+ mChargingControl.setChargingDeadline(deadline);
+ mSavedTargetTime = targetTime;
+ } catch (IllegalStateException | RemoteException | UnsupportedOperationException e) {
+ Log.e(TAG, "Failed to set charge deadline", e);
+ }
+ }
+
+ private String msToString(long ms) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(ms);
+ return mDateFormatter.format(calendar.getTime());
+ }
+
+ /**
+ * Convert the seconds of the day to UTC milliseconds from epoch.
+ *
+ * @param time seconds of the day
+ * @return UTC milliseconds from epoch
+ */
+ private long getTimeMillisFromSecondOfDay(int time) {
+ ZoneId utcZone = ZoneOffset.UTC;
+ LocalDate currentDate = LocalDate.now();
+ LocalTime timeOfDay = LocalTime.ofSecondOfDay(time);
+
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(currentDate, timeOfDay,
+ ZoneId.systemDefault())
+ .withZoneSameInstant(utcZone);
+ return zonedDateTime.toInstant().toEpochMilli();
+ }
+
+ private LocalTime getLocalTimeFromEpochMilli(long time) {
+ return Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalTime();
+ }
+
+ private void handleSettingChange() {
+ mConfigEnabled = Settings.System.getInt(mContentResolver,
+ Settings.System.CHARGING_CONTROL_ENABLED, 0)
+ != 0;
+ mConfigLimit = Settings.System.getInt(mContentResolver,
+ Settings.System.CHARGING_CONTROL_LIMIT,
+ mDefaultLimit);
+ mConfigMode = Settings.System.getInt(mContentResolver,
+ Settings.System.CHARGING_CONTROL_MODE,
+ mDefaultMode);
+ mConfigStartTime = Settings.System.getInt(mContentResolver,
+ Settings.System.CHARGING_CONTROL_START_TIME,
+ mDefaultStartTime);
+ mConfigTargetTime = Settings.System.getInt(mContentResolver,
+ Settings.System.CHARGING_CONTROL_TARGET_TIME,
+ mDefaultTargetTime);
+
+ // Reset internal states
+ resetInternalState();
+
+ // Update based on those values
+ updateChargeControl();
+ }
+
+
+ @Override
+ protected void onSettingsChanged(Uri uri) {
+ handleSettingChange();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("ChargingControlController Configuration:");
+ pw.println(" mConfigEnabled: " + mConfigEnabled);
+ pw.println(" mConfigMode: " + mConfigMode);
+ pw.println(" mConfigLimit: " + mConfigLimit);
+ pw.println(" mConfigStartTime: " + mConfigStartTime);
+ pw.println(" mConfigTargetTime: " + mConfigTargetTime);
+ pw.println(" mChargingTimeMargin: " + mChargingTimeMargin);
+ pw.println();
+ pw.println("ChargingControlController State:");
+ pw.println(" mBatteryPct: " + mBatteryPct);
+ pw.println(" mIsPowerConnected: " + mIsPowerConnected);
+ pw.println(" mChargingStopReason: " + mChargingStopReason);
+ pw.println(" mIsNotificationPosted: " + mChargingNotification.isPosted());
+ pw.println(" mIsDoneNotification: " + mChargingNotification.isDoneNotification());
+ pw.println(" mIsControlCancelledOnce: " + mIsControlCancelledOnce);
+ pw.println(" mSavedAlarmTime: " + msToString(mSavedAlarmTime));
+ if (mIsChargingDeadlineSupported) {
+ pw.println(" mSavedTargetTime (Deadline): " + msToString(mSavedTargetTime));
+ }
+ }
+
+ /* Battery Broadcast Receiver */
+ private class LineageHealthBatteryBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mIsPowerConnected) {
+ return;
+ }
+
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ if (level == -1 || scale == -1) {
+ return;
+ }
+
+ mBatteryPct = level * 100 / (float) scale;
+ updateChargeControl();
+ }
+ }
+
+ /* Notification class */
+ class ChargingControlNotification {
+ private final NotificationManager mNotificationManager;
+ private final Context mContext;
+
+ private static final int CHARGING_CONTROL_NOTIFICATION_ID = 1000;
+ private static final String ACTION_CHARGING_CONTROL_CANCEL_ONCE =
+ "lineageos.platform.intent.action.CHARGING_CONTROL_CANCEL_ONCE";
+ private static final String CHARGING_CONTROL_CHANNEL_ID = "LineageHealthChargingControl";
+
+ private boolean mIsDoneNotification = false;
+ private boolean mIsNotificationPosted = false;
+
+ ChargingControlNotification(Context context) {
+ mContext = context;
+
+ // Get notification manager
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+
+ // Register notification monitor
+ IntentFilter notificationFilter = new IntentFilter(ACTION_CHARGING_CONTROL_CANCEL_ONCE);
+ mContext.registerReceiver(new LineageHealthNotificationBroadcastReceiver(),
+ notificationFilter);
+ }
+
+ public void post(Long targetTime, boolean done) {
+ if (mIsNotificationPosted && mIsDoneNotification == done) {
+ return;
+ }
+
+ if (mIsNotificationPosted) {
+ cancel();
+ }
+
+ if (done) {
+ postChargingDoneNotification(targetTime);
+ } else {
+ postChargingControlNotification(targetTime);
+ }
+
+ mIsNotificationPosted = true;
+ mIsDoneNotification = done;
+ }
+
+ public void cancel() {
+ cancelChargingControlNotification();
+ mIsNotificationPosted = false;
+ }
+
+ public boolean isPosted() {
+ return mIsNotificationPosted;
+ }
+
+ public boolean isDoneNotification() {
+ return mIsDoneNotification;
+ }
+
+ private void handleNotificationIntent(Intent intent) {
+ if (intent.getAction().equals(ACTION_CHARGING_CONTROL_CANCEL_ONCE)) {
+ mIsControlCancelledOnce = true;
+
+ if (!mIsChargingBypassSupported) {
+ IntentFilter disconnectFilter = new IntentFilter(
+ Intent.ACTION_POWER_DISCONNECTED);
+ // Register a one-time receiver that resets internal state on power
+ // disconnection
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Power disconnected, reset internal states");
+ resetInternalState();
+ mContext.unregisterReceiver(this);
+ }
+ }, disconnectFilter);
+ }
+ updateChargeControl();
+ cancelChargingControlNotification();
+ }
+ }
+
+ private void postChargingControlNotification(Long targetTime) {
+ String title = mContext.getString(R.string.charging_control_notification_title);
+ String message;
+ if (targetTime != null) {
+ message = String.format(
+ mContext.getString(R.string.charging_control_notification_content_target),
+ getLocalTimeFromEpochMilli(targetTime).format(mFormatter));
+ } else {
+ message = String.format(
+ mContext.getString(R.string.charging_control_notification_content_limit),
+ mConfigLimit);
+ }
+
+ Intent mainIntent = new Intent(INTENT_PARTS);
+ mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent mainPendingIntent = PendingIntent.getActivity(mContext, 0, mainIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ Intent cancelOnceIntent = new Intent(ACTION_CHARGING_CONTROL_CANCEL_ONCE);
+ PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ cancelOnceIntent, PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Builder notification =
+ new Notification.Builder(mContext, CHARGING_CONTROL_CHANNEL_ID)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(mainPendingIntent)
+ .setSmallIcon(R.drawable.ic_charging_control)
+ .setOngoing(true)
+ .addAction(R.drawable.ic_charging_control,
+ mContext.getString(
+ R.string.charging_control_notification_cancel_once),
+ cancelPendingIntent);
+
+ createNotificationChannelIfNeeded();
+ mNotificationManager.notify(CHARGING_CONTROL_NOTIFICATION_ID, notification.build());
+ }
+
+ private void postChargingDoneNotification(Long targetTime) {
+ cancelChargingControlNotification();
+
+ String title = mContext.getString(R.string.charging_control_notification_title);
+ String message;
+ if (targetTime != null) {
+ message = mContext.getString(
+ R.string.charging_control_notification_content_target_reached);
+ } else {
+ message = String.format(
+ mContext.getString(
+ R.string.charging_control_notification_content_limit_reached),
+ mConfigLimit);
+ }
+
+ Intent mainIntent = new Intent(INTENT_PARTS);
+ mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent mainPendingIntent = PendingIntent.getActivity(mContext, 0, mainIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Builder notification = new Notification.Builder(mContext,
+ CHARGING_CONTROL_CHANNEL_ID)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(mainPendingIntent)
+ .setSmallIcon(R.drawable.ic_charging_control)
+ .setOngoing(false);
+
+ if (targetTime == null) {
+ Intent cancelOnceIntent = new Intent(ACTION_CHARGING_CONTROL_CANCEL_ONCE);
+ PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ cancelOnceIntent, PendingIntent.FLAG_IMMUTABLE);
+ notification.addAction(R.drawable.ic_charging_control,
+ mContext.getString(R.string.charging_control_notification_cancel_once),
+ cancelPendingIntent);
+ }
+
+ createNotificationChannelIfNeeded();
+ mNotificationManager.notify(CHARGING_CONTROL_NOTIFICATION_ID, notification.build());
+ }
+
+ private void createNotificationChannelIfNeeded() {
+ String id = CHARGING_CONTROL_CHANNEL_ID;
+ NotificationChannel channel = mNotificationManager.getNotificationChannel(id);
+ if (channel != null) {
+ return;
+ }
+
+ String name = mContext.getString(R.string.charging_control_notification_channel);
+ int importance = NotificationManager.IMPORTANCE_LOW;
+ NotificationChannel batteryHealthChannel = new NotificationChannel(id, name,
+ importance);
+ batteryHealthChannel.setBlockable(true);
+ mNotificationManager.createNotificationChannel(batteryHealthChannel);
+ }
+
+ private void cancelChargingControlNotification() {
+ mNotificationManager.cancel(CHARGING_CONTROL_NOTIFICATION_ID);
+ }
+
+ /* Notification Broadcast Receiver */
+ private class LineageHealthNotificationBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleNotificationIntent(intent);
+ }
+ }
+ }
+
+ /* A representation of start and target time */
+ static final class ChargeTime {
+ private final long mStartTime;
+ private final long mTargetTime;
+
+ ChargeTime(long startTime, long targetTime) {
+ mStartTime = startTime;
+ mTargetTime = targetTime;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getTargetTime() {
+ return mTargetTime;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/lineage/health/HealthInterfaceService.java b/services/core/java/com/android/server/lineage/health/HealthInterfaceService.java
new file mode 100644
index 0000000..86745d1
--- /dev/null
+++ b/services/core/java/com/android/server/lineage/health/HealthInterfaceService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.server.lineage.health;
+
+import android.Manifest;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.server.ServiceThread;
+
+import com.android.server.SystemService;
+
+import com.android.internal.lineage.app.LineageContextConstants;
+import com.android.internal.lineage.health.IHealthInterface;
+import vendor.lineage.health.ChargingControlSupportedMode;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HealthInterfaceService extends SystemService {
+
+ private static final String TAG = "LineageHealth";
+ private final Context mContext;
+ private final Handler mHandler;
+ private final ServiceThread mHandlerThread;
+
+ private final List<LineageHealthFeature> mFeatures = new ArrayList<LineageHealthFeature>();
+
+ // Health features
+ private ChargingControlController mCCC;
+
+ public HealthInterfaceService(Context context) {
+ super(context);
+ mContext = context;
+
+ mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DEFAULT, false);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ @Override
+ public void onStart() {
+ mCCC = new ChargingControlController(mContext, mHandler);
+ if (mCCC.isSupported()) {
+ mFeatures.add(mCCC);
+ }
+
+ if (!mFeatures.isEmpty()) {
+ publishBinderService(LineageContextConstants.LINEAGE_HEALTH_INTERFACE, mService);
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase != PHASE_BOOT_COMPLETED) {
+ return;
+ }
+
+ // start and update all features
+ for (LineageHealthFeature feature : mFeatures) {
+ feature.start();
+ }
+ }
+
+ /* Service */
+ private final IBinder mService = new IHealthInterface.Stub() {
+ @Override
+ public boolean isChargingControlSupported() {
+ return mCCC.isSupported();
+ }
+
+ @Override
+ public boolean getChargingControlEnabled() {
+ return mCCC.isEnabled();
+ }
+
+ @Override
+ public boolean setChargingControlEnabled(boolean enabled) {
+ return mCCC.setEnabled(enabled);
+ }
+
+ @Override
+ public int getChargingControlMode() {
+ return mCCC.getMode();
+ }
+
+ @Override
+ public boolean setChargingControlMode(int mode) {
+ return mCCC.setMode(mode);
+ }
+
+ @Override
+ public int getChargingControlStartTime() {
+ return mCCC.getStartTime();
+ }
+
+ @Override
+ public boolean setChargingControlStartTime(int startTime) {
+ return mCCC.setStartTime(startTime);
+ }
+
+ @Override
+ public int getChargingControlTargetTime() {
+ return mCCC.getTargetTime();
+ }
+
+ @Override
+ public boolean setChargingControlTargetTime(int targetTime) {
+ return mCCC.setTargetTime(targetTime);
+ }
+
+ @Override
+ public int getChargingControlLimit() {
+ return mCCC.getLimit();
+ }
+
+ @Override
+ public boolean setChargingControlLimit(int limit) {
+ return mCCC.setLimit(limit);
+ }
+
+ @Override
+ public boolean resetChargingControl() {
+ return mCCC.reset();
+ }
+
+ @Override
+ public boolean allowFineGrainedSettings() {
+ // We allow fine-grained settings if allow toggle and bypass
+ return mCCC.isChargingModeSupported(ChargingControlSupportedMode.TOGGLE);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, TAG);
+
+ pw.println();
+ pw.println("LineageHealth Service State:");
+
+ for (LineageHealthFeature feature : mFeatures) {
+ feature.dump(pw);
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/lineage/health/LineageHealthFeature.java b/services/core/java/com/android/server/lineage/health/LineageHealthFeature.java
new file mode 100644
index 0000000..efc5df1
--- /dev/null
+++ b/services/core/java/com/android/server/lineage/health/LineageHealthFeature.java
@@ -0,0 +1,33 @@
+
+/*
+ * Copyright (C) 2023 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.server.lineage.health;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.server.lineage.LineageBaseFeature;
+
+public abstract class LineageHealthFeature extends LineageBaseFeature {
+ protected static final String TAG = "LineageHealth";
+
+ public LineageHealthFeature(Context context, Handler handler) {
+ super(context, handler);
+ }
+
+ public abstract boolean isSupported();
+}
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 39df5be..a299967 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -401,6 +401,7 @@
if (mGnssVisibilityControl != null) {
mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
}
+ toggleXtraDaemon();
}
public GnssLocationProvider(Context context, GnssNative gnssNative,
@@ -513,6 +514,16 @@
}
}, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ASSISTED_GPS_ENABLED),
+ false,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ toggleXtraDaemon();
+ }
+ }, UserHandle.USER_ALL);
+
mHandler.post(this::handleInitialize);
mHandler.post(mGnssSatelliteBlocklistHelper::updateSatelliteBlocklist);
}
@@ -668,7 +679,7 @@
private void onNetworkAvailable() {
mNetworkTimeHelper.onNetworkAvailable();
// Download only if supported, (prevents an unnecessary on-boot download)
- if (mSupportsPsds) {
+ if (mSupportsPsds && isAssistedGpsEnabled()) {
synchronized (mLock) {
for (int psdsType : mPendingDownloadPsdsTypes) {
postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
@@ -763,6 +774,11 @@
Log.d(TAG, "handleDownloadPsdsData() called when PSDS not supported");
return;
}
+ if (!isAssistedGpsEnabled()) {
+ // PSDS download disabled by system setting, don't try
+ Log.d(TAG, "handleDownloadPsdsData() called when PSDS disabled by system setting");
+ return;
+ }
if (!mNetworkConnectivityHandler.isDataNetworkConnected()) {
// try again when network is up
synchronized (mLock) {
@@ -1176,7 +1192,7 @@
} else if ("force_time_injection".equals(command)) {
demandUtcTimeInjection();
} else if ("force_psds_injection".equals(command)) {
- if (mSupportsPsds) {
+ if (mSupportsPsds && isAssistedGpsEnabled()) {
postWithWakeLockHeld(() -> handleDownloadPsdsData(
GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
}
@@ -1220,12 +1236,7 @@
mTimeToFirstFix = 0;
mLastFixTime = 0;
setStarted(true);
- mPositionMode = GNSS_POSITION_MODE_STANDALONE;
-
- boolean agpsEnabled =
- (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
- mPositionMode = getSuplMode(agpsEnabled);
+ mPositionMode = getSuplMode(isAssistedGpsEnabled());
if (DEBUG) {
String mode;
@@ -1669,6 +1680,7 @@
"PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured());
pw.println("native internal state: ");
pw.println(" " + mGnssNative.getInternalState());
+ pw.println("isAssistedGpsEnabled=" + isAssistedGpsEnabled());
}
}
@@ -1796,9 +1808,17 @@
mContext.getSystemService(Context.TELEPHONY_SERVICE);
int type = AGPS_SETID_TYPE_NONE;
String setId = null;
+ final Boolean isEmergency = mNIHandler.getInEmergency();
+
+ // Unless we are in an emergency, do not provide sensitive subscriber information
+ // to SUPL servers.
+ if (!isEmergency) {
+ mGnssNative.setAgpsSetId(type, "");
+ return;
+ }
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- if (mGnssConfiguration.isActiveSimEmergencySuplEnabled() && mNIHandler.getInEmergency()
+ if (mGnssConfiguration.isActiveSimEmergencySuplEnabled() && isEmergency
&& mNetworkConnectivityHandler.getActiveSubId() >= 0) {
subId = mNetworkConnectivityHandler.getActiveSubId();
}
@@ -1855,4 +1875,19 @@
otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
isCachedLocation);
}
+
+ private boolean isAssistedGpsEnabled() {
+ final Boolean isEmergency = mNIHandler.getInEmergency();
+ if (isEmergency) {
+ Log.i(TAG, "Forcing Assisted GPS due to emergency");
+ }
+ return (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ASSISTED_GPS_ENABLED, 0) != 0) || isEmergency;
+ }
+
+ private void toggleXtraDaemon() {
+ Log.i(TAG, "Toggling xtra-daemon via property");
+ SystemProperties.set("persist.sys.xtra-daemon.enabled",
+ Boolean.toString(isAssistedGpsEnabled()));
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 29ea071..71a4da5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -147,6 +147,7 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.RebootEscrowListener;
import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.app.AppLockManagerServiceInternal;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
@@ -1685,6 +1686,11 @@
&& !getSeparateProfileChallengeEnabledInternal(userId);
}
+
+ public byte getLockPatternSize(int userId) {
+ return mStorage.getLockPatternSize(userId);
+ }
+
/**
* Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
* unlock operation.
@@ -2479,6 +2485,7 @@
PasswordMetrics.computeForCredential(newCredential),
userId);
LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
+ LocalServices.getService(AppLockManagerServiceInternal.class).reportPasswordChanged(userId);
});
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 17f2fcc..d0fd9ef 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -229,8 +229,9 @@
}
}
if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+ final byte patternSize = mLockPatternUtils.getLockPatternSize(mCurrentUserId);
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
- mOld.getBytes()));
+ mOld.getBytes(), patternSize), patternSize);
}
// User supplied some old credential but the device has neither password nor pattern,
// so just return a password credential (and let it be rejected during LSS verification)
@@ -239,8 +240,9 @@
}
private boolean runSetPattern() {
+ final byte patternSize = mLockPatternUtils.getLockPatternSize(mCurrentUserId);
final LockscreenCredential pattern = LockscreenCredential.createPattern(
- LockPatternUtils.byteArrayToPattern(mNew.getBytes()));
+ LockPatternUtils.byteArrayToPattern(mNew.getBytes(), patternSize), patternSize);
if (!isNewCredentialSufficient(pattern)) {
return false;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 158d444..8fa7b8d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -368,6 +368,14 @@
}
}
+ public byte getLockPatternSize(int userId) {
+ long size = Long.valueOf(readKeyValue(Settings.Secure.LOCK_PATTERN_SIZE, "-1", userId));
+ if (size > 0 && size < 128) {
+ return (byte) size;
+ }
+ return LockPatternUtils.PATTERN_SIZE_DEFAULT;
+ }
+
@VisibleForTesting
File getChildProfileLockFile(int userId) {
return getLockCredentialFileForUser(userId, CHILD_PROFILE_LOCK_FILE);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index e5807e8..d67ba83 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -1126,9 +1126,11 @@
CharSequence pinStr = new String(password);
return LockscreenCredential.createPin(pinStr);
case KeyguardManager.PATTERN:
+ byte patternSize = new LockPatternUtils(mContext).getLockPatternSize(
+ mContext.getUserId());
List<LockPatternView.Cell> pattern =
- LockPatternUtils.byteArrayToPattern(password);
- return LockscreenCredential.createPattern(pattern);
+ LockPatternUtils.byteArrayToPattern(password, patternSize);
+ return LockscreenCredential.createPattern(pattern, patternSize);
default:
throw new IllegalStateException("Lockscreen is not set");
}
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index b8900d7..373c2a9 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -89,7 +89,8 @@
NotificationChannel recordChannel = record.getChannel();
if (!userEnabledBubbles
|| appPreference == BUBBLE_PREFERENCE_NONE
- || !notifCanPresentAsBubble) {
+ || !notifCanPresentAsBubble
+ || record.getSbn().getIsContentSecure()) {
record.setAllowBubble(false);
if (!notifCanPresentAsBubble) {
// clear out bubble metadata since it can't be used
@@ -143,10 +144,9 @@
*/
@VisibleForTesting
boolean canPresentAsBubble(NotificationRecord r) {
- if (!mSupportsBubble) {
+ if (!mSupportsBubble || r.isBubbleUpSuppressedByAppLock()) {
return false;
}
-
Notification notification = r.getNotification();
Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
String pkg = r.getSbn().getPackageName();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 4b8de4e..2f99e62 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -73,4 +73,7 @@
* Otherwise an {@link IllegalStateException} will be thrown.
*/
void setDeviceEffectsApplier(DeviceEffectsApplier applier);
+
+ void updateSecureNotifications(String pkg, boolean isContentSecure,
+ boolean isBubbleUpSuppressed, int userId);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3a7ac0b..1112792 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -53,6 +53,7 @@
import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
import static android.app.NotificationManager.EXTRA_NOTIFICATION_POLICY;
@@ -349,6 +350,7 @@
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.app.AppLockManagerServiceInternal;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -663,6 +665,10 @@
protected boolean mInCallStateOffHook = false;
boolean mNotificationPulseEnabled;
+ private AppLockManagerServiceInternal mAppLockManagerService = null;
+
+ private boolean mSoundVibScreenOn;
+
private Uri mInCallNotificationUri;
private AudioAttributes mInCallNotificationAudioAttributes;
private float mInCallNotificationVolume;
@@ -2168,6 +2174,8 @@
= Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
private final Uri SHOW_NOTIFICATION_SNOOZE
= Settings.Secure.getUriFor(Settings.Secure.SHOW_NOTIFICATION_SNOOZE);
+ private final Uri NOTIFICATION_SOUND_VIB_SCREEN_ON
+ = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND_VIB_SCREEN_ON);
SettingsObserver(Handler handler) {
super(handler);
@@ -2194,10 +2202,10 @@
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS,
false, this, UserHandle.USER_ALL);
-
resolver.registerContentObserver(SHOW_NOTIFICATION_SNOOZE,
false, this, UserHandle.USER_ALL);
-
+ resolver.registerContentObserver(NOTIFICATION_SOUND_VIB_SCREEN_ON,
+ false, this, UserHandle.USER_ALL);
update(null);
}
@@ -2250,6 +2258,11 @@
unsnoozeAll();
}
}
+ if (uri == null || NOTIFICATION_SOUND_VIB_SCREEN_ON.equals(uri)) {
+ mSoundVibScreenOn = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_SOUND_VIB_SCREEN_ON, 1,
+ UserHandle.USER_CURRENT) == 1;
+ }
}
public void update(Uri uri, int userId) {
@@ -3091,6 +3104,7 @@
}
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
+ mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class);
} else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) {
mPreferencesHelper.updateFixedImportance(mUm.getUsers());
mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers());
@@ -4034,8 +4048,8 @@
public int getBubblePreferenceForPackage(String pkg, int uid) {
enforceSystemOrSystemUIOrSamePackage(pkg,
"Caller not system or systemui or same package");
-
- if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) {
+ final int userId = UserHandle.getUserId(uid);
+ if (UserHandle.getCallingUserId() != userId) {
getContext().enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
"getBubblePreferenceForPackage for uid " + uid);
@@ -4870,7 +4884,8 @@
sbn.getOpPkg(),
sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
notification,
- sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ sbn.getUser(), sbn.getOverrideGroupKey(),
+ sbn.getPostTime(), sbn.getIsContentSecure());
}
}
return null;
@@ -6824,6 +6839,8 @@
0, appIntent, PendingIntent.FLAG_IMMUTABLE, null,
pkg, appInfo.uid);
}
+ final boolean isContentSecure = mAppLockManagerService != null &&
+ mAppLockManagerService.shouldRedactNotification(pkg, userId);
final StatusBarNotification summarySbn =
new StatusBarNotification(adjustedSbn.getPackageName(),
adjustedSbn.getOpPkg(),
@@ -6831,13 +6848,16 @@
GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
adjustedSbn.getInitialPid(), summaryNotification,
adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
- System.currentTimeMillis());
+ System.currentTimeMillis(), isContentSecure);
summaryRecord = new NotificationRecord(getContext(), summarySbn,
notificationRecord.getChannel());
summaryRecord.setImportanceFixed(isPermissionFixed);
summaryRecord.setIsAppImportanceLocked(
notificationRecord.getIsAppImportanceLocked());
summaries.put(pkg, summarySbn.getKey());
+ summaryRecord.setBubbleUpSuppressedByAppLock(
+ mAppLockManagerService != null &&
+ mAppLockManagerService.requireUnlock(pkg, userId));
}
if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid,
summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
@@ -7347,6 +7367,33 @@
}
// This can also throw IllegalStateException if called too late.
mZenModeHelper.setDeviceEffectsApplier(applier);
+
+ }
+
+ public void updateSecureNotifications(String pkg, boolean isContentSecure,
+ boolean isBubbleUpSuppressed, int userId) {
+ mHandler.post(() -> updateSecureNotificationsInternal(pkg, isContentSecure,
+ isBubbleUpSuppressed, userId));
+ }
+
+ private void updateSecureNotificationsInternal(String pkg, boolean isContentSecure,
+ boolean isBubbleUpSuppressed, int userId) {
+ synchronized (mNotificationLock) {
+ for (int i = 0; i < mNotificationList.size(); i++) {
+ final NotificationRecord nr = mNotificationList.get(i);
+ final StatusBarNotification sbn = nr.getSbn();
+ if (UserHandle.getUserId(sbn.getUid()) == userId
+ && sbn.getPackageName().equals(pkg)) {
+ if (sbn.getIsContentSecure() != isContentSecure ||
+ nr.isBubbleUpSuppressedByAppLock() != isBubbleUpSuppressed) {
+ sbn.setIsContentSecure(isContentSecure);
+ nr.setBubbleUpSuppressedByAppLock(isBubbleUpSuppressed);
+ mListeners.notifyPostedLocked(nr, nr);
+ }
+ }
+ }
+ }
+ mRankingHandler.requestSort();
}
};
@@ -7544,9 +7591,11 @@
mUsageStats.registerEnqueuedByApp(pkg);
+ final boolean isContentSecure = mAppLockManagerService != null &&
+ mAppLockManagerService.shouldRedactNotification(pkg, userId);
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
- user, null, System.currentTimeMillis());
+ user, null, System.currentTimeMillis(), isContentSecure);
// setup local book-keeping
String channelId = notification.getChannelId();
@@ -7586,6 +7635,8 @@
r.setPostSilently(postSilently);
r.setFlagBubbleRemoved(false);
r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
+ r.setBubbleUpSuppressedByAppLock(mAppLockManagerService != null &&
+ mAppLockManagerService.requireUnlock(pkg, userId));
boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
r.setImportanceFixed(isImportanceFixed);
@@ -9175,7 +9226,8 @@
}
if (aboveThreshold && isNotificationForCurrentUser(record)) {
- if (mSystemReady && mAudioManager != null) {
+ boolean skipSound = mScreenOn && !mSoundVibScreenOn;
+ if (mSystemReady && mAudioManager != null && !skipSound) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
VibrationEffect vibration = record.getVibration();
@@ -9868,7 +9920,8 @@
r.getRankingScore(),
r.isConversation(),
r.getProposedImportance(),
- r.hasSensitiveContent());
+ r.hasSensitiveContent(),
+ r.isBubbleUpSuppressedByAppLock());
extractorDataBefore.put(r.getKey(), extractorData);
mRankingHelper.extractSignals(r);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6ab4b99..a8ec0e2 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -219,6 +219,8 @@
private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
private boolean mSensitiveContent = false;
+ private boolean mIsBubbleUpSuppressedByAppLock = false;
+
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
@@ -1679,6 +1681,14 @@
return mKeyguardManager;
}
+ public void setBubbleUpSuppressedByAppLock(boolean suppressed) {
+ mIsBubbleUpSuppressedByAppLock = suppressed;
+ }
+
+ public boolean isBubbleUpSuppressedByAppLock() {
+ return mIsBubbleUpSuppressedByAppLock;
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
index 3f4f7d3..3e883e7 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java
@@ -41,6 +41,7 @@
private final ArrayList<Notification.Action> mSystemSmartActions;
private final ArrayList<CharSequence> mSmartReplies;
private final int mImportance;
+ private final boolean mIsBubbleUpSuppressedByAppLock;
// These fields may not trigger a reranking but diffs here may be logged.
private final float mRankingScore;
@@ -54,7 +55,8 @@
Integer userSentiment, Integer suppressVisually,
ArrayList<Notification.Action> systemSmartActions,
ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
- boolean isConversation, int proposedImportance, boolean sensitiveContent) {
+ boolean isConversation, int proposedImportance, boolean sensitiveContent,
+ boolean isBubbleUpSuppressedByAppLock) {
mPosition = position;
mVisibility = visibility;
mShowBadge = showBadge;
@@ -73,6 +75,7 @@
mIsConversation = isConversation;
mProposedImportance = proposedImportance;
mSensitiveContent = sensitiveContent;
+ mIsBubbleUpSuppressedByAppLock = isBubbleUpSuppressedByAppLock;
}
// Returns whether the provided NotificationRecord differs from the cached data in any way.
@@ -93,7 +96,8 @@
|| !Objects.equals(mSmartReplies, r.getSmartReplies())
|| mImportance != r.getImportance()
|| mProposedImportance != r.getProposedImportance()
- || mSensitiveContent != r.hasSensitiveContent();
+ || mSensitiveContent != r.hasSensitiveContent()
+ || mIsBubbleUpSuppressedByAppLock != r.isBubbleUpSuppressedByAppLock();
}
// Returns whether the NotificationRecord has a change from this data for which we should
@@ -117,6 +121,7 @@
|| !r.rankingScoreMatches(mRankingScore)
|| mIsConversation != r.isConversation()
|| mProposedImportance != r.getProposedImportance()
- || mSensitiveContent != r.hasSensitiveContent();
+ || mSensitiveContent != r.hasSensitiveContent()
+ || mIsBubbleUpSuppressedByAppLock != r.isBubbleUpSuppressedByAppLock();
}
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 9afdde5..315c8e0 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1465,6 +1465,29 @@
return result;
}
+ private boolean requestsFakeSignature(AndroidPackage p) {
+ return p.getMetaData() != null &&
+ p.getMetaData().getString("fake-signature") != null;
+ }
+
+ private PackageInfo mayFakeSignature(AndroidPackage p, PackageInfo pi,
+ Set<String> permissions) {
+ try {
+ if (p.getMetaData() != null &&
+ p.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ String sig = p.getMetaData().getString("fake-signature");
+ if (sig != null &&
+ permissions.contains("android.permission.FAKE_PACKAGE_SIGNATURE")) {
+ pi.signatures = new Signature[] {new Signature(sig)};
+ }
+ }
+ } catch (Throwable t) {
+ // We should never die because of any failures, this is system code!
+ Log.w("PackageManagerService.FAKE_PACKAGE_SIGNATURE", t);
+ }
+ return pi;
+ }
+
public final PackageInfo generatePackageInfo(PackageStateInternal ps,
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
@@ -1498,13 +1521,14 @@
|| ArrayUtils.isEmpty(p.getPermissions())) ? Collections.emptySet()
: mPermissionManager.getInstalledPermissions(ps.getPackageName());
// Compute granted permissions only if package has requested permissions
- final Set<String> grantedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
+ final Set<String> grantedPermissions = (((flags & PackageManager.GET_PERMISSIONS) == 0
+ && !requestsFakeSignature(p))
|| ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
: mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);
- PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
+ PackageInfo packageInfo = mayFakeSignature(p, PackageInfoUtils.generate(p, gids, flags,
state.getFirstInstallTimeMillis(), ps.getLastUpdateTime(), installedPermissions,
- grantedPermissions, state, userId, ps);
+ grantedPermissions, state, userId, ps), grantedPermissions);
if (packageInfo == null) {
return null;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6b56b85..5d38c7c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -123,6 +123,7 @@
import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.app.AppLockManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -251,6 +252,8 @@
private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
new RemoteCallbackList<>();
+ private final AppLockManagerServiceInternal mAppLockManagerInternal;
+
public LauncherAppsImpl(Context context) {
mContext = context;
mIPM = AppGlobals.getPackageManager();
@@ -274,6 +277,7 @@
mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mAppLockManagerInternal = LocalServices.getService(AppLockManagerServiceInternal.class);
mInternal = new LocalService();
}
@@ -985,6 +989,7 @@
private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
Intent intent, int callingUid, UserHandle user) {
+ final Set<String> hiddenApps = mAppLockManagerInternal.getHiddenPackages(user.getIdentifier());
final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DIRECT_BOOT_AWARE
@@ -999,6 +1004,10 @@
// should not happen
continue;
}
+ if (hiddenApps.contains(packageName)) {
+ if (DEBUG) Slog.d(TAG, "Skipping package " + packageName);
+ continue;
+ }
final IncrementalStatesInfo incrementalStatesInfo =
mPackageManagerInternal.getIncrementalStatesInfo(packageName, callingUid,
user.getIdentifier());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8a369ec..b6eb297e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3223,15 +3223,24 @@
}
final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId);
- final boolean allowedPackageUid = packageUid == callingUid;
- // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
- final boolean allowedShell = callingUid == SHELL_UID
- && UserHandle.isSameApp(packageUid, callingUid);
-
- if (!allowedShell && !allowedPackageUid) {
- throw new SecurityException("Suspending package " + suspender.packageName
- + " in user " + targetUserId + " does not belong to calling uid " + callingUid);
+ if (packageUid == callingUid) {
+ return;
}
+
+ final String callerMismatchMessage = "Suspending package " + suspender.packageName
+ + " in user " + targetUserId + " does not belong to calling uid " + callingUid;
+ if (!UserHandle.isSameApp(packageUid, callingUid)) {
+ throw new SecurityException(callerMismatchMessage);
+ }
+
+ final UserManagerService ums = UserManagerService.getInstance();
+ final UserInfo parent = ums != null ? ums.getProfileParent(targetUserId) : null;
+
+ // If calling from a parent, we only need INTERACT_ACROSS_USERS, not full.
+ final boolean requireFullPermission = parent == null
+ || callingUid != snapshot.getPackageUid(suspender.packageName, 0, parent.id);
+ snapshot.enforceCrossUserPermission(callingUid, targetUserId, requireFullPermission,
+ false /* checkShell */, callerMismatchMessage);
}
void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
@@ -6798,16 +6807,6 @@
SparseArray<String> profileOwnerPackages) {
mProtectedPackages.setDeviceAndProfileOwnerPackages(
deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
- final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
- if (deviceOwnerPackage != null) {
- usersWithPoOrDo.add(deviceOwnerUserId);
- }
- final int sz = profileOwnerPackages.size();
- for (int i = 0; i < sz; i++) {
- if (profileOwnerPackages.valueAt(i) != null) {
- removeAllNonSystemPackageSuspensions(profileOwnerPackages.keyAt(i));
- }
- }
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index f7f76aa..2c5b6dd 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -776,19 +776,6 @@
Intent.CATEGORY_APP_EMAIL, userId),
userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
- // Browser
- String browserPackage = ArrayUtils.firstOrNull(getKnownPackages(
- KnownPackages.PACKAGE_BROWSER, userId));
- if (browserPackage == null) {
- browserPackage = getDefaultSystemHandlerActivityPackageForCategory(pm,
- Intent.CATEGORY_APP_BROWSER, userId);
- if (!pm.isSystemPackage(browserPackage)) {
- browserPackage = null;
- }
- }
- grantPermissionsToPackage(pm, browserPackage, userId, false /* ignoreSystemPackage */,
- true /*whitelistRestrictedPermissions*/, FOREGROUND_LOCATION_PERMISSIONS);
-
// Voice interaction
if (voiceInteractPackageNames != null) {
for (String voiceInteractPackageName : voiceInteractPackageNames) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2d4943f..8cc2645 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -219,6 +219,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.ExtconStateObserver;
import com.android.server.ExtconUEventObserver;
import com.android.server.GestureLauncherService;
@@ -678,6 +679,7 @@
boolean mHavePendingMediaKeyRepeatWithWakeLock;
private int mCurrentUserId;
+ private boolean haveEnableGesture = false;
// Maps global key codes to the components that will handle them.
private GlobalKeyManager mGlobalKeyManager;
@@ -728,6 +730,8 @@
private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26;
private static final int MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE = 27;
+ private SwipeToScreenshotListener mSwipeToScreenshot;
+
private class PolicyHandler extends Handler {
private PolicyHandler(Looper looper) {
@@ -906,6 +910,9 @@
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.THREE_FINGER_GESTURE), false, this,
+ UserHandle.USER_ALL);
updateSettings();
}
@@ -1787,10 +1794,14 @@
}
void showGlobalActionsInternal() {
+ final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.HIDE_POWERMENU_LOCKSCREEN, 0) == 1 && keyguardShowing) {
+ return;
+ }
if (mGlobalActions == null) {
mGlobalActions = mGlobalActionsFactory.get();
}
- final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
@@ -2275,6 +2286,12 @@
}
mHandler = new PolicyHandler(injector.getLooper());
+ mSwipeToScreenshot = new SwipeToScreenshotListener(mContext, new SwipeToScreenshotListener.Callbacks() {
+ @Override
+ public void onSwipeThreeFinger() {
+ interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ }
+ });
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
@@ -2832,6 +2849,18 @@
}
}
+ private void enableSwipeThreeFingerGesture(boolean enable){
+ if (enable) {
+ if (haveEnableGesture) return;
+ haveEnableGesture = true;
+ mWindowManagerFuncs.registerPointerEventListener(mSwipeToScreenshot, DEFAULT_DISPLAY);
+ } else {
+ if (!haveEnableGesture) return;
+ haveEnableGesture = false;
+ mWindowManagerFuncs.unregisterPointerEventListener(mSwipeToScreenshot, DEFAULT_DISPLAY);
+ }
+ }
+
/**
* Read values from config.xml that may be overridden depending on
* the configuration of the device.
@@ -2909,6 +2938,11 @@
mRingerToggleChord = VOLUME_HUSH_OFF;
}
+ //Three Finger Gesture
+ boolean threeFingerGesture = Settings.System.getIntForUser(resolver,
+ Settings.System.THREE_FINGER_GESTURE, 0, UserHandle.USER_CURRENT) == 1;
+ enableSwipeThreeFingerGesture(threeFingerGesture);
+
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
Settings.Secure.WAKE_GESTURE_ENABLED, 0,
diff --git a/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java b/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java
new file mode 100644
index 0000000..88a465b
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java
@@ -0,0 +1,148 @@
+/*
+ * 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.server.policy;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicyConstants.PointerEventListener;
+
+public class SwipeToScreenshotListener implements PointerEventListener {
+ private static final String TAG = "SwipeToScreenshotListener";
+ private static final int THREE_GESTURE_STATE_NONE = 0;
+ private static final int THREE_GESTURE_STATE_DETECTING = 1;
+ private static final int THREE_GESTURE_STATE_DETECTED_FALSE = 2;
+ private static final int THREE_GESTURE_STATE_DETECTED_TRUE = 3;
+ private static final int THREE_GESTURE_STATE_NO_DETECT = 4;
+ private boolean mBootCompleted;
+ private Context mContext;
+ private boolean mDeviceProvisioned = false;
+ private float[] mInitMotionY;
+ private int[] mPointerIds;
+ private int mThreeGestureState = THREE_GESTURE_STATE_NONE;
+ private int mThreeGestureThreshold;
+ private int mThreshold;
+ private final Callbacks mCallbacks;
+ DisplayMetrics mDisplayMetrics;
+
+ public SwipeToScreenshotListener(Context context, Callbacks callbacks) {
+ mPointerIds = new int[3];
+ mInitMotionY = new float[3];
+ mContext = context;
+ mCallbacks = callbacks;
+ mDisplayMetrics = mContext.getResources().getDisplayMetrics();
+ mThreshold = (int) (50.0f * mDisplayMetrics.density);
+ mThreeGestureThreshold = mThreshold * 3;
+ }
+
+ @Override
+ public void onPointerEvent(MotionEvent event) {
+ if (!mBootCompleted) {
+ mBootCompleted = SystemProperties.getBoolean("sys.boot_completed", false);
+ return;
+ }
+ if (!mDeviceProvisioned) {
+ mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ return;
+ }
+ if (event.getAction() == 0) {
+ changeThreeGestureState(THREE_GESTURE_STATE_NONE);
+ } else if (mThreeGestureState == THREE_GESTURE_STATE_NONE && event.getPointerCount() == 3) {
+ if (checkIsStartThreeGesture(event)) {
+ changeThreeGestureState(THREE_GESTURE_STATE_DETECTING);
+ for (int i = 0; i < 3; i++) {
+ mPointerIds[i] = event.getPointerId(i);
+ mInitMotionY[i] = event.getY(i);
+ }
+ } else {
+ changeThreeGestureState(THREE_GESTURE_STATE_NO_DETECT);
+ }
+ }
+ if (mThreeGestureState == THREE_GESTURE_STATE_DETECTING) {
+ if (event.getPointerCount() != 3) {
+ changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_FALSE);
+ return;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ float distance = 0.0f;
+ int i = 0;
+ while (i < 3) {
+ int index = event.findPointerIndex(mPointerIds[i]);
+ if (index < 0 || index >= 3) {
+ changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_FALSE);
+ return;
+ } else {
+ distance += event.getY(index) - mInitMotionY[i];
+ i++;
+ }
+ }
+ if (distance >= ((float) mThreeGestureThreshold)) {
+ changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_TRUE);
+ mCallbacks.onSwipeThreeFinger();
+ }
+ }
+ }
+ }
+
+ private void changeThreeGestureState(int state) {
+ if (mThreeGestureState != state){
+ mThreeGestureState = state;
+ boolean shouldEnableProp = mThreeGestureState == THREE_GESTURE_STATE_DETECTED_TRUE ||
+ mThreeGestureState == THREE_GESTURE_STATE_DETECTING;
+ try {
+ SystemProperties.set("sys.android.screenshot", shouldEnableProp ? "true" : "false");
+ } catch(Exception e) {
+ Log.e(TAG, "Exception when setprop", e);
+ }
+ }
+ }
+
+ private boolean checkIsStartThreeGesture(MotionEvent event) {
+ if (event.getEventTime() - event.getDownTime() > 500) {
+ return false;
+ }
+ int height = mDisplayMetrics.heightPixels;
+ int width = mDisplayMetrics.widthPixels;
+ float minX = Float.MAX_VALUE;
+ float maxX = Float.MIN_VALUE;
+ float minY = Float.MAX_VALUE;
+ float maxY = Float.MIN_VALUE;
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ float x = event.getX(i);
+ float y = event.getY(i);
+ if (y > ((float) (height - mThreshold))) {
+ return false;
+ }
+ maxX = Math.max(maxX, x);
+ minX = Math.min(minX, x);
+ maxY = Math.max(maxY, y);
+ minY = Math.min(minY, y);
+ }
+ if (maxY - minY <= mDisplayMetrics.density * 150.0f) {
+ return maxX - minX <= ((float) (width < height ? width : height));
+ }
+ return false;
+ }
+
+ interface Callbacks {
+ void onSwipeThreeFinger();
+ }
+}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5956594..b418938 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -259,6 +259,7 @@
public void shutdown(boolean confirm);
public void reboot(boolean confirm);
+ public void reboot(boolean confirm, String reason);
public void rebootSafeMode(boolean confirm);
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a172de0..5762e48 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3939,7 +3939,7 @@
}
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
- @Nullable final String reason, boolean wait) {
+ @Nullable final String reason, boolean wait, boolean custom) {
if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
throw new UnsupportedOperationException(
@@ -3966,7 +3966,11 @@
if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
ShutdownThread.rebootSafeMode(getUiContext(), confirm);
} else if (haltMode == HALT_MODE_REBOOT) {
- ShutdownThread.reboot(getUiContext(), reason, confirm);
+ if (custom) {
+ ShutdownThread.rebootCustom(getUiContext(), reason, confirm);
+ } else {
+ ShutdownThread.reboot(getUiContext(), reason, confirm);
+ }
} else {
ShutdownThread.shutdown(getUiContext(), reason, confirm);
}
@@ -6563,7 +6567,7 @@
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long ident = Binder.clearCallingIdentity();
try {
- shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
+ shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait, false);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6583,7 +6587,29 @@
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long ident = Binder.clearCallingIdentity();
try {
- shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait);
+ shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait, false);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Reboots the device.
+ *
+ * @param confirm If true, shows a reboot confirmation dialog.
+ * @param reason The reason for the reboot, or null if none.
+ * @param wait If true, this call waits for the reboot to complete and does not return.
+ */
+ @Override // Binder call
+ public void rebootCustom(boolean confirm, String reason, boolean wait) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+ if (PowerManager.REBOOT_RECOVERY.equals(reason)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait, true);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6602,7 +6628,7 @@
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long ident = Binder.clearCallingIdentity();
try {
- shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
+ shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait, false);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 4bf8a78..4d88510 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -93,6 +93,7 @@
private static boolean sIsStarted = false;
private static boolean mReboot;
+ private static boolean mRebootCustom;
private static boolean mRebootSafeMode;
private static boolean mRebootHasProgressBar;
private static String mReason;
@@ -159,6 +160,7 @@
*/
public static void shutdown(final Context context, String reason, boolean confirm) {
mReboot = false;
+ mRebootCustom = false;
mRebootSafeMode = false;
mReason = reason;
shutdownInner(context, confirm);
@@ -255,6 +257,26 @@
*/
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
+ mRebootCustom = false;
+ mRebootSafeMode = false;
+ mRebootHasProgressBar = false;
+ mReason = reason;
+ shutdownInner(context, confirm);
+ }
+
+ /**
+ * Request a clean shutdown, waiting for subsystems to clean up their
+ * state etc. Must be called from a Looper thread in which its UI
+ * is shown.
+ *
+ * @param context Context used to display the shutdown progress dialog. This must be a context
+ * suitable for displaying UI (aka Themable).
+ * @param reason code to pass to the kernel (e.g. "recovery", "bootloader"), or null.
+ * @param confirm true if user confirmation is needed before shutting down.
+ */
+ public static void rebootCustom(final Context context, String reason, boolean confirm) {
+ mReboot = true;
+ mRebootCustom = true;
mRebootSafeMode = false;
mRebootHasProgressBar = false;
mReason = reason;
@@ -276,6 +298,7 @@
}
mReboot = true;
+ mRebootCustom = false;
mRebootSafeMode = true;
mRebootHasProgressBar = false;
mReason = null;
@@ -344,7 +367,7 @@
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
- } else if (showSysuiReboot()) {
+ } else if (mRebootCustom && showSysuiReboot()) {
return null;
} else {
// Factory reset path. Set the dialog message accordingly.
@@ -375,7 +398,7 @@
try {
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
- if (service.showShutdownUi(mReboot, mReason)) {
+ if (service.showShutdownUi(mReboot, mReason, mRebootCustom)) {
// Sysui will handle shutdown UI.
if (DEBUG) {
Log.d(TAG, "SysUI handling shutdown UI");
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 14e0ce1..0ebe6d0 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -138,7 +138,7 @@
*/
void setTopAppHidesStatusBar(boolean hidesStatusBar);
- boolean showShutdownUi(boolean isReboot, String requestString);
+ boolean showShutdownUi(boolean isReboot, String requestString, boolean rebootCustom);
/**
* Notify system UI the immersive prompt should be dismissed as confirmed, and the confirmed
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6e95cbc..4459c75 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -626,14 +626,14 @@
}
@Override
- public boolean showShutdownUi(boolean isReboot, String reason) {
+ public boolean showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) {
if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) {
return false;
}
IStatusBar bar = mBar;
if (bar != null) {
try {
- bar.showShutdownUi(isReboot, reason);
+ bar.showShutdownUi(isReboot, reason, rebootCustom);
return true;
} catch (RemoteException ex) {}
}
@@ -1639,11 +1639,8 @@
* Allows the status bar to reboot the device.
*/
@Override
- public void reboot(boolean safeMode) {
+ public void reboot(boolean safeMode, String reason) {
enforceStatusBarService();
- String reason = safeMode
- ? PowerManager.REBOOT_SAFE_MODE
- : PowerManager.SHUTDOWN_USER_REQUESTED;
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1653,7 +1650,7 @@
if (safeMode) {
ShutdownThread.rebootSafeMode(getUiContext(), true);
} else {
- ShutdownThread.reboot(getUiContext(), reason, false);
+ ShutdownThread.rebootCustom(getUiContext(), reason, false);
}
});
} finally {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2b05993..99ae379 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -82,7 +82,9 @@
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.app.AppLockManagerServiceInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -269,6 +271,8 @@
}
}
+ private AppLockManagerServiceInternal mAppLockManagerService = null;
+
public TrustManagerService(Context context) {
this(context, new Injector(new LockPatternUtils(context), Looper.myLooper()));
}
@@ -870,9 +874,17 @@
}
setDeviceLockedForUser(id, deviceLocked);
+ getAppLockManagerService().notifyDeviceLocked(deviceLocked, id);
}
}
+ private AppLockManagerServiceInternal getAppLockManagerService() {
+ if (mAppLockManagerService == null) {
+ mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class);
+ }
+ return mAppLockManagerService;
+ }
+
private void setDeviceLockedForUser(@UserIdInt int userId, boolean locked) {
final boolean changed;
synchronized (mDeviceLockedForUser) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c3efcb1..04ec11c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2684,6 +2684,20 @@
WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
+ // remove gone UIDs from the map
+ boolean arrayChanged = false;
+ final int uidCount = wallpaper.mUidToDimAmount.size();
+ int[] uids = new int[uidCount];
+ for (int i = 0; i < uidCount; i++) {
+ uids[i] = wallpaper.mUidToDimAmount.keyAt(i);
+ }
+ for (Integer u : uids) {
+ final String cname = mPackageManagerInternal.getNameForUid(u);
+ if (cname != null && !cname.isEmpty()) continue;
+ wallpaper.mUidToDimAmount.remove(u);
+ arrayChanged = true;
+ }
+
if (dimAmount == 0.0f) {
wallpaper.mUidToDimAmount.remove(uid);
} else {
@@ -2718,7 +2732,7 @@
changed = true;
}
}
- if (changed) {
+ if (changed || arrayChanged) {
saveSettingsLocked(wallpaper.userId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 182e1c1..cda85c6 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -65,6 +65,7 @@
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
+import com.android.server.app.AppLockManagerServiceInternal;
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
/**
@@ -232,6 +233,10 @@
return true;
}
+ if (interceptLockedAppIfNeeded()) {
+ return true;
+ }
+
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
@@ -543,4 +548,49 @@
.build();
}
+ private AppLockManagerServiceInternal getAppLockManagerService() {
+ return mService.getAppLockManagerService();
+ }
+
+ private boolean interceptLockedAppIfNeeded() {
+ if (getAppLockManagerService() == null) return false;
+ final Intent interceptingIntent = getAppLockManagerService().interceptActivity(getInterceptorInfo(null));
+ if (interceptingIntent == null) {
+ return false;
+ }
+ mIntent = interceptingIntent;
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+ final TaskFragment taskFragment = getLaunchTaskFragment();
+ // If we are intercepting and there was a task, convert it into an extra for the
+ // ConfirmCredentials intent and unassign it, as otherwise the task will move to
+ // front even if ConfirmCredentials is cancelled.
+ if (mInTask != null) {
+ mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId);
+ mInTask = null;
+ } else if (taskFragment != null) {
+ // If the original intent is started to an embedded TaskFragment, append its parent task
+ // id to extra. It is to embed back the original intent to the TaskFragment with the
+ // same task.
+ final Task parentTask = taskFragment.getTask();
+ if (parentTask != null) {
+ mIntent.putExtra(EXTRA_TASK_ID, parentTask.mTaskId);
+ }
+ }
+ if (mActivityOptions == null) {
+ mActivityOptions = ActivityOptions.makeBasic();
+ }
+
+ final UserInfo parent = mUserManager.getProfileParent(mUserId);
+ if (parent != null) {
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
+ mRealCallingUid, mRealCallingPid);
+ } else {
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+ mRealCallingUid, mRealCallingPid);
+ }
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index c088118..4556554 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -825,4 +825,6 @@
/** Returns whether assist data is allowed. */
public abstract boolean isAssistDataAllowed();
+
+ public abstract boolean isVisibleActivity(IBinder activityToken);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e283f3e..5274082 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -278,6 +278,7 @@
import com.android.server.am.PendingIntentController;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.UserState;
+import com.android.server.app.AppLockManagerServiceInternal;
import com.android.server.firewall.IntentFirewall;
import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
import com.android.server.pm.UserManagerService;
@@ -286,9 +287,12 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.usage.AppStandbyInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.wm.shell.Flags;
+import com.android.internal.gmscompat.AttestationHooks;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
@@ -803,6 +807,8 @@
private Set<Integer> mProfileOwnerUids = new ArraySet<Integer>();
+ public AppStandbyInternal mAppStandbyInternal;
+
private final class SettingObserver extends ContentObserver {
private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
@@ -856,6 +862,8 @@
}
};
+ private AppLockManagerServiceInternal mAppLockManagerService = null;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public ActivityTaskManagerService(Context context) {
mContext = context;
@@ -888,6 +896,7 @@
mGrammaticalManagerInternal = LocalServices.getService(
GrammaticalInflectionManagerInternal.class);
}
+ mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
}
public void onInitPowerManagement() {
@@ -1808,6 +1817,7 @@
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
final long origId = Binder.clearCallingIdentity();
try {
@@ -1981,7 +1991,9 @@
@Override
public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
- enforceTaskPermission("getFocusedRootTaskInfo()");
+ if (!AttestationHooks.shouldBypassTaskPermission(mContext)) {
+ enforceTaskPermission("getFocusedRootTaskInfo()");
+ }
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3105,7 +3117,9 @@
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) {
- enforceTaskPermission("registerTaskStackListener()");
+ if (!AttestationHooks.shouldBypassTaskPermission(mContext)) {
+ enforceTaskPermission("registerTaskStackListener()");
+ }
mTaskChangeNotificationController.registerTaskStackListener(listener);
}
@@ -3913,10 +3927,21 @@
Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible");
return null;
}
+
+ final Task rootTask = task.getRootTask();
+ final String packageName =
+ rootTask != null && rootTask.realActivity != null
+ ? rootTask.realActivity.getPackageName()
+ : null;
+ if (packageName != null && getAppLockManagerService().requireUnlock(
+ packageName, task.mUserId)) {
+ return null;
+ }
+
// Note that if updateCache is true, ActivityRecord#shouldUseAppThemeSnapshot will
// be used to decide whether the task is allowed to be captured because that may
// be retrieved by recents. While if updateCache is false, the real snapshot will
- // always be taken and the snapshot won't be put into SnapshotPersister.
+ // always be taken and the snapshot won't be put into SnapshotPersister.
if (updateCache) {
return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
} else {
@@ -5407,6 +5432,13 @@
return mWallpaperManagerInternal;
}
+ AppLockManagerServiceInternal getAppLockManagerService() {
+ if (mAppLockManagerService == null) {
+ mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class);
+ }
+ return mAppLockManagerService;
+ }
+
AppWarnings getAppWarningsLocked() {
return mAppWarnings;
}
@@ -7358,6 +7390,14 @@
public boolean isAssistDataAllowed() {
return ActivityTaskManagerService.this.isAssistDataAllowed();
}
+
+ @Override
+ public boolean isVisibleActivity(IBinder activityToken) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken);
+ return r != null && r.isInterestingToUserLocked();
+ }
+ }
}
static boolean isPip2ExperimentEnabled() {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index d376613..0f6f9c2 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -78,6 +78,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.view.RotationPolicy;
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
@@ -221,6 +222,8 @@
@AllowAllRotations
private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
+ private int mUserRotationAngles = -1;
+
@WindowManagerPolicy.UserRotationMode
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
@@ -1327,10 +1330,13 @@
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
// Otherwise, use sensor only if requested by the application or enabled
// by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
- if (sensorRotation != Surface.ROTATION_180
- || getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+ boolean allowed = true;
+ if (orientation != ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+ allowed = RotationPolicy.isRotationAllowed(sensorRotation,
+ mUserRotationAngles, getAllowAllRotations() != ALLOW_ALL_ROTATIONS_DISABLED);
+ }
+ if (allowed) {
preferredRotation = sensorRotation;
} else {
preferredRotation = lastRotation;
@@ -1623,6 +1629,13 @@
shouldUpdateRotation = true;
}
+ final int userRotationAngles = Settings.System.getIntForUser(resolver,
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1, UserHandle.USER_CURRENT);
+ if (mUserRotationAngles != userRotationAngles) {
+ mUserRotationAngles = userRotationAngles;
+ shouldUpdateRotation = true;
+ }
+
final int userRotationMode = Settings.System.getIntForUser(resolver,
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
? WindowManagerPolicy.USER_ROTATION_FREE
@@ -2135,6 +2148,9 @@
Settings.System.ACCELEROMETER_ROTATION), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.USER_ROTATION), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae5a5cb..063dda2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3630,6 +3630,14 @@
// Called by window manager policy. Not exposed externally.
@Override
+ public void reboot(boolean confirm, String reason) {
+ // Pass in the UI context, since ShutdownThread requires it (to show UI).
+ ShutdownThread.rebootCustom(ActivityThread.currentActivityThread().getSystemUiContext(),
+ reason, confirm);
+ }
+
+ // Called by window manager policy. Not exposed externally.
+ @Override
public void rebootSafeMode(boolean confirm) {
// Pass in the UI context, since ShutdownThread requires it (to show UI).
ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(),
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e19f08c..1aa4d15 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -138,6 +138,7 @@
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
import com.android.server.flags.FeatureFlagsService;
+import com.android.server.gmscompat.AttestationService;
import com.android.server.gpu.GpuService;
import com.android.server.grammaticalinflection.GrammaticalInflectionService;
import com.android.server.graphics.fonts.FontManagerService;
@@ -234,6 +235,7 @@
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowManagerGlobalLock;
import com.android.server.wm.WindowManagerService;
+import com.android.server.lineage.health.HealthInterfaceService;
import dalvik.system.VMRuntime;
@@ -471,6 +473,9 @@
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
+ private static final String APP_LOCK_SERVICE_CLASS =
+ "com.android.server.app.AppLockManagerService$Lifecycle";
+
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -2615,6 +2620,10 @@
t.traceEnd();
}
+ t.traceBegin("AppLockManagerService");
+ mSystemServiceManager.startService(APP_LOCK_SERVICE_CLASS);
+ t.traceEnd();
+
if (!isWatch) {
// We don't run this on watches as there are no plans to use the data logged
// on watch devices.
@@ -2669,6 +2678,10 @@
t.traceBegin("StartBackgroundInstallControlService");
mSystemServiceManager.startService(BackgroundInstallControlService.class);
t.traceEnd();
+
+ t.traceBegin("StartHealthService");
+ mSystemServiceManager.startService(HealthInterfaceService.class);
+ t.traceEnd();
}
t.traceBegin("StartMediaProjectionManager");
@@ -3327,6 +3340,10 @@
}
t.traceEnd();
+ // AttestationService
+ t.traceBegin("AttestationService");
+ mSystemServiceManager.startService(AttestationService.class);
+ t.traceEnd();
t.traceEnd(); // startOtherServices
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 2ba3969..8d80d95 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -388,7 +388,8 @@
protected LockscreenCredential newPattern(String pattern) {
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
- pattern.getBytes()));
+ pattern.getBytes(), LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
}
protected LockscreenCredential nonePassword() {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index b9ae670..e7c4f4b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -211,7 +211,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
@@ -219,8 +220,10 @@
new String[] { "set-pattern", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
verify(mLockPatternUtils).setLockCredential(
- LockscreenCredential.createPattern(stringToPattern("4321")),
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("4321"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId);
}
@@ -230,7 +233,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
@@ -256,7 +260,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
@@ -265,7 +270,8 @@
mShellCallback, mResultReceiver));
verify(mLockPatternUtils).setLockCredential(
LockscreenCredential.createNone(),
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId);
}
@@ -275,7 +281,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
@@ -336,7 +343,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
@@ -356,7 +364,8 @@
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.checkCredential(
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId, null)).thenReturn(true);
when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
.thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
@@ -368,8 +377,10 @@
mShellCallback, mResultReceiver));
verify(mLockPatternUtils).setLockCredential(
- LockscreenCredential.createPattern(stringToPattern("4321")),
- LockscreenCredential.createPattern(stringToPattern("1234")),
+ LockscreenCredential.createPattern(stringToPattern("4321"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
+ LockscreenCredential.createPattern(stringToPattern("1234"),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT),
mUserId);
}
@@ -387,7 +398,8 @@
}
private List<LockPatternView.Cell> stringToPattern(String str) {
- return LockPatternUtils.byteArrayToPattern(str.getBytes());
+ return LockPatternUtils.byteArrayToPattern(str.getBytes(),
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
}
private PasswordMetrics metricsForAdminQuality(int quality) {