Merge tag 'android-14.0.0_r31' into leaf-3.2
Android 14.0.0 Release 31 (AP1A.240405.002.A1)
* tag 'android-14.0.0_r31':
Gate SideFpsOverlayViewBinder and DeviceEntrySideFpsOverlayInteractor on device having SFPS
Return correct result
Fix security vulnerability that creates user with no restrictions when accountOptions are too long.
isUserInLockDown can be true when there are other strong auth requirements
Added limitations for attributions to handle invalid cases
Stop marking apps as privileged if they are not signed properly.
Don't store invalid pkgs when migrating filters
Change-Id: Ib169e4292ba0c2aabfb5c866be2909d820ba97e0
diff --git a/Android.bp b/Android.bp
index 676a0f5..5b45ac6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -257,6 +257,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 9b5316f..8cf116c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -95,6 +95,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";
@@ -328,6 +329,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";
@@ -37104,6 +37106,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 f331e7f..f570926 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1169,6 +1169,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.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
New API must be flagged with @FlaggedApi: field android.accessibilityservice.AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 260e985..e1245f3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -950,4 +950,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/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 2162e3a..9ae5c10 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -62,6 +62,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;
@@ -1344,6 +1346,7 @@
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
+ AttestationHooks.initApplicationBeforeOnCreate(context);
return app;
}
@@ -1361,6 +1364,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 8b8576a..cee3dbd 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/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/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 91adc37..c380ae36 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -244,6 +244,9 @@
private List<SplitPermissionInfo> mSplitPermissionInfos;
+ private static String[] sLocationProviderPkgNames;
+ private static String[] sLocationExtraPkgNames;
+
/**
* Creates a new instance.
*
@@ -1193,6 +1196,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;
}
@@ -1208,6 +1221,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 2cc56d8..1dd656f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5591,6 +5591,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
@@ -6003,6 +6016,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
@@ -6012,6 +6032,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.
@@ -6034,6 +6062,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
@@ -6064,6 +6125,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
@@ -6261,6 +6356,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
@@ -6489,6 +6602,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
*/
@@ -6524,6 +6643,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
*/
@@ -6587,10 +6716,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);
@@ -8050,6 +8182,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
@@ -11657,6 +11807,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
*/
@@ -11911,6 +12067,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.
*
@@ -12133,6 +12301,68 @@
public static final String HIDE_PRIVATESPACE_ENTRY_POINT = "hide_privatespace_entry_point";
/**
+ * 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/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e83488e..3da4081 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7424,6 +7424,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
@@ -12279,4 +12283,12 @@
void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) {
mWindowlessBackKeyCallback = callback;
}
+
+ private boolean isSwipeToScreenshotGestureActive() {
+ try {
+ return ActivityManager.getService().isSwipeToScreenshotGestureActive();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
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..db118a3
--- /dev/null
+++ b/core/java/com/android/internal/gmscompat/AttestationHooks.java
@@ -0,0 +1,224 @@
+/*
+ * 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) {
+ Log.e(TAG, "shouldBypassTaskPermission: unable to get gms uid", 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 dbe4fba..e10602e 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -2854,9 +2854,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 5351c6d..3156fb2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -164,7 +164,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 757978b..4855b69 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.
@@ -947,16 +952,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;
}
@@ -966,7 +973,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];
}
@@ -975,7 +982,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;
}
@@ -1046,6 +1053,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 c6a241f..ecaff74 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4310,6 +4310,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. -->
@@ -7871,6 +7886,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-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/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 eddd81e..00cf424 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 bf8e55f..5ae20e3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4856,7 +4856,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>
@@ -4941,7 +4941,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..30cdcee
--- /dev/null
+++ b/core/res/res/values/leaf_config.xml
@@ -0,0 +1,46 @@
+<?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>
+</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..f24ceba9
--- /dev/null
+++ b/core/res/res/values/leaf_symbols.xml
@@ -0,0 +1,35 @@
+<?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" />
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4596ca7..76b935e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -995,6 +995,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>
@@ -5116,16 +5128,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 619ec31..5eae2d7 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 5616d1d..dd0bae2 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.systemui.xml b/data/etc/com.android.systemui.xml
index 43683ff..c52a209 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -67,6 +67,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/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 2d8c5a3..caadbef 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 ec456e0..ae0ebc1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -257,6 +257,7 @@
Settings.Secure.CREDENTIAL_SERVICE,
Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
Settings.Secure.EVEN_DIMMER_ACTIVATED,
- Settings.Secure.EVEN_DIMMER_MIN_NITS
+ Settings.Secure.EVEN_DIMMER_MIN_NITS,
+ 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 59c3cd3..f10e9bd1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -107,5 +107,7 @@
Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
Settings.System.NOTIFICATION_COOLDOWN_ALL,
Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ Settings.System.NETWORK_TRAFFIC_STATE,
+ Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 5ad14ce..0b60d43 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -409,5 +409,6 @@
VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR);
VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_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 572303a..d961ea4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -244,5 +244,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 f10ac1b..086cdeb 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -205,6 +205,7 @@
"LowLightDreamLib",
"motion_tool_lib",
"notification_flags_lib",
+ "vendor.lineage.powershare-V1.0-java",
],
libs: [
"keepanno-annotations",
@@ -359,6 +360,7 @@
"androidx.test.ext.junit",
"androidx.test.ext.truth",
"kotlin-test",
+ "vendor.lineage.powershare-V1.0-java",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f1029a3..7a0ccf7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -359,6 +359,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"
@@ -485,7 +488,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">
@@ -499,6 +502,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/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 9c46ebdc..ec1f8d0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -388,7 +388,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",
@@ -397,7 +402,7 @@
return null
}
- return GhostedViewLaunchAnimatorController(view, cujType)
+ return GhostedViewLaunchAnimatorController(animatedView, cujType)
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 168039e..14b2131 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -212,7 +212,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 5d85fba..0c7c46d 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/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_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..7af4a05
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background_no_mask.xml
@@ -0,0 +1,36 @@
+<?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="@color/qs_tile_ripple_color">
+ <item android:id="@id/background">
+ <layer-list>
+ <item
+ android:id="@+id/qs_tile_background_base"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item android:id="@+id/qs_tile_background_overlay">
+ <selector>
+ <item
+ android:state_hovered="true"
+ android:drawable="@drawable/qs_tile_background_shape_round" />
+ <item
+ android:state_focused="true"
+ android:drawable="@drawable/qs_tile_background_shape_round" />
+ </selector>
+ </item>
+ </layer-list>
+ </item>
+</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/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 16eba22..438d394 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -127,7 +127,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"
@@ -195,10 +196,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">
@@ -250,6 +325,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"
@@ -313,7 +389,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_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 3be9993..53af932 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -56,10 +56,4 @@
android:importantForAccessibility="auto" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
- <View
- android:id="@+id/nav_bar_background"
- android:layout_width="match_parent"
- android:layout_height="@dimen/navigation_bar_size"
- android:layout_gravity="bottom"
- android:background="#ff000000" />
</merge>
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..10cf162
--- /dev/null
+++ b/packages/SystemUI/res/values-ast-rES/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">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>
+</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..ad2f865
--- /dev/null
+++ b/packages/SystemUI/res/values-az/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">Yenidən başlat\u2026</string>
+ <string name="global_action_restart_system">Sistem</string>
+ <string name="global_action_restart_recovery">Bərpa</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">Bərpa 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\nBatereyaya qənaət</string>
+ <string name="quick_settings_powershare_off_low_battery_label">Naqilsiz Enerji Paylaşma bağlıdır\nBatereya 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>
+</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..6a373e6
--- /dev/null
+++ b/packages/SystemUI/res/values-be/cm_strings.xml
@@ -0,0 +1,36 @@
+<?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_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_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>
+</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..32a3644
--- /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">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">Рестартиране в режим 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-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..64a0073
--- /dev/null
+++ b/packages/SystemUI/res/values-ca/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">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>
+</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..4e7e4a2
--- /dev/null
+++ b/packages/SystemUI/res/values-cs/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">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>
+</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..399c367
--- /dev/null
+++ b/packages/SystemUI/res/values-cy/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">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>
+</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..2e84f70
--- /dev/null
+++ b/packages/SystemUI/res/values-da/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">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">Fastboot</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 fastboot mode\u2026</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>
+</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..0a467a3
--- /dev/null
+++ b/packages/SystemUI/res/values-de/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">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">Neustart in den Fastbootd-Modus\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>
+</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..948e69e
--- /dev/null
+++ b/packages/SystemUI/res/values-el/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">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>
+</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..6394ad5
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rAU/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">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>
+</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..6394ad5
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rCA/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">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>
+</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..6394ad5
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rGB/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">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>
+</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..6394ad5
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rIN/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">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>
+</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/cm_strings.xml b/packages/SystemUI/res/values-fr/cm_strings.xml
new file mode 100644
index 0000000..a2c07d9
--- /dev/null
+++ b/packages/SystemUI/res/values-fr/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">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 fastboot\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>
+</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..92f81b0
--- /dev/null
+++ b/packages/SystemUI/res/values-hu/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">Ú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">Fastboot-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 Fastboot-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>
+</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-it/cm_strings.xml b/packages/SystemUI/res/values-it/cm_strings.xml
new file mode 100644
index 0000000..dd31cb4
--- /dev/null
+++ b/packages/SystemUI/res/values-it/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">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>
+</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..551fc5e
--- /dev/null
+++ b/packages/SystemUI/res/values-ja/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">ワイヤレス 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>
+</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..06c2f09
--- /dev/null
+++ b/packages/SystemUI/res/values-ka/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 გამორთ.\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>
+</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..c7ef0cf
--- /dev/null
+++ b/packages/SystemUI/res/values-ko/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">무선 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-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 cfb4017..d841117 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-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..e967d1e
--- /dev/null
+++ b/packages/SystemUI/res/values-nl/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">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">Fastboot</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 fastboot 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>
+</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..5f28900
--- /dev/null
+++ b/packages/SystemUI/res/values-pl/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">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">Ponowne uruchamianie w trybie recovery\u2026</string>
+ <string name="global_action_restart_bootloader_progress">Ponowne uruchamianie w trybie bootloader\u2026</string>
+ <string name="global_action_restart_download_progress">Ponowne uruchamianie w trybie download\u2026</string>
+ <string name="global_action_restart_fastboot_progress">Ponowne uruchamianie w trybie fastbootd\u2026</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>
+</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..6f926b4
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rBR/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">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 de 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_caffeine_label">Cafeína</string>
+ <string name="accessibility_quick_settings_caffeine_off">Cafeína desligado.</string>
+ <string name="accessibility_quick_settings_caffeine_on">Cafeína ligado.</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..7670fd7
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rPT/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">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>
+</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..98c40d4
--- /dev/null
+++ b/packages/SystemUI/res/values-ro/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">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">Fastboot</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 fastboot\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>
+</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..e5955e5
--- /dev/null
+++ b/packages/SystemUI/res/values-ru/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">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>
+</resources>
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..2be5d98
--- /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">Fastboot</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 fastboot\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..813b4c7
--- /dev/null
+++ b/packages/SystemUI/res/values-sl/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">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>
+</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..9b6d2d3
--- /dev/null
+++ b/packages/SystemUI/res/values-sq/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">Rinisni\u2026</string>
+ <string name="global_action_restart_system">Sistemi</string>
+ <string name="global_action_restart_recovery">Rimëkëmbje</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 rivendosjes\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>
+</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-sv/cm_strings.xml b/packages/SystemUI/res/values-sv/cm_strings.xml
new file mode 100644
index 0000000..860b19e
--- /dev/null
+++ b/packages/SystemUI/res/values-sv/cm_strings.xml
@@ -0,0 +1,29 @@
+<?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_progress">Startar om\u2026</string>
+ <string name="quick_settings_powershare_label">Trådlös strömdelning</string>
+ <string name="quick_settings_powershare_off_powersave_label">Trådlös strömdelning av\nStrömsparläge</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-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..e8403e6
--- /dev/null
+++ b/packages/SystemUI/res/values-tr/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">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">Fastboot</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>
+</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..e895d16
--- /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..8e82614
--- /dev/null
+++ b/packages/SystemUI/res/values-vi/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">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">Fastboot</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>
+</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..27d5889
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rCN/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-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..cec95bf
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rTW/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">Recovery</string>
+ <string name="global_action_restart_bootloader">Bootloader</string>
+ <string name="global_action_restart_download">下載</string>
+ <string name="global_action_restart_fastboot">Fastboot</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_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/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 5f6a39a..3a23599b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -215,7 +215,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 33a0a06..1eb40c4 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
+ internet,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,powershare,caffeine,dataswitch
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b947801..734b79d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -557,7 +557,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>
@@ -596,7 +596,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>
@@ -1001,9 +1001,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>
@@ -1658,6 +1658,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_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..52af5fb
--- /dev/null
+++ b/packages/SystemUI/res/values/leaf_strings.xml
@@ -0,0 +1,46 @@
+<!--
+ 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>
+ <!-- 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>
+</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e10925d..0cbea13 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1514,6 +1514,15 @@
<!-- Name of the alarm status bar icon. -->
<string name="status_bar_alarm">Alarm</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>
+
<!-- Wallet strings -->
<!-- Wallet empty state, title [CHAR LIMIT=32] -->
<string name="wallet_title">Wallet</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e3d4419..344cd6e 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>
@@ -389,11 +389,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>
@@ -406,15 +406,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 -->
@@ -681,7 +683,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 db7ff88..ff9ce79 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 2aab1f1..5963a14 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;
@@ -32,6 +37,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;
@@ -50,6 +60,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,
@@ -94,6 +109,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()) {
@@ -114,6 +161,7 @@
@Override
public void startAppearAnimation() {
+ updatePinScrambling();
super.startAppearAnimation();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 871d57d..3bd3b96 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;
@@ -117,21 +119,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();
@@ -143,6 +131,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 fda23b7f..4096637 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
@@ -97,6 +97,9 @@
*/
val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
+ /** The current pattern size. */
+ val patternSize: StateFlow<Byte>
+
/**
* The currently-configured authentication method. This determines how the authentication
* challenge needs to be completed in order to unlock an otherwise locked device.
@@ -183,6 +186,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 797154e..56635de 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
@@ -127,6 +127,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
@@ -309,7 +312,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 b1a153a..8d61cef 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -126,12 +126,19 @@
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 setupLayoutTransition() {
LayoutTransition transition = new LayoutTransition();
transition.setDuration(200);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index ab23564..280ae36 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -65,6 +65,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;
@@ -451,6 +452,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/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 65668b5..f4a4988 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;
@@ -216,6 +217,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()
@@ -742,6 +746,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,
@@ -964,6 +970,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;
}
@@ -1051,6 +1115,7 @@
for (Callback cb : mCallbacks) {
cb.onFingerDown();
}
+ updateViewDimAmount();
}
private void onFingerUp(long requestId, @NonNull View view) {
@@ -1093,6 +1158,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 dae6d08..4983610 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -131,6 +131,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() */,
@@ -142,12 +144,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/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index ac4b717..1d25e2f 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
@@ -145,6 +146,7 @@
request: BiometricPromptRequest.Credential,
text: CharSequence? = null,
pattern: List<LockPatternView.Cell>? = null,
+ patternSize: Byte = LockPatternUtils.PATTERN_SIZE_DEFAULT
): CredentialStatus =
withContext(bgDispatcher) {
val credential =
@@ -154,7 +156,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 03c5c53..6402dce 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
@@ -116,8 +116,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 724c0fe..cad085f 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
@@ -86,6 +86,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 ca8268d..7a3ef1a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -62,6 +62,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;
@@ -197,6 +198,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 b1d4587..a9cc45a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -349,7 +349,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 45433e6..3713eca 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;
@@ -186,6 +187,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
@@ -222,6 +231,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;
@@ -232,6 +243,7 @@
protected MyAdapter mAdapter;
protected MyOverflowAdapter mOverflowAdapter;
protected MyPowerOptionsAdapter mPowerAdapter;
+ protected MyRestartOptionsAdapter mRestartAdapter;
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
@@ -545,6 +557,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.
@@ -579,6 +601,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) {
@@ -592,11 +633,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();
@@ -662,6 +711,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()) {
@@ -691,6 +761,7 @@
mAdapter = new MyAdapter();
mOverflowAdapter = new MyOverflowAdapter();
mPowerAdapter = new MyPowerOptionsAdapter();
+ mRestartAdapter = new MyRestartOptionsAdapter();
}
/**
@@ -715,6 +786,7 @@
this::onRefresh,
mKeyguardShowing,
mPowerAdapter,
+ mRestartAdapter,
mUiEventLogger,
mShadeController,
mKeyguardUpdateMonitor,
@@ -760,6 +832,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
@@ -820,7 +901,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;
@@ -949,7 +1030,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
@@ -961,7 +1044,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;
@@ -985,7 +1068,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);
}
}
@@ -1023,14 +1229,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;
}
}
@@ -1446,8 +1645,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.
@@ -1493,7 +1693,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));
@@ -1535,7 +1735,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.
@@ -1549,6 +1750,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.
*/
@@ -2134,6 +2347,7 @@
mAdapter.notifyDataSetChanged();
mOverflowAdapter.notifyDataSetChanged();
mPowerAdapter.notifyDataSetChanged();
+ mRestartAdapter.notifyDataSetChanged();
}
};
@@ -2216,6 +2430,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;
@@ -2228,6 +2443,7 @@
private final StatusBarWindowController mStatusBarWindowController;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
+ private Dialog mRestartOptionsDialog;
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
@@ -2309,6 +2525,7 @@
Runnable onRefreshCallback,
boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter,
+ MyRestartOptionsAdapter restartAdapter,
UiEventLogger uiEventLogger,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -2321,6 +2538,7 @@
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
mPowerOptionsAdapter = powerAdapter;
+ mRestartOptionsAdapter = restartAdapter;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mLightBarController = lightBarController;
@@ -2411,6 +2629,12 @@
mPowerOptionsDialog.show();
}
+ public void showRestartOptionsMenu() {
+ mRestartOptionsDialog = GlobalActionsPowerDialog.create(mContext,
+ mRestartOptionsAdapter);
+ mRestartOptionsDialog.show();
+ }
+
protected void showPowerOverflowMenu() {
mOverflowPopup = createPowerOverflowPopup();
mOverflowPopup.show();
@@ -2665,6 +2889,7 @@
public void dismiss() {
dismissOverflow();
dismissPowerOptions();
+ dismissRestartOptions();
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
super.dismiss();
@@ -2682,6 +2907,12 @@
}
}
+ private void dismissRestartOptions() {
+ if (mRestartOptionsDialog != null) {
+ mRestartOptionsDialog.dismiss();
+ }
+ }
+
protected final void setRotationSuggestionsEnabled(boolean enabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -2719,6 +2950,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 eee5206..4f506d3 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(
view = settingsMenu,
@@ -406,7 +411,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)
@@ -476,9 +486,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..16fec11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/leaf/LeafModule.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.DataSwitchTile
+import com.android.systemui.qs.tiles.PowerShareTile
+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 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<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 0a72a2f..6f535e8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1980,6 +1980,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 0320dec..051ad30 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;
@@ -76,6 +75,7 @@
public class NavigationBarControllerImpl implements
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener,
Dumpable, NavigationBarController {
private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
@@ -89,7 +89,7 @@
private final TaskbarDelegate mTaskbarDelegate;
private final NavBarHelper mNavBarHelper;
private int mNavMode;
- @VisibleForTesting boolean mIsLargeScreen;
+ @VisibleForTesting boolean mTaskbarShowing;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -138,16 +138,15 @@
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController, lightBarController, pipOptional,
backAnimation.orElse(null), taskStackChangeListeners);
- mIsLargeScreen = isLargeScreen(mContext);
+ overviewProxyService.addCallback(this);
dumpManager.registerDumpable(this);
}
@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/243765256): Disable this logging once b/243765256 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
@@ -196,6 +195,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,
@@ -253,8 +262,8 @@
/** @return {@code true} if taskbar is enabled, false otherwise */
private boolean initializeTaskbarIfNecessary() {
- // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
- boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification())
+ // Enable for large screens or (phone AND flag is set); assuming phone = !mTaskbarShowing
+ boolean taskbarEnabled = (shouldShowTaskbar() || enableTaskbarNavbarUnification())
&& shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
if (taskbarEnabled) {
@@ -284,7 +293,6 @@
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsLargeScreen = isLargeScreen(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -454,6 +462,10 @@
}
}
+ private boolean shouldShowTaskbar() {
+ return mTaskbarShowing;
+ }
+
@Override
public @Nullable NavigationBarView getDefaultNavigationBarView() {
return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
@@ -488,7 +500,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 e660b97..81b9690 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;
@@ -270,6 +272,8 @@
private int mRightInset;
private int mSysUiFlags;
+ private int mEdgeHeight;
+
// For Tf-Lite model.
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
private Map<String, Integer> mVocab;
@@ -470,6 +474,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;
}
@@ -589,6 +615,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 8e1b00d..86b7f59 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -123,7 +123,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)
@@ -133,4 +133,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 052c0da..f5982aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -495,6 +495,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 0644237..a057f22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,7 +18,10 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+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;
@@ -94,11 +97,19 @@
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;
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
+ // 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);
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,
@@ -106,6 +117,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));
// QSCustomizer will always be the height of the screen, but do this after
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index c908e6e..b335640 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,15 +46,20 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
+import com.android.settingslib.net.DataUsageController;
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
@@ -53,51 +69,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();
}
}
@@ -122,7 +206,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();
@@ -149,21 +233,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) {
@@ -176,16 +257,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 7f91fd2..a8ad6aa 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;
@@ -519,6 +520,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 ddd7d67..8647556 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();
}
/**
@@ -191,21 +225,28 @@
removeView(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);
}
}
@@ -330,13 +371,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;
}
@@ -372,7 +431,7 @@
updatePageIndicator();
- setBrightnessViewMargin();
+ setBrightnessViewMargin(mTop);
if (mTileLayout != null) {
mTileLayout.updateResources();
@@ -442,10 +501,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);
@@ -611,16 +680,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);
@@ -677,6 +761,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) {}
@@ -730,6 +824,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 5eb9620..7a6f4c9 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 final BrightnessMirrorHandler mBrightnessMirrorHandler;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private boolean mListening;
+ private BrightnessMirrorController mBrightnessMirrorController;
private final boolean mSceneContainerEnabled;
@@ -119,13 +119,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);
@@ -140,6 +149,7 @@
@Override
protected void onViewDetached() {
mTunerService.removeTunable(mView);
+ mView.setBrightnessRunnable(null);
mBrightnessMirrorHandler.onQsPanelDettached();
super.onViewDetached();
}
@@ -147,6 +157,7 @@
@Override
protected void onConfigurationChanged() {
mView.updateResources();
+ mView.updateColumns();
if (mView.isListening()) {
refreshAllTiles();
}
@@ -159,6 +170,12 @@
mView.setCanCollapse(!shouldUseSplitNotificationShade);
}
+ private void updateBrightnessMirror() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessSliderController.setMirrorControllerAndMirror(mBrightnessMirrorController);
+ }
+ }
+
/** */
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
@@ -181,6 +198,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 f278dce..fa1f3f9 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 a103566..705d6bb 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;
@@ -67,13 +68,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));
Toolbar toolbar = 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);
toolbar.setNavigationIcon(
- getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ getResources().getDrawable(value.resourceId, themedContext.getTheme()));
toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, com.android.internal.R.string.reset)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
@@ -93,12 +96,8 @@
}
void updateNavBackDrop(Configuration newConfig, LightBarController lightBarController) {
- View navBackdrop = findViewById(R.id.nav_bar_background);
mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600
|| newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
- if (navBackdrop != null) {
- navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE);
- }
updateNavColors(lightBarController);
}
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 529d684..cf96f20 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;
@@ -123,6 +126,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);
/**
@@ -203,6 +212,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() {
@@ -284,6 +297,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 4565200..b92aec9 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,10 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.StateListDrawable
import android.os.Trace
+import android.provider.Settings
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
@@ -50,6 +54,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.BooleanState
@@ -57,6 +62,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 +80,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 +107,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 +119,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
@@ -137,13 +154,22 @@
private lateinit var ripple: RippleDrawable
private lateinit var backgroundDrawable: LayerDrawable
private lateinit var backgroundBaseDrawable: Drawable
- private lateinit var backgroundOverlayDrawable: Drawable
+ private lateinit var backgroundOverlayDrawable: StateListDrawable
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 +183,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 +207,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 +277,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
@@ -251,19 +315,56 @@
}
}
+ 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))
@@ -272,7 +373,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))
@@ -280,12 +381,12 @@
}
fun createTileBackground(): Drawable {
- ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ ripple = mContext.getDrawable(if (isRoundQS()) R.drawable.qs_tile_background_no_mask else R.drawable.qs_tile_background)!!.mutate() 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)
+ backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_overlay) as StateListDrawable
backgroundOverlayDrawable.mutate().setTintMode(PorterDuff.Mode.SRC)
return ripple
}
@@ -318,7 +419,7 @@
}
override fun getIconWithBackground(): View {
- return icon
+ return if (isRoundQS()) iconContainer else icon
}
override fun init(tile: QSTile) {
@@ -362,14 +463,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
+ }
}
}
@@ -393,6 +506,10 @@
launchableViewDelegate.setShouldBlockVisibilityChanges(block)
}
+ override fun getAnimatedView(): LaunchableView {
+ return if (isRoundQS()) getIconWithBackground() as LaunchableView else this
+ }
+
override fun setVisibility(visibility: Int) {
launchableViewDelegate.setVisibility(visibility)
}
@@ -517,7 +634,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
}
@@ -525,13 +642,24 @@
// 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()) {
+ for (i in 0 until backgroundOverlayDrawable.getStateCount()) {
+ shapeAnimator.setFloatValues(
+ (backgroundOverlayDrawable.getStateDrawable(i) as GradientDrawable).cornerRadius,
+ getCornerRadiusForState(state.state))
+ }
+ }
singleAnimator.setValues(
colorValuesHolder(
BACKGROUND_NAME,
@@ -559,7 +687,11 @@
getOverlayColorForState(state.state)
)
)
- singleAnimator.start()
+ if (isRoundQS()) {
+ tileAnimator.start()
+ } else {
+ singleAnimator.start()
+ }
} else {
setAllColors(
getBackgroundColorForState(state.state, state.disabledByPolicy),
@@ -568,6 +700,9 @@
getChevronColorForState(state.state, state.disabledByPolicy),
getOverlayColorForState(state.state)
)
+ if (isRoundQS()) {
+ setCornerRadius(getCornerRadiusForState(state.state))
+ }
}
}
@@ -637,6 +772,28 @@
return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
}
+ private fun setCornerRadius(cornerRadius: Float) {
+ for (i in 0 until backgroundOverlayDrawable.getStateCount()) {
+ (backgroundOverlayDrawable.getStateDrawable(i) as GradientDrawable).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/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index ccf7afb..3cd52ff 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.qs.tileimpl.QSTileImpl;
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";
@@ -68,10 +69,11 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
DataSaverController dataSaverController,
- DialogLaunchAnimator dialogLaunchAnimator
+ DialogLaunchAnimator dialogLaunchAnimator,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mDataSaverController = dataSaverController;
mDialogLaunchAnimator = dialogLaunchAnimator;
mDataSaverController.observe(getLifecycle(), this);
@@ -87,7 +89,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 1b504a8..c4dee7b 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.InternetDialogFactory;
+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,
- InternetDialogFactory internetDialogFactory
+ InternetDialogFactory internetDialogFactory,
+ KeyguardStateController keyguardStateController
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ statusBarStateController, activityStarter, qsLogger, keyguardStateController);
mInternetDialogFactory = internetDialogFactory;
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(() -> mInternetDialogFactory.create(true,
mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi(), view));
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..b68ee89 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;
}
@@ -155,7 +162,7 @@
private NfcAdapter getAdapter() {
if (mAdapter == null) {
try {
- mAdapter = NfcAdapter.getDefaultAdapter(mContext);
+ mAdapter = NfcAdapter.getNfcAdapter(mContext.getApplicationContext());
} catch (UnsupportedOperationException e) {
mAdapter = null;
}
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/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/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 9d214e7..7bdc655 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.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;
@@ -115,6 +119,7 @@
private LinearLayout mSecondaryMobileNetworkLayout;
private LinearLayout mTurnWifiOnLayout;
private LinearLayout mEthernetLayout;
+ private LinearLayout mHotspotLayout;
private TextView mWifiToggleTitleText;
private LinearLayout mWifiScanNotifyLayout;
private TextView mWifiScanNotifyText;
@@ -132,7 +137,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
@@ -183,7 +194,6 @@
mCanConfigWifi = canConfigWifi;
mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
mKeyguard = keyguardStateController;
-
mUiEventLogger = uiEventLogger;
mDialogLaunchAnimator = dialogLaunchAnimator;
mAdapter = new InternetAdapter(mInternetDialogController);
@@ -218,6 +228,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);
@@ -238,12 +249,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 = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
setOnClickListener();
+ setHotspotLayout();
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
@@ -278,6 +296,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);
@@ -305,8 +326,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");
}
@@ -321,6 +344,10 @@
mInternetDialogController.isCarrierNetworkActive());
}
+ if (shouldUpdateHotspot) {
+ setHotspotLayout();
+ }
+
if (!mCanConfigWifi) {
return;
}
@@ -334,6 +361,10 @@
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
}
+ void updateDialog(boolean shouldUpdateMobileNetwork) {
+ updateDialog(shouldUpdateMobileNetwork, false /* shouldUpdateHotspot */);
+ }
+
private void setOnClickListener() {
mMobileNetworkLayout.setOnClickListener(v -> {
int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
@@ -352,6 +383,13 @@
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(
@@ -412,6 +450,8 @@
mSignalIcon.setImageDrawable(drawable);
});
});
+ mMobileConnectedSpace.setVisibility(
+ isNetworkConnected ? View.VISIBLE : View.GONE);
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
mMobileToggleDivider.setVisibility(
@@ -499,6 +539,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) {
@@ -509,8 +569,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);
@@ -645,6 +712,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;
@@ -802,6 +898,12 @@
}
@Override
+ public void onHotspotChanged() {
+ mHandler.post(() -> updateDialog(false /* shouldUpdateMobileNetwork */,
+ true /* shouldUpdateHotspot */));
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
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 592cb3b..e9aa44a 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.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
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;
@@ -193,6 +197,9 @@
private boolean mHasWifiEntries;
private WifiStateWorker mWifiStateWorker;
+ private final HotspotController mHotspotController;
+ private final NetworkPolicyManager mPolicyManager;
+
@VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@VisibleForTesting
@@ -233,6 +240,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();
}
@@ -251,6 +278,7 @@
LocationController locationController,
DialogLaunchAnimator dialogLaunchAnimator,
WifiStateWorker wifiStateWorker,
+ HotspotController hotspotController,
FeatureFlags featureFlags
) {
if (DEBUG) {
@@ -285,6 +313,8 @@
mDialogLaunchAnimator = dialogLaunchAnimator;
mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
mWifiStateWorker = wifiStateWorker;
+ mHotspotController = hotspotController;
+ mPolicyManager = NetworkPolicyManager.from(context);
mFeatureFlags = featureFlags;
}
@@ -297,6 +327,8 @@
mAccessPointController.addAccessPointCallback(this);
mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
mExecutor);
+ mHotspotController.addCallback(mHotspotCallback);
+ mPolicyManager.registerListener(mPolicyListener);
// Listen the subscription changes
mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
@@ -340,6 +372,7 @@
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
mConnectedWifiInternetMonitor.unregisterCallback();
+ mHotspotController.removeCallback(mHotspotCallback);
mCallback = null;
}
@@ -792,6 +825,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.
*
@@ -1022,6 +1067,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) {
@@ -1383,6 +1452,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/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 45917e8..6ce5c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -278,6 +278,12 @@
}
@Override
+ public void setTaskbarEnabled(boolean enabled) {
+ verifyCallerAndClearCallingIdentityPostMain("setTaskbarEnabled", () ->
+ onTaskbarEnabled(enabled));
+ }
+
+ @Override
public void notifyTaskbarStatus(boolean visible, boolean stashed) {
verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
onTaskbarStatusUpdated(visible, stashed));
@@ -881,6 +887,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);
@@ -1074,6 +1086,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/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 31c9284..5dde3c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -150,6 +150,7 @@
private OverlayActionChip mEditChip;
private OverlayActionChip mScrollChip;
private OverlayActionChip mQuickShareChip;
+ private OverlayActionChip mDeleteChip;
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
@@ -173,6 +174,7 @@
private enum PendingInteraction {
PREVIEW,
EDIT,
+ DELETE,
SHARE,
QUICK_SHARE
}
@@ -380,6 +382,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;
});
@@ -812,6 +831,10 @@
imageData.editTransition.get().bundle,
imageData.owner, true);
});
+ mDeleteChip.setPendingIntent(imageData.deleteAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_DELETE_TAPPED);
+ animateDismissal();
+ });
mScreenshotPreview.setOnClickListener(v -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED, 0, mPackageName);
prepareSharedTransition();
@@ -851,6 +874,9 @@
case EDIT:
mEditChip.callOnClick();
break;
+ case DELETE:
+ mDeleteChip.callOnClick();
+ break;
case QUICK_SHARE:
mQuickShareChip.callOnClick();
break;
@@ -893,6 +919,7 @@
mQuickShareChip.setOnClickListener(v -> {
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
+ mDeleteChip.setIsPending(false);
mQuickShareChip.setIsPending(true);
mPendingInteraction = PendingInteraction.QUICK_SHARE;
});
@@ -1080,8 +1107,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 6af9b73..a1a3070 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 = 3000;
+ 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 bc5090f..29fd7a3 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;
@@ -58,6 +59,7 @@
private Listener mListener;
private ToggleSlider mMirror;
+ private ImageView mIcon;
private BrightnessMirrorController mMirrorController;
private boolean mTracking;
private final FalsingManager mFalsingManager;
@@ -98,6 +100,7 @@
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mBrightnessSliderHapticPlugin = brightnessSliderHapticPlugin;
+ mIcon = mView.findViewById(R.id.brightness_icon);
}
/**
@@ -107,6 +110,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 2460a33..c7ddce2 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
@@ -91,6 +98,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,
@@ -101,7 +109,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]. */
@@ -137,12 +145,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) {
@@ -193,6 +204,7 @@
if (qsVisible && field != value) {
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
+ updateVisibility()
}
}
@@ -244,6 +256,8 @@
val update =
combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(visible)
header.updateAllConstraints(update)
+ privacyChipVisible = visible
+ setBatteryClickable(qsExpandedFraction == 1f || !visible)
}
}
@@ -288,6 +302,10 @@
updateCarrierGroupPadding()
clock.onDensityOrFontScaleChanged()
}
+
+ override fun onUiModeChanged() {
+ updateResources()
+ }
}
private val nextAlarmCallback =
@@ -314,6 +332,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() {
@@ -336,6 +387,7 @@
demoModeController.addCallback(demoModeReceiver)
statusBarIconController.addIconGroup(iconManager)
nextAlarmController.addCallback(nextAlarmCallback)
+ updateResources()
systemIconsHoverContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createListener(systemIconsHoverContainer)
)
@@ -504,6 +556,7 @@
header.progress = qsExpandedFraction
updateBatteryMode()
}
+ setBatteryClickable(qsExpandedFraction == 1f || !privacyChipVisible)
}
private fun logInstantEvent(message: String) {
@@ -539,6 +592,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() {
@@ -554,6 +626,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..727a187 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
@@ -152,6 +152,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;
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..a5bcfea 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
@@ -18,8 +18,11 @@
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;
@@ -58,6 +61,19 @@
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);
getCarrier1View().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 ada7d3e..d3b4a07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -314,7 +314,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) { }
@@ -968,11 +968,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();
}
}
@@ -1583,7 +1583,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/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..88da372 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) {
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 5a8b636..2201581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -480,6 +480,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 ae04eaf..65155ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,6 +34,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;
@@ -187,6 +188,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.
*/
@@ -219,6 +222,7 @@
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private GradientColors mColors;
+ private GradientColors mBehindColors;
private boolean mNeedsDrawableColorUpdate;
private float mAdditionalScrimBehindAlphaKeyguard = 0f;
@@ -317,6 +321,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();
@@ -359,6 +365,7 @@
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mWallpaperRepository = wallpaperRepository;
mMainDispatcher = mainDispatcher;
+ mBehindColors = new GradientColors();
}
@Override
@@ -394,6 +401,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()
@@ -901,8 +909,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);
@@ -944,8 +952,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) {
@@ -996,7 +1004,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,
@@ -1012,7 +1020,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.
@@ -1132,7 +1140,7 @@
&& !mBlankScreen;
mScrimInFront.setColors(mColors, animateScrimInFront);
- mScrimBehind.setColors(mColors, animateBehindScrim);
+ mScrimBehind.setColors(mBehindColors, animateBehindScrim);
mNotificationsScrim.setColors(mColors, animateScrimNotifications);
dispatchBackScrimState(mScrimBehind.getViewAlpha());
@@ -1497,6 +1505,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);
@@ -1510,6 +1520,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 e3b65ab..59d0fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -76,13 +76,13 @@
}
mFrontTint = Color.BLACK;
mBehindTint = Color.BLACK;
- mNotifTint = mClipQsScrim ? Color.BLACK : 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 */, Color.BLACK);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -121,9 +121,9 @@
BOUNCER {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
- mBehindTint = mClipQsScrim ? Color.BLACK : mSurfaceColor;
- mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
+ mBehindAlpha = mClipQsScrim ? mQSClipScrimAlpha : mDefaultScrimAlpha;
+ mBehindTint = Color.TRANSPARENT;
+ mNotifAlpha = mClipQsScrim ? mQSClipScrimAlpha : 0;
mNotifTint = Color.TRANSPARENT;
mFrontAlpha = 0f;
}
@@ -151,13 +151,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 : Color.BLACK;
+ mBehindTint = Color.TRANSPARENT;
if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -242,7 +242,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;
@@ -259,22 +259,22 @@
&& !fromAod;
mFrontTint = Color.TRANSPARENT;
- mBehindTint = Color.BLACK;
+ mBehindTint = Color.TRANSPARENT;
mBlankScreen = false;
if (mDisplayRequiresBlanking && previousState == ScrimState.AOD) {
// Set all scrims black, before they fade transparent.
updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
- updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
+ updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT /* tint */);
// Scrims should still be black at the end of the transition.
mFrontTint = Color.BLACK;
- mBehindTint = Color.BLACK;
+ mBehindTint = Color.TRANSPARENT;
mBlankScreen = true;
}
if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+ updateScrimColor(mScrimBehind, mQSClipScrimAlpha /* alpha */, Color.TRANSPARENT);
}
}
},
@@ -313,6 +313,7 @@
float mScrimBehindAlphaKeyguard;
float mDefaultScrimAlpha;
+ float mQSClipScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
@@ -422,6 +423,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 9ae4195..27348c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -16,6 +16,7 @@
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;
@@ -48,6 +49,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;
@@ -195,8 +197,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
@@ -432,6 +436,9 @@
case TYPE_MOBILE_NEW:
return addNewMobileIcon(index, slot, holder.getTag());
+
+ case TYPE_NETWORK_TRAFFIC:
+ return addNetworkTraffic(index, slot);
}
return null;
@@ -446,6 +453,12 @@
return view;
}
+ protected NetworkTrafficSB addNetworkTraffic(int index, String slot) {
+ NetworkTrafficSB view = onCreateNetworkTraffic(slot);
+ mGroup.addView(view, index, onCreateLayoutParams());
+ return view;
+ }
+
protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
mGroup.addView(view, index, onCreateLayoutParams());
@@ -486,6 +499,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);
@@ -519,8 +538,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.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 7048a78..19db585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -67,6 +67,8 @@
@Retention(RetentionPolicy.SOURCE)
@interface IconType {}
+ public static final int TYPE_NETWORK_TRAFFIC = 5;
+
private StatusBarIcon mIcon;
private @IconType int mType = TYPE_ICON;
private int mTag = 0;
@@ -126,6 +128,12 @@
return holder;
}
+ public static StatusBarIconHolder fromNetworkTraffic() {
+ StatusBarIconHolder holder = new StatusBarIconHolder();
+ holder.mType = TYPE_NETWORK_TRAFFIC;
+ return holder;
+ }
+
public @IconType int getType() {
return mType;
}
@@ -148,6 +156,8 @@
// The new pipeline controls visibilities via the view model and view binder, so
// this is effectively an unused return value.
return true;
+ case TYPE_NETWORK_TRAFFIC:
+ return true;
default:
return true;
}
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/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 30a445f..345b8f8 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());
mIconController.setIcon(mSlotVpn, vpnIconId,
@@ -146,12 +149,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/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 53b343c..1b7e7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -78,6 +78,7 @@
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
private boolean mAudioProfileOnly;
private boolean mIsActive;
+ private int mBatteryLevel;
private final H mHandler;
private int mState;
@@ -133,6 +134,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());
@@ -272,6 +274,7 @@
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
updateAudioProfile();
+ updateBattery();
}
private void updateActive() {
@@ -321,6 +324,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
@@ -482,4 +500,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 b06ebe9..9b0860f 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 3376e23..86b1dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -109,6 +109,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.
@@ -136,6 +141,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. */
@@ -242,6 +253,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 2b9ad50..872de76 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -30,6 +30,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.UiModeManager;
import android.app.WallpaperColors;
@@ -78,6 +81,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;
@@ -122,6 +127,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;
@@ -135,6 +142,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.
@@ -175,6 +183,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
@@ -414,10 +431,12 @@
WakefulnessLifecycle wakefulnessLifecycle,
JavaAdapter javaAdapter,
KeyguardTransitionInteractor keyguardTransitionInteractor,
- UiModeManager uiModeManager) {
+ UiModeManager uiModeManager,
+ ConfigurationController configurationController) {
mContext = context;
mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY);
+ mConfigurationController = configurationController;
mDeviceProvisionedController = deviceProvisionedController;
mBroadcastDispatcher = broadcastDispatcher;
mUserManager = userManager;
@@ -476,12 +495,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.
@@ -799,6 +947,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()) {
@@ -806,11 +962,6 @@
}
}
- if (colorSchemeIsApplied(managedProfiles)) {
- Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
- 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 404621d..d5e9c6a 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;
@@ -46,6 +48,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.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
@@ -97,6 +105,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;
@@ -125,6 +134,7 @@
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.res.R;
+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;
@@ -252,6 +262,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();
@@ -284,6 +296,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;
@@ -304,6 +319,17 @@
private final Lazy<SecureSettings> mSecureSettings;
private int mDialogTimeoutMillis;
+ // 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,
@@ -366,6 +392,10 @@
};
}
+ if (!mShowActiveStreamOnly) {
+ mVolumePanelOnLeft = mContext.getResources().getBoolean(R.bool.config_audioPanelOnLeftSide);;
+ }
+
initDimens();
mOrientation = mContext.getResources().getConfiguration().orientation;
@@ -456,27 +486,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) {
@@ -487,6 +546,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));
@@ -506,6 +566,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;
@@ -517,6 +581,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);
@@ -618,6 +684,9 @@
updateBackgroundForDrawerClosedAmount();
setTopContainerBackgroundDrawable();
+
+ // Rows need to be updated after mRingerAndDrawerContainerBackground is set
+ updateRowsH(getActiveRow());
}
});
}
@@ -661,6 +730,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,
@@ -714,6 +818,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;
}
@@ -726,7 +849,7 @@
}
private boolean shouldSlideInVolumeTray() {
- return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION;
+ return mContext.getDisplay().getRotation() != RotationPolicy.getNaturalRotation();
}
private boolean isLandscape() {
@@ -735,8 +858,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) {
@@ -927,6 +1049,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(),
@@ -997,15 +1125,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() {
@@ -1049,12 +1184,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);
@@ -1131,7 +1267,7 @@
.start();
} else {
mRingerDrawerContainer.animate()
- .translationX(mRingerDrawerItemSize * 2)
+ .translationX(getTranslationForPanelLocation() * mRingerDrawerItemSize * 2)
.start();
}
@@ -1149,6 +1285,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
@@ -1177,23 +1377,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();
- if (FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_VOLUME_PANEL_IN_SYSTEMUI)) {
- mVolumePanelFactory.create(true /* aboveStatusBar */, null);
- } else {
- mActivityStarter.startActivity(new Intent(Settings.Panel.ACTION_VOLUME),
- true /* dismissShade */);
- }
+ });
+ }
+
+ 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);
});
}
}
@@ -1471,6 +1685,10 @@
mConfigChanged = false;
}
+ if (mDefaultRow == null) {
+ mDefaultRow = getActiveRow();
+ }
+
initSettingsH(lockTaskModeState);
mShowing = true;
mIsAnimatingDismiss = false;
@@ -1572,9 +1790,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(
@@ -1599,6 +1824,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;
@@ -1618,10 +1850,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);
}
@@ -1637,29 +1877,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.
@@ -1667,12 +1917,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.
@@ -1680,13 +1931,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.
@@ -1695,8 +1946,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();
@@ -1999,13 +2348,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);
@@ -2205,6 +2555,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;
@@ -2213,6 +2578,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());
}
@@ -2220,7 +2588,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
@@ -2254,8 +2622,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
@@ -2304,7 +2673,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 8a531fd..05d3a17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -141,7 +141,7 @@
@Test
public void testCreateNavigationBarsIncludeDefaultTrue() {
// 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 2db79c2..a18e02c 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/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 7c5696c..fc74f11 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
@@ -55,6 +55,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> =
@@ -215,7 +218,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!")
@@ -223,7 +227,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/core/Android.bp b/services/core/Android.bp
index f5a80d8..a2e36a8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -192,7 +192,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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 39b8643..c1ff9de 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1621,6 +1621,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 89b53aa..0f3ab5d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1727,6 +1727,10 @@
static final HostingRecord sNullHostingRecord =
new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY);
+
+ final SwipeToScreenshotObserver mSwipeToScreenshotObserver;
+ private boolean mIsSwipeToScreenshotEnabled;
+
/**
* Used to notify activity lifecycle events.
*/
@@ -2536,6 +2540,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
@@ -2645,6 +2650,7 @@
mPendingStartActivityUids = new PendingStartActivityUids();
mTraceErrorLogger = new TraceErrorLogger();
mComponentAliasResolver = new ComponentAliasResolver(this);
+ mSwipeToScreenshotObserver = new SwipeToScreenshotObserver(mHandler, mContext);
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -8712,6 +8718,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));
@@ -20528,6 +20535,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();
@@ -20677,5 +20710,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/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index dafea9a..c017114 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -31,6 +31,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;
@@ -851,6 +853,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.
@@ -880,6 +886,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 032ab87..07a69d9 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
@@ -99,6 +99,8 @@
@SuppressWarnings("deprecation")
public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
+ private static final String TAG = "FingerprintProvider";
+
private boolean mTestHalEnabled;
@NonNull
@@ -248,7 +250,7 @@
}
private String getTag() {
- return "FingerprintProvider/" + mHalInstanceName;
+ return TAG + "/" + mHalInstanceName;
}
boolean hasHalInstance() {
@@ -832,7 +834,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(
@@ -853,7 +855,7 @@
}
@Nullable
- private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
+ private static SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
if (array == null) {
return null;
}
@@ -865,7 +867,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 d3cecd0..ca5e0e7 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
@@ -143,6 +143,8 @@
private final boolean mIsPowerbuttonFps;
private AidlSession mSession;
+ private boolean mCleanup;
+
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
@@ -374,6 +376,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);
@@ -1004,6 +1008,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 26332ff..b45a8b2 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
@@ -219,6 +219,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 0730c67..305fd91 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
@@ -27,6 +27,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;
@@ -107,6 +108,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);
@@ -118,6 +123,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 27b01a5..cbd5d48 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -400,6 +400,7 @@
if (mGnssVisibilityControl != null) {
mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
}
+ toggleXtraDaemon();
}
public GnssLocationProvider(Context context, GnssNative gnssNative,
@@ -512,6 +513,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);
}
@@ -667,7 +678,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));
@@ -762,6 +773,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) {
@@ -1175,7 +1191,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));
}
@@ -1219,12 +1235,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;
@@ -1663,6 +1674,7 @@
"PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured());
pw.println("native internal state: ");
pw.println(" " + mGnssNative.getInternalState());
+ pw.println("isAssistedGpsEnabled=" + isAssistedGpsEnabled());
}
}
@@ -1785,9 +1797,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();
}
@@ -1844,4 +1864,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 0c2eee5..96a896c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1659,6 +1659,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.
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 6d123cc..a9a18f6 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -367,6 +367,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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a0e69cf..11b90ac 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -630,6 +630,8 @@
protected boolean mInCallStateOffHook = false;
boolean mNotificationPulseEnabled;
+ private boolean mSoundVibScreenOn;
+
private Uri mInCallNotificationUri;
private AudioAttributes mInCallNotificationAudioAttributes;
private float mInCallNotificationVolume;
@@ -2057,6 +2059,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);
@@ -2083,10 +2087,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);
}
@@ -2144,6 +2148,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;
+ }
}
}
@@ -8785,7 +8794,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();
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3cb2420..9929eda 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1463,6 +1463,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;
@@ -1496,13 +1519,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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bc441b8..71c7f4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3171,15 +3171,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,
@@ -6689,16 +6698,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 938ed23..d9627ae 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -217,6 +217,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();
}
@@ -1788,10 +1795,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.
@@ -2273,6 +2284,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();
@@ -2785,6 +2802,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.
@@ -2862,6 +2891,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 03a7bd3..3172e1a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -253,6 +253,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 ec5172f..9328e58 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3924,7 +3924,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(
@@ -3951,7 +3951,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);
}
@@ -6547,7 +6551,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);
}
@@ -6567,7 +6571,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);
}
@@ -6586,7 +6612,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 871e98b..c919dcb 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;
@@ -339,7 +362,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.
@@ -370,7 +393,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 b271a03..667ab82 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -137,7 +137,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 b21721a..a9c41ba 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -625,14 +625,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) {}
}
@@ -1600,11 +1600,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 {
@@ -1614,7 +1611,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/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 869bcc0..1977fe6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -285,6 +285,8 @@
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;
@@ -1980,7 +1982,9 @@
@Override
public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
- enforceTaskPermission("getFocusedRootTaskInfo()");
+ if (!AttestationHooks.shouldBypassTaskPermission(mContext)) {
+ enforceTaskPermission("getFocusedRootTaskInfo()");
+ }
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3099,7 +3103,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);
}
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 2125c63..367fb3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3609,6 +3609,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 59e95e7..268023e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -137,6 +137,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;
@@ -232,6 +233,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;
@@ -2633,6 +2635,10 @@
t.traceBegin("StartBackgroundInstallControlService");
mSystemServiceManager.startService(BackgroundInstallControlService.class);
t.traceEnd();
+
+ t.traceBegin("StartHealthService");
+ mSystemServiceManager.startService(HealthInterfaceService.class);
+ t.traceEnd();
}
t.traceBegin("StartMediaProjectionManager");
@@ -3275,6 +3281,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 f5d50d1..3733a57 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) {