Merge "Fix TextAppearanceInfo#getTextColor is wrong when there is a hint text" into udc-qpr-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 175c8d1..07958dd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -3153,7 +3153,8 @@
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS;
- private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 45 * MINUTE_IN_MILLIS;
+ // TODO(267949143): set a different limit for headless system apps
+ private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d7222d2..863efff 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,6 +30,8 @@
#include <binder/ProcessState.h>
+#include <ftl/concat.h>
+#include <ftl/optional.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
@@ -45,14 +47,7 @@
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname, std::optional<PhysicalDisplayId> displayId)
-{
- std::string defaultDisplayStr = "";
- if (!displayId) {
- defaultDisplayStr = "";
- } else {
- defaultDisplayStr = " (default: " + to_string(*displayId) + ")";
- }
+void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
@@ -61,7 +56,13 @@
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
- pname, defaultDisplayStr.c_str());
+ pname,
+ displayIdOpt
+ .transform([](DisplayId id) {
+ return std::string(ftl::Concat(" (default: ", id.value, ')').str());
+ })
+ .value_or(std::string())
+ .c_str());
}
static int32_t flinger2bitmapFormat(PixelFormat f)
@@ -132,7 +133,7 @@
fprintf(stderr, "Failed to get ID for any displays.\n");
return 1;
}
- std::optional<PhysicalDisplayId> displayId;
+ std::optional<DisplayId> displayIdOpt;
const char* pname = argv[0];
bool png = false;
int c;
@@ -142,8 +143,8 @@
png = true;
break;
case 'd':
- displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg));
- if (!displayId) {
+ displayIdOpt = DisplayId::fromValue(atoll(optarg));
+ if (!displayIdOpt) {
fprintf(stderr, "Invalid display ID: %s\n", optarg);
return 1;
}
@@ -151,15 +152,15 @@
case '?':
case 'h':
if (ids.size() == 1) {
- displayId = ids.front();
- }
- usage(pname, displayId);
+ displayIdOpt = ids.front();
+ }
+ usage(pname, displayIdOpt);
return 1;
}
}
- if (!displayId) { // no diplsay id is specified
- displayId = ids.front();
+ if (!displayIdOpt) {
+ displayIdOpt = ids.front();
if (ids.size() > 1) {
fprintf(stderr,
"[Warning] Multiple displays were found, but no display id was specified! "
@@ -191,7 +192,7 @@
}
if (fd == -1) {
- usage(pname, displayId);
+ usage(pname, displayIdOpt);
return 1;
}
@@ -208,7 +209,7 @@
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
+ status_t result = ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 02b14ad..f8f4cc3 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -510,6 +510,17 @@
],
}
+// common protolog sources without classes that rely on Android SDK
+filegroup {
+ name: "protolog-common-no-android-src",
+ srcs: [
+ ":protolog-common-src",
+ ],
+ exclude_srcs: [
+ "com/android/internal/protolog/common/ProtoLog.java",
+ ],
+}
+
java_library {
name: "protolog-lib",
platform_apis: true,
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index 856bde8..36e0529 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -76,6 +76,20 @@
public final boolean disableFlushForViewTreeAppearing;
/**
+ * Is the content capture receiver enabled.
+ *
+ * @hide
+ */
+ public final boolean enableReceiver;
+
+ /**
+ * Options for the content protection flow.
+ *
+ * @hide
+ */
+ @NonNull public final ContentProtectionOptions contentProtectionOptions;
+
+ /**
* List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted
* for all acitivites in the package).
*/
@@ -94,52 +108,99 @@
* for contexts belonging to the content capture service app.
*/
public ContentCaptureOptions(int loggingLevel) {
- this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0,
- /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0,
- /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false,
+ this(
+ /* lite= */ true,
+ loggingLevel,
+ /* maxBufferSize= */ 0,
+ /* idleFlushingFrequencyMs= */ 0,
+ /* textChangeFlushingFrequencyMs= */ 0,
+ /* logHistorySize= */ 0,
+ /* disableFlushForViewTreeAppearing= */ false,
+ /* enableReceiver= */ false,
+ new ContentProtectionOptions(
+ /* enableReceiver= */ false,
+ /* bufferSize= */ 0),
/* whitelistedComponents= */ null);
}
- /**
- * Default constructor.
- */
- public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
- int textChangeFlushingFrequencyMs, int logHistorySize,
- @SuppressLint({"ConcreteCollection", "NullableCollection"})
- @Nullable ArraySet<ComponentName> whitelistedComponents) {
- this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
- textChangeFlushingFrequencyMs, logHistorySize,
+ /** Default constructor. */
+ public ContentCaptureOptions(
+ int loggingLevel,
+ int maxBufferSize,
+ int idleFlushingFrequencyMs,
+ int textChangeFlushingFrequencyMs,
+ int logHistorySize,
+ @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable
+ ArraySet<ComponentName> whitelistedComponents) {
+ this(
+ /* lite= */ false,
+ loggingLevel,
+ maxBufferSize,
+ idleFlushingFrequencyMs,
+ textChangeFlushingFrequencyMs,
+ logHistorySize,
ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER,
+ new ContentProtectionOptions(
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE),
whitelistedComponents);
}
/** @hide */
- public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
- int textChangeFlushingFrequencyMs, int logHistorySize,
+ public ContentCaptureOptions(
+ int loggingLevel,
+ int maxBufferSize,
+ int idleFlushingFrequencyMs,
+ int textChangeFlushingFrequencyMs,
+ int logHistorySize,
boolean disableFlushForViewTreeAppearing,
- @SuppressLint({"ConcreteCollection", "NullableCollection"})
- @Nullable ArraySet<ComponentName> whitelistedComponents) {
- this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
- textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing,
+ boolean enableReceiver,
+ @NonNull ContentProtectionOptions contentProtectionOptions,
+ @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable
+ ArraySet<ComponentName> whitelistedComponents) {
+ this(
+ /* lite= */ false,
+ loggingLevel,
+ maxBufferSize,
+ idleFlushingFrequencyMs,
+ textChangeFlushingFrequencyMs,
+ logHistorySize,
+ disableFlushForViewTreeAppearing,
+ enableReceiver,
+ contentProtectionOptions,
whitelistedComponents);
}
/** @hide */
@VisibleForTesting
public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) {
- this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE,
+ this(
+ ContentCaptureManager.LOGGING_LEVEL_VERBOSE,
ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE,
ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS,
ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS,
ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE,
ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER,
+ new ContentProtectionOptions(
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE),
whitelistedComponents);
}
- private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize,
- int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize,
+ private ContentCaptureOptions(
+ boolean lite,
+ int loggingLevel,
+ int maxBufferSize,
+ int idleFlushingFrequencyMs,
+ int textChangeFlushingFrequencyMs,
+ int logHistorySize,
boolean disableFlushForViewTreeAppearing,
- @Nullable ArraySet<ComponentName> whitelistedComponents) {
+ boolean enableReceiver,
+ @NonNull ContentProtectionOptions contentProtectionOptions,
+ @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable
+ ArraySet<ComponentName> whitelistedComponents) {
this.lite = lite;
this.loggingLevel = loggingLevel;
this.maxBufferSize = maxBufferSize;
@@ -147,6 +208,8 @@
this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs;
this.logHistorySize = logHistorySize;
this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing;
+ this.enableReceiver = enableReceiver;
+ this.contentProtectionOptions = contentProtectionOptions;
this.whitelistedComponents = whitelistedComponents;
}
@@ -191,12 +254,22 @@
return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]";
}
final StringBuilder string = new StringBuilder("ContentCaptureOptions [");
- string.append("loggingLevel=").append(loggingLevel)
- .append(", maxBufferSize=").append(maxBufferSize)
- .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs)
- .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs)
- .append(", logHistorySize=").append(logHistorySize)
- .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing);
+ string.append("loggingLevel=")
+ .append(loggingLevel)
+ .append(", maxBufferSize=")
+ .append(maxBufferSize)
+ .append(", idleFlushingFrequencyMs=")
+ .append(idleFlushingFrequencyMs)
+ .append(", textChangeFlushingFrequencyMs=")
+ .append(textChangeFlushingFrequencyMs)
+ .append(", logHistorySize=")
+ .append(logHistorySize)
+ .append(", disableFlushForViewTreeAppearing=")
+ .append(disableFlushForViewTreeAppearing)
+ .append(", enableReceiver=")
+ .append(enableReceiver)
+ .append(", contentProtectionOptions=")
+ .append(contentProtectionOptions);
if (whitelistedComponents != null) {
string.append(", whitelisted=").append(whitelistedComponents);
}
@@ -210,11 +283,21 @@
pw.print(", lite");
return;
}
- pw.print(", bufferSize="); pw.print(maxBufferSize);
- pw.print(", idle="); pw.print(idleFlushingFrequencyMs);
- pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs);
- pw.print(", logSize="); pw.print(logHistorySize);
- pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing);
+ pw.print(", bufferSize=");
+ pw.print(maxBufferSize);
+ pw.print(", idle=");
+ pw.print(idleFlushingFrequencyMs);
+ pw.print(", textIdle=");
+ pw.print(textChangeFlushingFrequencyMs);
+ pw.print(", logSize=");
+ pw.print(logHistorySize);
+ pw.print(", disableFlushForViewTreeAppearing=");
+ pw.print(disableFlushForViewTreeAppearing);
+ pw.print(", enableReceiver=");
+ pw.print(enableReceiver);
+ pw.print(", contentProtectionOptions=[");
+ contentProtectionOptions.dumpShort(pw);
+ pw.print("]");
if (whitelistedComponents != null) {
pw.print(", whitelisted="); pw.print(whitelistedComponents);
}
@@ -236,6 +319,8 @@
parcel.writeInt(textChangeFlushingFrequencyMs);
parcel.writeInt(logHistorySize);
parcel.writeBoolean(disableFlushForViewTreeAppearing);
+ parcel.writeBoolean(enableReceiver);
+ contentProtectionOptions.writeToParcel(parcel);
parcel.writeArraySet(whitelistedComponents);
}
@@ -254,12 +339,22 @@
final int textChangeFlushingFrequencyMs = parcel.readInt();
final int logHistorySize = parcel.readInt();
final boolean disableFlushForViewTreeAppearing = parcel.readBoolean();
+ final boolean enableReceiver = parcel.readBoolean();
+ final ContentProtectionOptions contentProtectionOptions =
+ ContentProtectionOptions.createFromParcel(parcel);
@SuppressWarnings("unchecked")
final ArraySet<ComponentName> whitelistedComponents =
(ArraySet<ComponentName>) parcel.readArraySet(null);
- return new ContentCaptureOptions(loggingLevel, maxBufferSize,
- idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize,
- disableFlushForViewTreeAppearing, whitelistedComponents);
+ return new ContentCaptureOptions(
+ loggingLevel,
+ maxBufferSize,
+ idleFlushingFrequencyMs,
+ textChangeFlushingFrequencyMs,
+ logHistorySize,
+ disableFlushForViewTreeAppearing,
+ enableReceiver,
+ contentProtectionOptions,
+ whitelistedComponents);
}
@Override
@@ -267,4 +362,62 @@
return new ContentCaptureOptions[size];
}
};
+
+ /**
+ * Content protection options for a given package.
+ *
+ * <p>Does not implement {@code Parcelable} since it is an inner class without a matching AIDL.
+ *
+ * @hide
+ */
+ public static class ContentProtectionOptions {
+
+ /**
+ * Is the content protection receiver enabled.
+ *
+ * @hide
+ */
+ public final boolean enableReceiver;
+
+ /**
+ * Size of the in-memory ring buffer for the content protection flow.
+ *
+ * @hide
+ */
+ public final int bufferSize;
+
+ public ContentProtectionOptions(boolean enableReceiver, int bufferSize) {
+ this.enableReceiver = enableReceiver;
+ this.bufferSize = bufferSize;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder("ContentProtectionOptions [");
+ stringBuilder
+ .append("enableReceiver=")
+ .append(enableReceiver)
+ .append(", bufferSize=")
+ .append(bufferSize);
+ return stringBuilder.append(']').toString();
+ }
+
+ private void dumpShort(@NonNull PrintWriter pw) {
+ pw.print("enableReceiver=");
+ pw.print(enableReceiver);
+ pw.print(", bufferSize=");
+ pw.print(bufferSize);
+ }
+
+ private void writeToParcel(Parcel parcel) {
+ parcel.writeBoolean(enableReceiver);
+ parcel.writeInt(bufferSize);
+ }
+
+ private static ContentProtectionOptions createFromParcel(Parcel parcel) {
+ boolean enableReceiver = parcel.readBoolean();
+ int bufferSize = parcel.readInt();
+ return new ContentProtectionOptions(enableReceiver, bufferSize);
+ }
+ }
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 7f0a666..4e086c2 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1391,7 +1391,7 @@
Trace.endSection();
}
- if (redrawNeeded) {
+ if (redrawNeeded || sizeChanged) {
Trace.beginSection("WPMS.Engine.onSurfaceRedrawNeeded");
onSurfaceRedrawNeeded(mSurfaceHolder);
Trace.endSection();
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 668351b..c37e311 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -352,6 +352,30 @@
public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING =
"disable_flush_for_view_tree_appearing";
+ /**
+ * Enables the content protection receiver.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER =
+ "enable_content_protection_receiver";
+
+ /**
+ * Sets the size of the app blocklist for the content protection flow.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE =
+ "content_protection_apps_blocklist_size";
+
+ /**
+ * Sets the size of the in-memory ring buffer for the content protection flow.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE =
+ "content_protection_buffer_size";
+
/** @hide */
@TestApi
public static final int LOGGING_LEVEL_OFF = 0;
@@ -384,6 +408,14 @@
public static final int DEFAULT_LOG_HISTORY_SIZE = 10;
/** @hide */
public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false;
+ /** @hide */
+ public static final boolean DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER = true;
+ /** @hide */
+ public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false;
+ /** @hide */
+ public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000;
+ /** @hide */
+ public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150;
private final Object mLock = new Object();
@@ -425,11 +457,11 @@
/** @hide */
static class StrippedContext {
- final String mPackageName;
- final String mContext;
+ @NonNull final String mPackageName;
+ @NonNull final String mContext;
final @UserIdInt int mUserId;
- private StrippedContext(Context context) {
+ private StrippedContext(@NonNull Context context) {
mPackageName = context.getPackageName();
mContext = context.toString();
mUserId = context.getUserId();
@@ -440,6 +472,7 @@
return mContext;
}
+ @NonNull
public String getPackageName() {
return mPackageName;
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionUtils.java b/core/java/android/view/contentprotection/ContentProtectionUtils.java
new file mode 100644
index 0000000..9abf6f1
--- /dev/null
+++ b/core/java/android/view/contentprotection/ContentProtectionUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.view.contentprotection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ViewNode;
+
+/**
+ * Utilities for reading data from {@link ContentCaptureEvent} and {@link ViewNode}.
+ *
+ * @hide
+ */
+public final class ContentProtectionUtils {
+
+ /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */
+ @Nullable
+ public static String getEventText(@NonNull ContentCaptureEvent event) {
+ CharSequence text = event.getText();
+ if (text == null) {
+ return null;
+ }
+ return text.toString();
+ }
+
+ /** Returns the text extracted from the event's {@link ViewNode}, if set. */
+ @Nullable
+ public static String getViewNodeText(@NonNull ContentCaptureEvent event) {
+ ViewNode viewNode = event.getViewNode();
+ if (viewNode == null) {
+ return null;
+ }
+ return getViewNodeText(viewNode);
+ }
+
+ /** Returns the text extracted directly from the {@link ViewNode}, if set. */
+ @Nullable
+ public static String getViewNodeText(@NonNull ViewNode viewNode) {
+ CharSequence text = viewNode.getText();
+ if (text == null) {
+ return null;
+ }
+ return text.toString();
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 41ef44e..40b060a 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -4361,15 +4361,14 @@
* @param icProto {@link InputConnection} call data in proto format.
* @hide
*/
- @GuardedBy("mH")
public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) {
- if (!isImeSessionAvailableLocked()) {
- return;
- }
-
- proto.write(DISPLAY_ID, mDisplayId);
- final long token = proto.start(INPUT_METHOD_MANAGER);
synchronized (mH) {
+ if (!isImeSessionAvailableLocked()) {
+ return;
+ }
+
+ proto.write(DISPLAY_ID, mDisplayId);
+ final long token = proto.start(INPUT_METHOD_MANAGER);
proto.write(CUR_ID, mCurBindState.mImeId);
proto.write(FULLSCREEN_MODE, mFullscreenMode);
proto.write(ACTIVE, mActive);
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 52e17ca..f40874b 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -43,6 +43,7 @@
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -183,7 +184,7 @@
// We consider nearly matched dimensions as there can be rounding errors and the user
// won't notice very minute differences from scaling one dimension more than the other
- final boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
+ boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
// Keep a reference to it such that it doesn't get destroyed when finalized.
SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -199,8 +200,20 @@
// still hidden.
mTransaction.show(childSurfaceControl);
if (aspectRatioMismatch) {
- // Clip off ugly navigation bar.
- final Rect crop = calculateSnapshotCrop();
+ Rect crop = null;
+ final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
+ if (letterboxInsets.left != 0 || letterboxInsets.top != 0
+ || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
+ // Clip off letterbox.
+ crop = calculateSnapshotCrop(letterboxInsets);
+ // If the snapshot can cover the frame, then no need to draw background.
+ aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop);
+ }
+ // if letterbox doesn't match window frame, try crop by content insets
+ if (aspectRatioMismatch) {
+ // Clip off ugly navigation bar.
+ crop = calculateSnapshotCrop(mSnapshot.getContentInsets());
+ }
frame = calculateSnapshotFrame(crop);
mTransaction.setWindowCrop(childSurfaceControl, crop);
mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
@@ -242,14 +255,13 @@
/**
* Calculates the snapshot crop in snapshot coordinate space.
- *
+ * @param insets Content insets or Letterbox insets
* @return crop rect in snapshot coordinate space.
*/
- Rect calculateSnapshotCrop() {
+ Rect calculateSnapshotCrop(@NonNull Rect insets) {
final Rect rect = new Rect();
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
- final Rect insets = mSnapshot.getContentInsets();
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
@@ -334,6 +346,15 @@
- ((float) frame.width() / frame.height())) <= 0.01f;
}
+ private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
+ if (frame1.isEmpty() || frame2.isEmpty()) {
+ return false;
+ }
+ return Math.abs(
+ ((float) frame2.width() / frame2.height())
+ - ((float) frame1.width() / frame1.height())) <= 0.01f;
+ }
+
/**
* Get or create a TaskDescription from a RunningTaskInfo.
*/
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 22b2ec0..421d998 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -31,6 +31,8 @@
import android.view.IWindow;
import android.view.IWindowSession;
+import androidx.annotation.VisibleForTesting;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -66,7 +68,9 @@
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
/** Holds all callbacks by priorities. */
- private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
+
+ @VisibleForTesting
+ public final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
mOnBackInvokedCallbacks = new TreeMap<>();
private Checker mChecker;
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 86c2893..3d95dd3 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -80,7 +80,7 @@
/** Gating the logging of DND state change events. */
public static final Flag LOG_DND_STATE_EVENTS =
- devFlag("persist.sysui.notification.log_dnd_state_events");
+ releasedFlag("persist.sysui.notification.log_dnd_state_events");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java
index 93765cd..8870096 100644
--- a/core/java/com/android/internal/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/ProtoLog.java
@@ -16,6 +16,8 @@
package com.android.internal.protolog.common;
+import android.util.Log;
+
/**
* ProtoLog API - exposes static logging methods. Usage of this API is similar
* to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
@@ -53,6 +55,9 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.d(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -68,6 +73,9 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.v(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -83,6 +91,9 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.i(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -98,6 +109,9 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.w(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -113,6 +127,9 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.e(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -128,5 +145,8 @@
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.wtf(group.getTag(), String.format(messageString, args));
+ }
}
}
diff --git a/core/res/res/layout/language_picker_section_header.xml b/core/res/res/layout/language_picker_section_header.xml
index 58042f9..54ac677 100644
--- a/core/res/res/layout/language_picker_section_header.xml
+++ b/core/res/res/layout/language_picker_section_header.xml
@@ -25,4 +25,5 @@
android:textColor="?android:attr/colorAccent"
android:textStyle="bold"
android:id="@+id/language_picker_header"
- tools:text="@string/language_picker_section_all"/>
+ tools:text="@string/language_picker_section_all"
+ android:scrollbars="none"/>
diff --git a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
index c6f4fa2..f8348d2 100644
--- a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
+++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Parcel;
import android.util.ArraySet;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
@@ -40,16 +41,29 @@
@RunWith(MockitoJUnitRunner.class)
public class ContentCaptureOptionsTest {
- private final ComponentName mContextComponent = new ComponentName("marco", "polo");
- private final ComponentName mComp1 = new ComponentName("comp", "one");
- private final ComponentName mComp2 = new ComponentName("two", "comp");
+ private static final ComponentName CONTEXT_COMPONENT = new ComponentName("marco", "polo");
+ private static final ComponentName COMPONENT1 = new ComponentName("comp", "one");
+ private static final ComponentName COMPONENT2 = new ComponentName("two", "comp");
+ private static final ContentCaptureOptions CONTENT_CAPTURE_OPTIONS =
+ new ContentCaptureOptions(
+ /* loggingLevel= */ 1000,
+ /* maxBufferSize= */ 1001,
+ /* idleFlushingFrequencyMs= */ 1002,
+ /* textChangeFlushingFrequencyMs= */ 1003,
+ /* logHistorySize= */ 1004,
+ /* disableFlushForViewTreeAppearing= */ true,
+ /* enableReceiver= */ false,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ /* bufferSize= */ 2001),
+ /* whitelistedComponents= */ toSet(COMPONENT1, COMPONENT2));
@Mock private Context mContext;
@Mock private ContentCaptureClient mClient;
@Before
public void setExpectation() {
- when(mClient.contentCaptureClientGetComponentName()).thenReturn(mContextComponent);
+ when(mClient.contentCaptureClientGetComponentName()).thenReturn(CONTEXT_COMPONENT);
when(mContext.getContentCaptureClient()).thenReturn(mClient);
}
@@ -67,26 +81,27 @@
@Test
public void testIsWhitelisted_notWhitelisted() {
- ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mComp2));
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(COMPONENT1, COMPONENT2));
assertThat(options.isWhitelisted(mContext)).isFalse();
}
@Test
public void testIsWhitelisted_whitelisted() {
- ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mContextComponent));
+ ContentCaptureOptions options =
+ new ContentCaptureOptions(toSet(COMPONENT1, CONTEXT_COMPONENT));
assertThat(options.isWhitelisted(mContext)).isTrue();
}
@Test
public void testIsWhitelisted_invalidContext() {
- ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent));
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT));
Context invalidContext = mock(Context.class); // has no client
assertThat(options.isWhitelisted(invalidContext)).isFalse();
}
@Test
public void testIsWhitelisted_clientWithNullComponentName() {
- ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent));
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT));
ContentCaptureClient client = mock(ContentCaptureClient.class);
Context context = mock(Context.class);
when(context.getContentCaptureClient()).thenReturn(client);
@@ -94,8 +109,69 @@
assertThat(options.isWhitelisted(context)).isFalse();
}
+ @Test
+ public void testToString() {
+ String actual = CONTENT_CAPTURE_OPTIONS.toString();
+
+ String expected =
+ new StringBuilder("ContentCaptureOptions [")
+ .append("loggingLevel=")
+ .append(CONTENT_CAPTURE_OPTIONS.loggingLevel)
+ .append(", maxBufferSize=")
+ .append(CONTENT_CAPTURE_OPTIONS.maxBufferSize)
+ .append(", idleFlushingFrequencyMs=")
+ .append(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs)
+ .append(", textChangeFlushingFrequencyMs=")
+ .append(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs)
+ .append(", logHistorySize=")
+ .append(CONTENT_CAPTURE_OPTIONS.logHistorySize)
+ .append(", disableFlushForViewTreeAppearing=")
+ .append(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing)
+ .append(", enableReceiver=")
+ .append(CONTENT_CAPTURE_OPTIONS.enableReceiver)
+ .append(", contentProtectionOptions=ContentProtectionOptions [")
+ .append("enableReceiver=")
+ .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver)
+ .append(", bufferSize=")
+ .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize)
+ .append("], whitelisted=")
+ .append(CONTENT_CAPTURE_OPTIONS.whitelistedComponents)
+ .append(']')
+ .toString();
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testParcelSerializationDeserialization() {
+ Parcel parcel = Parcel.obtain();
+ CONTENT_CAPTURE_OPTIONS.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ ContentCaptureOptions actual = ContentCaptureOptions.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actual).isNotNull();
+ assertThat(actual.loggingLevel).isEqualTo(CONTENT_CAPTURE_OPTIONS.loggingLevel);
+ assertThat(actual.maxBufferSize).isEqualTo(CONTENT_CAPTURE_OPTIONS.maxBufferSize);
+ assertThat(actual.idleFlushingFrequencyMs)
+ .isEqualTo(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs);
+ assertThat(actual.textChangeFlushingFrequencyMs)
+ .isEqualTo(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs);
+ assertThat(actual.logHistorySize).isEqualTo(CONTENT_CAPTURE_OPTIONS.logHistorySize);
+ assertThat(actual.disableFlushForViewTreeAppearing)
+ .isEqualTo(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing);
+ assertThat(actual.enableReceiver).isEqualTo(CONTENT_CAPTURE_OPTIONS.enableReceiver);
+ assertThat(actual.contentProtectionOptions).isNotNull();
+ assertThat(actual.contentProtectionOptions.enableReceiver)
+ .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver);
+ assertThat(actual.contentProtectionOptions.bufferSize)
+ .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize);
+ assertThat(actual.whitelistedComponents)
+ .containsExactlyElementsIn(CONTENT_CAPTURE_OPTIONS.whitelistedComponents);
+ }
+
@NonNull
- private ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) {
+ private static ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) {
ArraySet<ComponentName> set = new ArraySet<>();
if (comps != null) {
for (int i = 0; i < comps.length; i++) {
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
new file mode 100644
index 0000000..1459799
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013 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 android.view.contentprotection;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.View;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ViewNode;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link ContentProtectionUtils}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksCoreTests:android.view.contentprotection.ContentProtectionUtilsTest}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ContentProtectionUtilsTest {
+
+ private static final String TEXT = "TEST_TEXT";
+
+ private static final ContentCaptureEvent EVENT = createEvent();
+
+ private static final ViewNode VIEW_NODE = new ViewNode();
+
+ private static final ViewNode VIEW_NODE_WITH_TEXT = createViewNodeWithText();
+
+ @Test
+ public void event_getEventText_null() {
+ String actual = ContentProtectionUtils.getEventText(EVENT);
+
+ assertThat(actual).isNull();
+ }
+
+ @Test
+ public void event_getEventText_notNull() {
+ ContentCaptureEvent event = createEvent();
+ event.setText(TEXT);
+
+ String actual = ContentProtectionUtils.getEventText(event);
+
+ assertThat(actual).isEqualTo(TEXT);
+ }
+
+ @Test
+ public void event_getViewNodeText_null() {
+ String actual = ContentProtectionUtils.getViewNodeText(EVENT);
+
+ assertThat(actual).isNull();
+ }
+
+ @Test
+ public void event_getViewNodeText_notNull() {
+ ContentCaptureEvent event = createEvent();
+ event.setViewNode(VIEW_NODE_WITH_TEXT);
+
+ String actual = ContentProtectionUtils.getViewNodeText(event);
+
+ assertThat(actual).isEqualTo(TEXT);
+ }
+
+ @Test
+ public void viewNode_getViewNodeText_null() {
+ String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE);
+
+ assertThat(actual).isNull();
+ }
+
+ @Test
+ public void viewNode_getViewNodeText_notNull() {
+ String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE_WITH_TEXT);
+
+ assertThat(actual).isEqualTo(TEXT);
+ }
+
+ private static ContentCaptureEvent createEvent() {
+ return new ContentCaptureEvent(/* sessionId= */ 123, TYPE_SESSION_STARTED);
+ }
+
+ private static ViewNode createViewNodeWithText() {
+ View view = new View(ApplicationProvider.getApplicationContext());
+ ViewStructureImpl viewStructure = new ViewStructureImpl(view);
+ viewStructure.setText(TEXT);
+ return viewStructure.getNode();
+ }
+}
diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
index 6764ac8..0361546 100644
--- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
+++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
@@ -158,32 +158,42 @@
@Test
public void testCalculateSnapshotCrop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
+ final Rect contentInsets = new Rect(0, 10, 0, 10);
+ setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+ assertEquals(new Rect(0, 0, 100, 90),
+ mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
public void testCalculateSnapshotCrop_taskNotOnTop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 50, 100, 150));
- assertEquals(new Rect(0, 10, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
+ final Rect contentInsets = new Rect(0, 10, 0, 10);
+ setupSurface(100, 100, contentInsets, 0, new Rect(0, 50, 100, 150));
+ assertEquals(new Rect(0, 10, 100, 90),
+ mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
public void testCalculateSnapshotCrop_navBarLeft() {
- setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(10, 0, 100, 100), mSnapshotSurface.calculateSnapshotCrop());
+ final Rect contentInsets = new Rect(0, 10, 0, 0);
+ setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+ assertEquals(new Rect(10, 0, 100, 100),
+ mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
public void testCalculateSnapshotCrop_navBarRight() {
- setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 90, 100), mSnapshotSurface.calculateSnapshotCrop());
+ final Rect contentInsets = new Rect(0, 10, 10, 0);
+ setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+ assertEquals(new Rect(0, 0, 90, 100),
+ mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
public void testCalculateSnapshotCrop_waterfall() {
- setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(5, 0, 95, 90), mSnapshotSurface.calculateSnapshotCrop());
+ final Rect contentInsets = new Rect(5, 10, 5, 10);
+ setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+ assertEquals(new Rect(5, 0, 95, 90),
+ mSnapshotSurface.calculateSnapshotCrop(contentInsets));
}
@Test
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 8e772a2..2ef2d3a 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -16,12 +16,15 @@
package android.window;
+import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
+import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -45,6 +48,9 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Tests for {@link WindowOnBackInvokedDispatcherTest}
*
@@ -69,6 +75,8 @@
@Mock
private ApplicationInfo mApplicationInfo;
+ private int mCallbackInfoCalls = 0;
+
private final BackMotionEvent mBackEvent = new BackMotionEvent(
/* touchX = */ 0,
/* touchY = */ 0,
@@ -93,105 +101,243 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+ private List<OnBackInvokedCallbackInfo> captureCallbackInfo() throws RemoteException {
+ ArgumentCaptor<OnBackInvokedCallbackInfo> captor = ArgumentCaptor
+ .forClass(OnBackInvokedCallbackInfo.class);
+ // atLeast(0) -> get all setOnBackInvokedCallbackInfo() invocations
+ verify(mWindowSession, atLeast(0))
+ .setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture());
+ verifyNoMoreInteractions(mWindowSession);
+ return captor.getAllValues();
+ }
+
+ private OnBackInvokedCallbackInfo assertSetCallbackInfo() throws RemoteException {
+ List<OnBackInvokedCallbackInfo> callbackInfos = captureCallbackInfo();
+ int actual = callbackInfos.size();
+ assertEquals("setOnBackInvokedCallbackInfo", ++mCallbackInfoCalls, actual);
+ return callbackInfos.get(mCallbackInfoCalls - 1);
+ }
+
+ private void assertNoSetCallbackInfo() throws RemoteException {
+ List<OnBackInvokedCallbackInfo> callbackInfos = captureCallbackInfo();
+ int actual = callbackInfos.size();
+ assertEquals("No setOnBackInvokedCallbackInfo", mCallbackInfoCalls, actual);
+ }
+
+ private void assertCallbacksSize(int expectedDefault, int expectedOverlay) {
+ ArrayList<OnBackInvokedCallback> callbacksDefault = mDispatcher
+ .mOnBackInvokedCallbacks.get(PRIORITY_DEFAULT);
+ int actualSizeDefault = callbacksDefault != null ? callbacksDefault.size() : 0;
+ assertEquals("mOnBackInvokedCallbacks DEFAULT size", expectedDefault, actualSizeDefault);
+
+ ArrayList<OnBackInvokedCallback> callbacksOverlay = mDispatcher
+ .mOnBackInvokedCallbacks.get(PRIORITY_OVERLAY);
+ int actualSizeOverlay = callbacksOverlay != null ? callbacksOverlay.size() : 0;
+ assertEquals("mOnBackInvokedCallbacks OVERLAY size", expectedOverlay, actualSizeOverlay);
+ }
+
+ private void assertTopCallback(OnBackInvokedCallback expectedCallback) {
+ assertEquals("topCallback", expectedCallback, mDispatcher.getTopCallback());
+ }
+
+ @Test
+ public void registerCallback_samePriority_sameCallback() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ // The callback is removed and added again
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
+ }
+
+ @Test
+ public void registerCallback_samePriority_differentCallback() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ // The new callback becomes the TopCallback
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback2);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
+ verifyNoMoreInteractions(mCallback2);
+ }
+
+ @Test
+ public void registerCallback_differentPriority_sameCallback() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
+ assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ // The callback is moved to the new priority list
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
+ }
+
+ @Test
+ public void registerCallback_differentPriority_differentCallback() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
+ assertSetCallbackInfo();
+ assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+ assertTopCallback(mCallback1);
+
+ // The callback with higher priority is still the TopCallback
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ assertNoSetCallbackInfo();
+ assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+ assertTopCallback(mCallback1);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
+ verifyNoMoreInteractions(mCallback2);
+ }
+
+ @Test
+ public void registerCallback_sameInstanceAddedTwice() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
+ assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+ assertNoSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback1);
+
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback2);
+ assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+ assertSetCallbackInfo();
+ assertTopCallback(mCallback2);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
+ verifyNoMoreInteractions(mCallback2);
+ }
+
@Test
public void propagatesTopCallback_samePriority() throws RemoteException {
- ArgumentCaptor<OnBackInvokedCallbackInfo> captor =
- ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ OnBackInvokedCallbackInfo callbackInfo1 = assertSetCallbackInfo();
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ OnBackInvokedCallbackInfo callbackInfo2 = assertSetCallbackInfo();
- verify(mWindowSession, times(2)).setOnBackInvokedCallbackInfo(
- Mockito.eq(mWindow),
- captor.capture());
- captor.getAllValues().get(0).getCallback().onBackStarted(mBackEvent);
+ callbackInfo1.getCallback().onBackStarted(mBackEvent);
+
waitForIdle();
verify(mCallback1).onBackStarted(any(BackEvent.class));
verifyZeroInteractions(mCallback2);
- captor.getAllValues().get(1).getCallback().onBackStarted(mBackEvent);
+ callbackInfo2.getCallback().onBackStarted(mBackEvent);
+
waitForIdle();
verify(mCallback2).onBackStarted(any(BackEvent.class));
+
+ // Calls sequence: BackProgressAnimator.onBackStarted() -> BackProgressAnimator.reset() ->
+ // Spring.animateToFinalPosition(0). This causes a progress event to be fired.
+ verify(mCallback1, atMost(1)).onBackProgressed(any(BackEvent.class));
verifyNoMoreInteractions(mCallback1);
}
@Test
public void propagatesTopCallback_differentPriority() throws RemoteException {
- ArgumentCaptor<OnBackInvokedCallbackInfo> captor =
- ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback1);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2);
+ OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
- verify(mWindowSession).setOnBackInvokedCallbackInfo(
- Mockito.eq(mWindow), captor.capture());
verifyNoMoreInteractions(mWindowSession);
- assertEquals(captor.getValue().getPriority(), OnBackInvokedDispatcher.PRIORITY_OVERLAY);
- captor.getValue().getCallback().onBackStarted(mBackEvent);
+ assertEquals(callbackInfo.getPriority(), PRIORITY_OVERLAY);
+
+ callbackInfo.getCallback().onBackStarted(mBackEvent);
+
waitForIdle();
verify(mCallback1).onBackStarted(any(BackEvent.class));
}
@Test
public void propagatesTopCallback_withRemoval() throws RemoteException {
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertSetCallbackInfo();
- reset(mWindowSession);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ assertSetCallbackInfo();
+
mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
- verifyZeroInteractions(mWindowSession);
+
+ waitForIdle();
+ verifyNoMoreInteractions(mWindowSession);
+ verifyNoMoreInteractions(mCallback1);
mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
+
+ waitForIdle();
verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
}
@Test
public void propagatesTopCallback_sameInstanceAddedTwice() throws RemoteException {
- ArgumentCaptor<OnBackInvokedCallbackInfo> captor =
- ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
+ assertSetCallbackInfo();
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+ assertNoSetCallbackInfo();
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+ assertSetCallbackInfo();
- mDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_OVERLAY,
- mCallback1
- );
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback2);
- reset(mWindowSession);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback2);
- verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture());
- captor.getValue().getCallback().onBackStarted(mBackEvent);
+ OnBackInvokedCallbackInfo lastCallbackInfo = assertSetCallbackInfo();
+
+ lastCallbackInfo.getCallback().onBackStarted(mBackEvent);
+
waitForIdle();
verify(mCallback2).onBackStarted(any(BackEvent.class));
}
@Test
public void onUnregisterWhileBackInProgress_callOnBackCancelled() throws RemoteException {
- ArgumentCaptor<OnBackInvokedCallbackInfo> captor =
- ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
- mDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1);
+ OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
- verify(mWindowSession).setOnBackInvokedCallbackInfo(
- Mockito.eq(mWindow),
- captor.capture());
- IOnBackInvokedCallback iOnBackInvokedCallback = captor.getValue().getCallback();
- iOnBackInvokedCallback.onBackStarted(mBackEvent);
+ callbackInfo.getCallback().onBackStarted(mBackEvent);
+
waitForIdle();
verify(mCallback1).onBackStarted(any(BackEvent.class));
mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
+
+ waitForIdle();
verify(mCallback1).onBackCancelled();
- verifyNoMoreInteractions(mCallback1);
+ verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 3b45d5d..4cedd41 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -308,7 +308,8 @@
forAllTaskContainers(taskContainer -> {
synchronized (mLock) {
- final List<TaskFragmentContainer> containers = taskContainer.mContainers;
+ final List<TaskFragmentContainer> containers =
+ taskContainer.getTaskFragmentContainers();
// Clean up the TaskFragmentContainers by the z-order from the lowest.
for (int i = 0; i < containers.size(); i++) {
final TaskFragmentContainer container = containers.get(i);
@@ -611,8 +612,7 @@
@NonNull TaskContainer taskContainer) {
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
- final List<TaskFragmentContainer> containers =
- new ArrayList<>(taskContainer.mContainers);
+ final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
@@ -1331,7 +1331,8 @@
// Check pending appeared activity first because there can be a delay for the server
// update.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasPendingAppearedActivity(activityToken)) {
@@ -1342,7 +1343,8 @@
// Check appeared activity if there is no such pending appeared activity.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasAppearedActivity(activityToken)) {
@@ -1472,7 +1474,7 @@
void removeContainers(@NonNull TaskContainer taskContainer,
@NonNull List<TaskFragmentContainer> containers) {
// Remove all split containers that included this one
- taskContainer.mContainers.removeAll(containers);
+ taskContainer.removeTaskFragmentContainers(containers);
// Marked as a pending removal which will be removed after it is actually removed on the
// server side (#onTaskFragmentVanished).
// In this way, we can keep track of the Task bounds until we no longer have any
@@ -1497,7 +1499,9 @@
taskContainer.removeSplitContainers(containersToRemove);
// Cleanup any dependent references.
- for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) {
+ final List<TaskFragmentContainer> taskFragmentContainers =
+ taskContainer.getTaskFragmentContainers();
+ for (TaskFragmentContainer containerToUpdate : taskFragmentContainers) {
containerToUpdate.removeContainersToFinishOnExit(containers);
}
}
@@ -1536,8 +1540,9 @@
if (taskContainer == null) {
return null;
}
- for (int i = taskContainer.mContainers.size() - 1; i >= 0; i--) {
- final TaskFragmentContainer container = taskContainer.mContainers.get(i);
+ final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+ for (int i = containers.size() - 1; i >= 0; i--) {
+ final TaskFragmentContainer container = containers.get(i);
if (!container.isFinished() && (container.getRunningActivityCount() > 0
// We may be waiting for the top TaskFragment to become non-empty after
// creation. In that case, we don't want to treat the TaskFragment below it as
@@ -1933,7 +1938,8 @@
@GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (TaskFragmentContainer container : containers) {
if (container.getTaskFragmentToken().equals(fragmentToken)) {
return container;
@@ -2094,7 +2100,7 @@
}
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .mContainers;
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (!container.hasActivity(activityToken)
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 0626510..4580c98 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -51,7 +51,7 @@
/** Active TaskFragments in this Task. */
@NonNull
- final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+ private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
/** Active split pairs in this Task. */
@NonNull
@@ -207,9 +207,13 @@
return false;
}
+ /**
+ * Returns a list of {@link SplitContainer}. Do not modify the containers directly on the
+ * returned list. Use {@link #addSplitContainer} or {@link #removeSplitContainers} instead.
+ */
@NonNull
List<SplitContainer> getSplitContainers() {
- return new ArrayList<>(mSplitContainers);
+ return mSplitContainers;
}
void addSplitContainer(@NonNull SplitContainer splitContainer) {
@@ -220,6 +224,36 @@
mSplitContainers.removeAll(containers);
}
+ void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.add(taskFragmentContainer);
+ }
+
+ void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.add(index, taskFragmentContainer);
+ }
+
+ void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.remove(taskFragmentContainer);
+ }
+
+ void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) {
+ mContainers.removeAll(taskFragmentContainer);
+ }
+
+ void clearTaskFragmentContainer() {
+ mContainers.clear();
+ }
+
+ /**
+ * Returns a list of {@link TaskFragmentContainer}. Do not modify the containers directly on
+ * the returned list. Use {@link #addTaskFragmentContainer},
+ * {@link #removeTaskFragmentContainer} or other related methods instead.
+ */
+ @NonNull
+ List<TaskFragmentContainer> getTaskFragmentContainers() {
+ return mContainers;
+ }
+
/** Adds the descriptors of split states in this Task to {@code outSplitStates}. */
void getSplitStates(@NonNull List<SplitInfo> outSplitStates) {
for (SplitContainer container : mSplitContainers) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 60be9d1..61df335 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -180,23 +180,25 @@
throw new IllegalArgumentException(
"pairedPrimaryContainer must be in the same Task");
}
- final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer);
- taskContainer.mContainers.add(primaryIndex + 1, this);
+ final int primaryIndex = taskContainer.indexOf(pairedPrimaryContainer);
+ taskContainer.addTaskFragmentContainer(primaryIndex + 1, this);
} else if (pendingAppearedActivity != null) {
// The TaskFragment will be positioned right above the pending appeared Activity. If any
// existing TaskFragment is empty with pending Intent, it is likely that the Activity of
// the pending Intent hasn't been created yet, so the new Activity should be below the
// empty TaskFragment.
- int i = taskContainer.mContainers.size() - 1;
+ final List<TaskFragmentContainer> containers =
+ taskContainer.getTaskFragmentContainers();
+ int i = containers.size() - 1;
for (; i >= 0; i--) {
- final TaskFragmentContainer container = taskContainer.mContainers.get(i);
+ final TaskFragmentContainer container = containers.get(i);
if (!container.isEmpty() || container.getPendingAppearedIntent() == null) {
break;
}
}
- taskContainer.mContainers.add(i + 1, this);
+ taskContainer.addTaskFragmentContainer(i + 1, this);
} else {
- taskContainer.mContainers.add(this);
+ taskContainer.addTaskFragmentContainer(this);
}
if (pendingAppearedActivity != null) {
addPendingAppearedActivity(pendingAppearedActivity);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 82692a5..9e26472 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -183,23 +183,23 @@
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
- taskContainer.mContainers.add(tf2);
+ taskContainer.addTaskFragmentContainer(tf2);
// tf3 is finished so is not active.
final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class);
doReturn(true).when(tf3).isFinished();
doReturn(false).when(tf3).isWaitingActivityAppear();
- taskContainer.mContainers.add(tf3);
+ taskContainer.addTaskFragmentContainer(tf3);
mSplitController.mTaskContainers.put(TASK_ID, taskContainer);
assertWithMessage("Must return tf2 because tf3 is not active.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
- taskContainer.mContainers.remove(tf3);
+ taskContainer.removeTaskFragmentContainer(tf3);
assertWithMessage("Must return tf2 because tf2 has running activity.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
- taskContainer.mContainers.remove(tf2);
+ taskContainer.removeTaskFragmentContainer(tf2);
assertWithMessage("Must return tf because we are waiting for tf1 to appear.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1);
@@ -365,7 +365,8 @@
final Activity r1 = createMockActivity();
addSplitTaskFragments(r0, r1);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
- final TaskFragmentContainer taskFragmentContainer = taskContainer.mContainers.get(0);
+ final TaskFragmentContainer taskFragmentContainer =
+ taskContainer.getTaskFragmentContainers().get(0);
spyOn(taskContainer);
// No update when the Task is invisible.
@@ -1092,7 +1093,7 @@
verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
- assertTrue(taskContainer.mContainers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
assertTrue(taskContainer.getSplitContainers().isEmpty());
}
@@ -1365,15 +1366,13 @@
TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
- List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
- .mContainers;
-
- assertEquals(containers.get(0), tf);
+ final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
+ assertEquals(taskContainer.getTaskFragmentContainers().get(0), tf);
mSplitController.finishActivityStacks(Collections.singleton(tf.getTaskFragmentToken()));
verify(mSplitPresenter).deleteTaskFragment(any(), eq(tf.getTaskFragmentToken()));
- assertTrue(containers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test
@@ -1383,10 +1382,8 @@
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
- List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
- .mContainers;
-
- assertEquals(containers.size(), 2);
+ final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
+ assertEquals(taskContainer.getTaskFragmentContainers().size(), 2);
Set<IBinder> activityStackTokens = new ArraySet<>(new IBinder[]{
topTf.getTaskFragmentToken(), bottomTf.getTaskFragmentToken()});
@@ -1405,7 +1402,7 @@
+ "regardless of the order in ActivityStack set",
topTf.getTaskFragmentToken(), fragmentTokens.get(1));
- assertTrue(containers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 13e7092..11af1d1 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -127,7 +127,7 @@
assertFalse(taskContainer.isEmpty());
taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
- taskContainer.mContainers.clear();
+ taskContainer.clearTaskFragmentContainer();
assertFalse(taskContainer.isEmpty());
}
@@ -152,13 +152,13 @@
assertNull(taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class);
- taskContainer.mContainers.add(tf0);
+ taskContainer.addTaskFragmentContainer(tf0);
final Activity activity0 = mock(Activity.class);
doReturn(activity0).when(tf0).getTopNonFinishingActivity();
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf1 = mock(TaskFragmentContainer.class);
- taskContainer.mContainers.add(tf1);
+ taskContainer.addTaskFragmentContainer(tf1);
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final Activity activity1 = mock(Activity.class);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 25e9401..c25352b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -166,7 +166,7 @@
"unocclude",
transition, info, startTransaction, finishTransaction, finishCallback);
} else {
- Log.wtf(TAG, "Failed to play: " + info);
+ Log.w(TAG, "Failed to play: " + info);
return false;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index ac5ff20..67ec6cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -248,6 +248,9 @@
mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
wct.addInsetsSource(mTaskInfo.token,
mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect);
} else {
startT.hide(mCaptionContainerSurface);
}
@@ -345,6 +348,8 @@
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
wct.removeInsetsSource(mTaskInfo.token,
mOwner, 0 /* index */, WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures());
mTaskOrganizer.applyTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 5a2326b..f941e95 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -316,7 +316,8 @@
releaseOrder.verify(t).remove(captionContainerSurface);
releaseOrder.verify(t).remove(decorContainerSurface);
releaseOrder.verify(t).apply();
- verify(mMockWindowContainerTransaction)
+ // Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
+ verify(mMockWindowContainerTransaction, Mockito.times(2))
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
index 5eaa495..460bf99 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
@@ -21,6 +21,7 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
@@ -34,42 +35,48 @@
fontWeight = FontWeight.Normal,
fontSize = 57.sp,
lineHeight = 64.sp,
- letterSpacing = (-0.2).sp
+ letterSpacing = (-0.2).sp,
+ hyphens = Hyphens.Auto,
),
displayMedium = TextStyle(
fontFamily = brand,
fontWeight = FontWeight.Normal,
fontSize = 45.sp,
lineHeight = 52.sp,
- letterSpacing = 0.0.sp
+ letterSpacing = 0.0.sp,
+ hyphens = Hyphens.Auto,
),
displaySmall = TextStyle(
fontFamily = brand,
fontWeight = FontWeight.Normal,
fontSize = 36.sp,
lineHeight = 44.sp,
- letterSpacing = 0.0.sp
+ letterSpacing = 0.0.sp,
+ hyphens = Hyphens.Auto,
),
headlineLarge = TextStyle(
fontFamily = brand,
fontWeight = FontWeight.Normal,
fontSize = 32.sp,
lineHeight = 40.sp,
- letterSpacing = 0.0.sp
+ letterSpacing = 0.0.sp,
+ hyphens = Hyphens.Auto,
),
headlineMedium = TextStyle(
fontFamily = brand,
fontWeight = FontWeight.Normal,
fontSize = 28.sp,
lineHeight = 36.sp,
- letterSpacing = 0.0.sp
+ letterSpacing = 0.0.sp,
+ hyphens = Hyphens.Auto,
),
headlineSmall = TextStyle(
fontFamily = brand,
fontWeight = FontWeight.Normal,
fontSize = 24.sp,
lineHeight = 32.sp,
- letterSpacing = 0.0.sp
+ letterSpacing = 0.0.sp,
+ hyphens = Hyphens.Auto,
),
titleLarge = TextStyle(
fontFamily = brand,
@@ -77,6 +84,7 @@
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.02.em,
+ hyphens = Hyphens.Auto,
),
titleMedium = TextStyle(
fontFamily = brand,
@@ -84,6 +92,7 @@
fontSize = 20.sp,
lineHeight = 24.sp,
letterSpacing = 0.02.em,
+ hyphens = Hyphens.Auto,
),
titleSmall = TextStyle(
fontFamily = brand,
@@ -91,6 +100,7 @@
fontSize = 18.sp,
lineHeight = 20.sp,
letterSpacing = 0.02.em,
+ hyphens = Hyphens.Auto,
),
bodyLarge = TextStyle(
fontFamily = plain,
@@ -98,6 +108,7 @@
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
bodyMedium = TextStyle(
fontFamily = plain,
@@ -105,6 +116,7 @@
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
bodySmall = TextStyle(
fontFamily = plain,
@@ -112,6 +124,7 @@
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
labelLarge = TextStyle(
fontFamily = plain,
@@ -119,6 +132,7 @@
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
labelMedium = TextStyle(
fontFamily = plain,
@@ -126,6 +140,7 @@
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
labelSmall = TextStyle(
fontFamily = plain,
@@ -133,6 +148,7 @@
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.01.em,
+ hyphens = Hyphens.Auto,
),
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 5f2344e..01ba8f8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -29,6 +30,7 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.toMediumWeight
@@ -78,7 +80,7 @@
@Composable
fun PlaceholderTitle(title: String) {
Box(
- modifier = Modifier.fillMaxSize(),
+ modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPadding),
contentAlignment = Alignment.Center,
) {
Text(
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index 2584be7..4ac4aae 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -17,6 +17,7 @@
<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<style name="TextAppearanceSmall">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearanceMedium">
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
@@ -91,7 +92,7 @@
<item name="android:ellipsize">end</item>
</style>
- <style name="DialogButtonPositive" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <style name="DialogButtonPositive">
<item name="android:buttonCornerRadius">0dp</item>
<item name="android:background">@drawable/dialog_btn_filled</item>
<item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
@@ -105,7 +106,6 @@
<style name="DialogButtonNegative">
<item name="android:buttonCornerRadius">28dp</item>
<item name="android:background">@drawable/dialog_btn_outline</item>
- <item name="android:buttonCornerRadius">28dp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
index 2bca7cf..1ff2bef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
@@ -43,5 +43,5 @@
/**
* Bluetooth scheme.
*/
- public static final String SCHEME_BT_BROADCAST_METADATA = "BT:BluetoothLeBroadcastMetadata:";
+ public static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
index b54b115..9bb11f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
@@ -15,23 +15,92 @@
*/
package com.android.settingslib.bluetooth
+import android.annotation.TargetApi
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata
+import android.bluetooth.BluetoothLeAudioContentMetadata
+import android.bluetooth.BluetoothLeBroadcastChannel
import android.bluetooth.BluetoothLeBroadcastMetadata
-import android.os.Parcel
-import android.os.Parcelable
+import android.bluetooth.BluetoothLeBroadcastSubgroup
+import android.os.Build
import android.util.Base64
import android.util.Log
import com.android.settingslib.bluetooth.BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA
object BluetoothLeBroadcastMetadataExt {
- private const val TAG = "BluetoothLeBroadcastMetadataExt"
+ private const val TAG = "BtLeBroadcastMetadataExt"
+
+ // BluetoothLeBroadcastMetadata
+ private const val KEY_BT_QR_VER = "R"
+ private const val KEY_BT_ADDRESS_TYPE = "T"
+ private const val KEY_BT_DEVICE = "D"
+ private const val KEY_BT_ADVERTISING_SID = "AS"
+ private const val KEY_BT_BROADCAST_ID = "B"
+ private const val KEY_BT_BROADCAST_NAME = "BN"
+ private const val KEY_BT_PUBLIC_BROADCAST_DATA = "PM"
+ private const val KEY_BT_SYNC_INTERVAL = "SI"
+ private const val KEY_BT_BROADCAST_CODE = "C"
+ private const val KEY_BT_SUBGROUPS = "SG"
+ private const val KEY_BT_VENDOR_SPECIFIC = "V"
+ private const val KEY_BT_ANDROID_VERSION = "VN"
+
+ // Subgroup data
+ private const val KEY_BTSG_BIS_SYNC = "BS"
+ private const val KEY_BTSG_BIS_MASK = "BM"
+ private const val KEY_BTSG_AUDIO_CONTENT = "AC"
+
+ // Vendor specific data
+ private const val KEY_BTVSD_COMPANY_ID = "VI"
+ private const val KEY_BTVSD_VENDOR_DATA = "VD"
+
+ private const val DELIMITER_KEY_VALUE = ":"
+ private const val DELIMITER_BT_LEVEL_1 = ";"
+ private const val DELIMITER_BT_LEVEL_2 = ","
+
+ private const val SUFFIX_QR_CODE = ";;"
+
+ private const val ANDROID_VER = "U"
+ private const val QR_CODE_VER = 0x010000
+
+ // BT constants
+ private const val BIS_SYNC_MAX_CHANNEL = 32
+ private const val BIS_SYNC_NO_PREFERENCE = 0xFFFFFFFFu
+ private const val SUBGROUP_LC3_CODEC_ID = 0x6L
/**
* Converts [BluetoothLeBroadcastMetadata] to QR code string.
*
- * QR code string will prefix with "BT:BluetoothLeBroadcastMetadata:".
+ * QR code string will prefix with "BT:".
*/
- fun BluetoothLeBroadcastMetadata.toQrCodeString(): String =
- SCHEME_BT_BROADCAST_METADATA + Base64.encodeToString(toBytes(this), Base64.NO_WRAP)
+ fun BluetoothLeBroadcastMetadata.toQrCodeString(): String {
+ val entries = mutableListOf<Pair<String, String>>()
+ entries.add(Pair(KEY_BT_QR_VER, QR_CODE_VER.toString()))
+ entries.add(Pair(KEY_BT_ADDRESS_TYPE, this.sourceAddressType.toString()))
+ entries.add(Pair(KEY_BT_DEVICE, this.sourceDevice.address.replace(":", "-")))
+ entries.add(Pair(KEY_BT_ADVERTISING_SID, this.sourceAdvertisingSid.toString()))
+ entries.add(Pair(KEY_BT_BROADCAST_ID, this.broadcastId.toString()))
+ if (this.broadcastName != null) {
+ entries.add(Pair(KEY_BT_BROADCAST_NAME, Base64.encodeToString(
+ this.broadcastName?.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)))
+ }
+ if (this.publicBroadcastMetadata != null) {
+ entries.add(Pair(KEY_BT_PUBLIC_BROADCAST_DATA, Base64.encodeToString(
+ this.publicBroadcastMetadata?.rawMetadata, Base64.NO_WRAP)))
+ }
+ entries.add(Pair(KEY_BT_SYNC_INTERVAL, this.paSyncInterval.toString()))
+ if (this.broadcastCode != null) {
+ entries.add(Pair(KEY_BT_BROADCAST_CODE,
+ Base64.encodeToString(this.broadcastCode, Base64.NO_WRAP)))
+ }
+ this.subgroups.forEach {
+ subgroup -> entries.add(Pair(KEY_BT_SUBGROUPS, subgroup.toQrCodeString())) }
+ entries.add(Pair(KEY_BT_ANDROID_VERSION, ANDROID_VER))
+ val qrCodeString = SCHEME_BT_BROADCAST_METADATA +
+ entries.toQrCodeString(DELIMITER_BT_LEVEL_1) + SUFFIX_QR_CODE
+ Log.d(TAG, "Generated QR string : $qrCodeString")
+ return qrCodeString
+ }
/**
* Converts QR code string to [BluetoothLeBroadcastMetadata].
@@ -39,32 +108,255 @@
* QR code string should prefix with "BT:BluetoothLeBroadcastMetadata:".
*/
fun convertToBroadcastMetadata(qrCodeString: String): BluetoothLeBroadcastMetadata? {
- if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) return null
+ if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) {
+ Log.e(TAG, "String \"$qrCodeString\" does not begin with " +
+ "\"$SCHEME_BT_BROADCAST_METADATA\"")
+ return null
+ }
return try {
- val encodedString = qrCodeString.removePrefix(SCHEME_BT_BROADCAST_METADATA)
- val bytes = Base64.decode(encodedString, Base64.NO_WRAP)
- createFromBytes(BluetoothLeBroadcastMetadata.CREATOR, bytes)
+ Log.d(TAG, "Parsing QR string: $qrCodeString")
+ val strippedString =
+ qrCodeString.removePrefix(SCHEME_BT_BROADCAST_METADATA)
+ .removeSuffix(SUFFIX_QR_CODE)
+ Log.d(TAG, "Stripped to: $strippedString")
+ parseQrCodeToMetadata(strippedString)
} catch (e: Exception) {
- Log.w(TAG, "Cannot convert QR code string to BluetoothLeBroadcastMetadata", e)
+ Log.w(TAG, "Cannot parse: $qrCodeString", e)
null
}
}
- private fun toBytes(parcelable: Parcelable): ByteArray =
- Parcel.obtain().run {
- parcelable.writeToParcel(this, 0)
- setDataPosition(0)
- val bytes = marshall()
- recycle()
- bytes
- }
+ private fun BluetoothLeBroadcastSubgroup.toQrCodeString(): String {
+ val entries = mutableListOf<Pair<String, String>>()
+ entries.add(Pair(KEY_BTSG_BIS_SYNC, getBisSyncFromChannels(this.channels).toString()))
+ entries.add(Pair(KEY_BTSG_BIS_MASK, getBisMaskFromChannels(this.channels).toString()))
+ entries.add(Pair(KEY_BTSG_AUDIO_CONTENT,
+ Base64.encodeToString(this.contentMetadata.rawMetadata, Base64.NO_WRAP)))
+ return entries.toQrCodeString(DELIMITER_BT_LEVEL_2)
+ }
- private fun <T> createFromBytes(creator: Parcelable.Creator<T>, bytes: ByteArray): T =
- Parcel.obtain().run {
- unmarshall(bytes, 0, bytes.size)
- setDataPosition(0)
- val created = creator.createFromParcel(this)
- recycle()
- created
+ private fun List<Pair<String, String>>.toQrCodeString(delimiter: String): String {
+ val entryStrings = this.map{ it.first + DELIMITER_KEY_VALUE + it.second }
+ return entryStrings.joinToString(separator = delimiter)
+ }
+
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ private fun parseQrCodeToMetadata(input: String): BluetoothLeBroadcastMetadata {
+ // Split into a list of list
+ val level1Fields = input.split(DELIMITER_BT_LEVEL_1)
+ .map{it.split(DELIMITER_KEY_VALUE, limit = 2)}
+ var qrCodeVersion = -1
+ var sourceAddrType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN
+ var sourceAddrString: String? = null
+ var sourceAdvertiserSid = -1
+ var broadcastId = -1
+ var broadcastName: String? = null
+ var publicBroadcastMetadata: BluetoothLeAudioContentMetadata? = null
+ var paSyncInterval = -1
+ var broadcastCode: ByteArray? = null
+ // List of VendorID -> Data Pairs
+ var vendorDataList = mutableListOf<Pair<Int, ByteArray?>>()
+ var androidVersion: String? = null
+ val builder = BluetoothLeBroadcastMetadata.Builder()
+
+ for (field: List<String> in level1Fields) {
+ if (field.isEmpty()) {
+ continue
+ }
+ val key = field[0]
+ // Ignore 3rd value and after
+ val value = if (field.size > 1) field[1] else ""
+ when (key) {
+ KEY_BT_QR_VER -> {
+ require(qrCodeVersion == -1) { "Duplicate qrCodeVersion: $input" }
+ qrCodeVersion = value.toInt()
+ }
+ KEY_BT_ADDRESS_TYPE -> {
+ require(sourceAddrType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
+ "Duplicate sourceAddrType: $input"
+ }
+ sourceAddrType = value.toInt()
+ }
+ KEY_BT_DEVICE -> {
+ require(sourceAddrString == null) { "Duplicate sourceAddr: $input" }
+ sourceAddrString = value.replace("-", ":")
+ }
+ KEY_BT_ADVERTISING_SID -> {
+ require(sourceAdvertiserSid == -1) { "Duplicate sourceAdvertiserSid: $input" }
+ sourceAdvertiserSid = value.toInt()
+ }
+ KEY_BT_BROADCAST_ID -> {
+ require(broadcastId == -1) { "Duplicate broadcastId: $input" }
+ broadcastId = value.toInt()
+ }
+ KEY_BT_BROADCAST_NAME -> {
+ require(broadcastName == null) { "Duplicate broadcastName: $input" }
+ broadcastName = String(Base64.decode(value, Base64.NO_WRAP))
+ }
+ KEY_BT_PUBLIC_BROADCAST_DATA -> {
+ require(publicBroadcastMetadata == null) {
+ "Duplicate publicBroadcastMetadata $input"
+ }
+ publicBroadcastMetadata = BluetoothLeAudioContentMetadata
+ .fromRawBytes(Base64.decode(value, Base64.NO_WRAP))
+ }
+ KEY_BT_SYNC_INTERVAL -> {
+ require(paSyncInterval == -1) { "Duplicate paSyncInterval: $input" }
+ paSyncInterval = value.toInt()
+ }
+ KEY_BT_BROADCAST_CODE -> {
+ require(broadcastCode == null) { "Duplicate broadcastCode: $input" }
+ broadcastCode = Base64.decode(value, Base64.NO_WRAP)
+ }
+ KEY_BT_ANDROID_VERSION -> {
+ require(androidVersion == null) { "Duplicate androidVersion: $input" }
+ androidVersion = value
+ Log.i(TAG, "QR code Android version: $androidVersion")
+ }
+ // Repeatable
+ KEY_BT_SUBGROUPS -> {
+ builder.addSubgroup(parseSubgroupData(value))
+ }
+ // Repeatable
+ KEY_BT_VENDOR_SPECIFIC -> {
+ vendorDataList.add(parseVendorData(value))
+ }
+ }
}
+ Log.d(TAG, "parseQrCodeToMetadata: sourceAddrType=$sourceAddrType, " +
+ "sourceAddr=$sourceAddrString, sourceAdvertiserSid=$sourceAdvertiserSid, " +
+ "broadcastId=$broadcastId, broadcastName=$broadcastName, " +
+ "publicBroadcastMetadata=${publicBroadcastMetadata != null}, " +
+ "paSyncInterval=$paSyncInterval, " +
+ "broadcastCode=${broadcastCode?.toString(Charsets.UTF_8)}")
+ Log.d(TAG, "Not used in current code, but part of the specification: " +
+ "qrCodeVersion=$qrCodeVersion, androidVersion=$androidVersion, " +
+ "vendorDataListSize=${vendorDataList.size}")
+ val adapter = BluetoothAdapter.getDefaultAdapter()
+ // add source device and set broadcast code
+ val device = adapter.getRemoteLeDevice(requireNotNull(sourceAddrString), sourceAddrType)
+ builder.apply {
+ setSourceDevice(device, sourceAddrType)
+ setSourceAdvertisingSid(sourceAdvertiserSid)
+ setBroadcastId(broadcastId)
+ setBroadcastName(broadcastName)
+ setPublicBroadcast(publicBroadcastMetadata != null)
+ setPublicBroadcastMetadata(publicBroadcastMetadata)
+ setPaSyncInterval(paSyncInterval)
+ setEncrypted(broadcastCode != null)
+ setBroadcastCode(broadcastCode)
+ // Presentation delay is unknown and not useful when adding source
+ // Broadcast sink needs to sync to the Broadcast source to get presentation delay
+ setPresentationDelayMicros(0)
+ }
+ return builder.build()
+ }
+
+ private fun parseSubgroupData(input: String): BluetoothLeBroadcastSubgroup {
+ Log.d(TAG, "parseSubgroupData: $input")
+ val fields = input.split(DELIMITER_BT_LEVEL_2)
+ var bisSync: UInt? = null
+ var bisMask: UInt? = null
+ var metadata: ByteArray? = null
+
+ fields.forEach { field ->
+ val(key, value) = field.split(DELIMITER_KEY_VALUE)
+ when (key) {
+ KEY_BTSG_BIS_SYNC -> {
+ require(bisSync == null) { "Duplicate bisSync: $input" }
+ bisSync = value.toUInt()
+ }
+ KEY_BTSG_BIS_MASK -> {
+ require(bisMask == null) { "Duplicate bisMask: $input" }
+ bisMask = value.toUInt()
+ }
+ KEY_BTSG_AUDIO_CONTENT -> {
+ require(metadata == null) { "Duplicate metadata: $input" }
+ metadata = Base64.decode(value, Base64.NO_WRAP)
+ }
+ }
+ }
+ val channels = convertToChannels(requireNotNull(bisSync), requireNotNull(bisMask))
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(0).build()
+ return BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(SUBGROUP_LC3_CODEC_ID)
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(
+ BluetoothLeAudioContentMetadata.fromRawBytes(metadata ?: ByteArray(0)))
+ channels.forEach(::addChannel)
+ }.build()
+ }
+
+ private fun parseVendorData(input: String): Pair<Int, ByteArray?> {
+ var companyId = -1
+ var data: ByteArray? = null
+ val fields = input.split(DELIMITER_BT_LEVEL_2)
+ fields.forEach { field ->
+ val(key, value) = field.split(DELIMITER_KEY_VALUE)
+ when (key) {
+ KEY_BTVSD_COMPANY_ID -> {
+ require(companyId == -1) { "Duplicate companyId: $input" }
+ companyId = value.toInt()
+ }
+ KEY_BTVSD_VENDOR_DATA -> {
+ require(data == null) { "Duplicate data: $input" }
+ data = Base64.decode(value, Base64.NO_WRAP)
+ }
+ }
+ }
+ return Pair(companyId, data)
+ }
+
+ private fun getBisSyncFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
+ var bisSync = 0u
+ // channel index starts from 1
+ channels.forEach { channel ->
+ if (channel.isSelected && channel.channelIndex > 0) {
+ bisSync = bisSync or (1u shl (channel.channelIndex - 1))
+ }
+ }
+ // No channel is selected means no preference on Android platform
+ return if (bisSync == 0u) BIS_SYNC_NO_PREFERENCE else bisSync
+ }
+
+ private fun getBisMaskFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
+ var bisMask = 0u
+ // channel index starts from 1
+ channels.forEach { channel ->
+ if (channel.channelIndex > 0) {
+ bisMask = bisMask or (1u shl (channel.channelIndex - 1))
+ }
+ }
+ return bisMask
+ }
+
+ private fun convertToChannels(bisSync: UInt, bisMask: UInt):
+ List<BluetoothLeBroadcastChannel> {
+ Log.d(TAG, "convertToChannels: bisSync=$bisSync, bisMask=$bisMask")
+ var selectionMask = bisSync
+ if (bisSync != BIS_SYNC_NO_PREFERENCE) {
+ require(bisMask == (bisMask or bisSync)) {
+ "bisSync($bisSync) must select a subset of bisMask($bisMask) if it has preferences"
+ }
+ } else {
+ // No channel preference means no channel is selected
+ selectionMask = 0u
+ }
+ val channels = mutableListOf<BluetoothLeBroadcastChannel>()
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(0).build()
+ for (i in 0 until BIS_SYNC_MAX_CHANNEL) {
+ val channelMask = 1u shl i
+ if ((bisMask and channelMask) != 0u) {
+ val channel = BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected((selectionMask and channelMask) != 0u)
+ setChannelIndex(i + 1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }
+ channels.add(channel.build())
+ }
+ }
+ return channels
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 76556639..1251b0d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -196,6 +196,9 @@
/** Gets the battery level from the intent. */
public static int getBatteryLevel(Intent batteryChangedIntent) {
+ if (batteryChangedIntent == null) {
+ return -1; /*invalid battery level*/
+ }
final int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
return scale == 0
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
index e835125..e6b1cfb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
@@ -16,14 +16,13 @@
package com.android.settingslib.mobile.dataservice;
-import java.util.List;
-
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
-import androidx.room.Update;
+
+import java.util.List;
@Dao
public interface SubscriptionInfoDao {
@@ -32,7 +31,9 @@
void insertSubsInfo(SubscriptionInfoEntity... subscriptionInfo);
@Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " ORDER BY "
- + DataServiceUtils.SubscriptionInfoData.COLUMN_ID)
+ + " CASE WHEN " + DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX
+ + " >= 0 THEN 1 ELSE 2 END , "
+ + DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX)
LiveData<List<SubscriptionInfoEntity>> queryAvailableSubInfos();
@Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " WHERE "
@@ -43,7 +44,8 @@
+ DataServiceUtils.SubscriptionInfoData.COLUMN_IS_ACTIVE_SUBSCRIPTION_ID
+ " = :isActiveSubscription" + " AND "
+ DataServiceUtils.SubscriptionInfoData.COLUMN_IS_SUBSCRIPTION_VISIBLE
- + " = :isSubscriptionVisible")
+ + " = :isSubscriptionVisible" + " ORDER BY "
+ + DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX)
LiveData<List<SubscriptionInfoEntity>> queryActiveSubInfos(
boolean isActiveSubscription, boolean isSubscriptionVisible);
diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
index 0e3590d..27d7078 100644
--- a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
+++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
@@ -34,24 +34,33 @@
@Test
fun toQrCodeString() {
val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
- setCodecId(100)
+ setCodecId(0x6)
val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
setCodecSpecificConfig(audioCodecConfigMetadata)
- setContentMetadata(BluetoothLeAudioContentMetadata.Builder().build())
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
addChannel(BluetoothLeBroadcastChannel.Builder().apply {
- setChannelIndex(1000)
+ setSelected(true)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(true)
+ setChannelIndex(1)
setCodecMetadata(audioCodecConfigMetadata)
}.build())
}.build()
val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
- setSourceDevice(Device, 0)
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
setSourceAdvertisingSid(1)
- setBroadcastId(2)
- setPaSyncInterval(3)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
setEncrypted(true)
- setBroadcastCode(byteArrayOf(10, 11, 12, 13))
- setPresentationDelayMicros(4)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
addSubgroup(subgroup)
}.build()
@@ -61,6 +70,108 @@
}
@Test
+ fun toQrCodeString_NoChannelSelected() {
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(0x6)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ }.build()
+
+ val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ setSourceAdvertisingSid(1)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
+ setEncrypted(true)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
+ addSubgroup(subgroup)
+ }.build()
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ val parsedMetadata =
+ BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(qrCodeString)!!
+
+ assertThat(parsedMetadata).isNotNull()
+ assertThat(parsedMetadata.subgroups).isNotNull()
+ assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isFalse()
+ // Input order does not matter due to parsing through bisMask
+ assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
+ assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isFalse()
+ }
+
+ @Test
+ fun toQrCodeString_OneChannelSelected() {
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(0x6)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(true)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ }.build()
+
+ val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ setSourceAdvertisingSid(1)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
+ setEncrypted(true)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
+ addSubgroup(subgroup)
+ }.build()
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ val parsedMetadata =
+ BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(qrCodeString)!!
+
+ assertThat(parsedMetadata).isNotNull()
+ assertThat(parsedMetadata.subgroups).isNotNull()
+ assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
+ // Only selected channel can be recovered
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isTrue()
+ assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
+ assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isTrue()
+ }
+
+ @Test
fun decodeAndEncodeAgain_sameString() {
val metadata = BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(QR_CODE_STRING)!!
@@ -73,13 +184,12 @@
const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
val Device: BluetoothDevice =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_DEVICE_ADDRESS)
+ BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(TEST_DEVICE_ADDRESS,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM)
const val QR_CODE_STRING =
- "BT:BluetoothLeBroadcastMetadata:AAAAAAEAAAABAAAAEQAAADAAMAA6AEEAMQA6AEEAMQA6AEEAMQA6" +
- "AEEAMQA6AEEAMQAAAAAAAAABAAAAAgAAAAMAAAABAAAABAAAAAQAAAAKCwwNBAAAAAEAAAABAAAAZAAA" +
- "AAAAAAABAAAAAAAAAAAAAAAGAAAABgAAAAUDAAAAAAAAAAAAAAAAAAAAAAAAAQAAAP//////////AAAA" +
- "AAAAAAABAAAAAQAAAAAAAADoAwAAAQAAAAAAAAAAAAAABgAAAAYAAAAFAwAAAAAAAAAAAAAAAAAAAAAA" +
- "AAAAAAD/////AAAAAAAAAAA="
+ "BT:R:65536;T:1;D:00-A1-A1-A1-A1-A1;AS:1;B:123456;BN:VGVzdA==;" +
+ "PM:BgNwVGVzdA==;SI:160;C:VGVzdENvZGU=;SG:BS:3,BM:3,AC:BQNUZXN0BARlbmc=;" +
+ "VN:U;;"
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
index 8fe1f48..4fe9f89 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
@@ -27,37 +27,52 @@
object Easings {
/** The standard interpolator that should be used on every normal animation */
- val StandardEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD)
+ val Standard = fromInterpolator(InterpolatorsAndroidX.STANDARD)
/**
* The standard accelerating interpolator that should be used on every regular movement of
* content that is disappearing e.g. when moving off screen.
*/
- val StandardAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE)
+ val StandardAccelerate = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE)
/**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
- val StandardDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE)
+ val StandardDecelerate = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE)
/** The default emphasized interpolator. Used for hero / emphasized movement of content. */
- val EmphasizedEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED)
+ val Emphasized = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED)
/**
* The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
* is disappearing e.g. when moving off screen.
*/
- val EmphasizedAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE)
+ val EmphasizedAccelerate = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE)
/**
* The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
* is appearing e.g. when coming from off screen
*/
- val EmphasizedDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE)
+ val EmphasizedDecelerate = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE)
/** The linear interpolator. */
- val LinearEasing = fromInterpolator(InterpolatorsAndroidX.LINEAR)
+ val Linear = fromInterpolator(InterpolatorsAndroidX.LINEAR)
+
+ /** The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. */
+ val Legacy = fromInterpolator(InterpolatorsAndroidX.LEGACY)
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1. Also known as
+ * FAST_OUT_LINEAR_IN.
+ */
+ val LegacyAccelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_ACCELERATE)
+
+ /**
+ * T The default legacy decelerating interpolator as defined in Material 1. Also known as
+ * LINEAR_OUT_SLOW_IN.
+ */
+ val LegacyDecelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_DECELERATE)
private fun fromInterpolator(source: Interpolator) = Easing { x -> source.getInterpolation(x) }
}
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 8844114..b3d2e35 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
@@ -131,7 +131,7 @@
animationSpec =
tween(
durationMillis = SELECTED_DOT_REACTION_ANIMATION_DURATION_MS,
- easing = Easings.StandardAccelerateEasing,
+ easing = Easings.StandardAccelerate,
),
)
} else {
@@ -140,7 +140,7 @@
animationSpec =
tween(
durationMillis = SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS,
- easing = Easings.StandardDecelerateEasing,
+ easing = Easings.StandardDecelerate,
),
)
}
@@ -333,7 +333,7 @@
FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS,
delayMillis =
rowIndex * FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS,
- easing = Easings.LinearEasing,
+ easing = Easings.Linear,
),
)
@@ -343,7 +343,7 @@
tween(
durationMillis =
FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION,
- easing = Easings.StandardEasing,
+ easing = Easings.Standard,
),
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 968e5ab..f801434 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -19,23 +19,19 @@
package com.android.systemui.bouncer.ui.composable
import android.view.HapticFeedbackConstants
-import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.Transition
+import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.keyframes
+import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.scaleIn
-import androidx.compose.animation.scaleOut
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.foundation.background
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -43,35 +39,40 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.systemui.R
+import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.thenIf
-import kotlin.math.max
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
import kotlinx.coroutines.async
@@ -86,8 +87,6 @@
// Report that the UI is shown to let the view-model run some logic.
LaunchedEffect(Unit) { viewModel.onShown() }
- // The length of the PIN input received so far, so we know how many dots to render.
- val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState()
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
@@ -103,30 +102,7 @@
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
- Row(
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- modifier = Modifier.heightIn(min = 16.dp).animateContentSize(),
- ) {
- // TODO(b/281871687): add support for dot shapes.
- val (previousPinLength, currentPinLength) = pinLength
- val dotCount = max(previousPinLength, currentPinLength) + 1
- repeat(dotCount) { index ->
- AnimatedVisibility(
- visible = index < currentPinLength,
- enter = fadeIn() + scaleIn() + slideInHorizontally(),
- exit = fadeOut() + scaleOut() + slideOutHorizontally(),
- ) {
- Box(
- modifier =
- Modifier.size(16.dp)
- .background(
- MaterialTheme.colorScheme.onSurfaceVariant,
- CircleShape,
- )
- )
- }
- }
- }
+ PinInputDisplay(viewModel)
Spacer(Modifier.height(100.dp))
@@ -187,6 +163,148 @@
}
@Composable
+private fun PinInputDisplay(viewModel: PinBouncerViewModel) {
+ val currentPinEntries: List<EnteredKey> by viewModel.pinEntries.collectAsState()
+
+ // visiblePinEntries keeps pins removed from currentPinEntries in the composition until their
+ // disappear-animation completed. The list is sorted by the natural ordering of EnteredKey,
+ // which is guaranteed to produce the original edit order, since the model only modifies entries
+ // at the end.
+ val visiblePinEntries = remember { SnapshotStateList<EnteredKey>() }
+ currentPinEntries.forEach {
+ val index = visiblePinEntries.binarySearch(it)
+ if (index < 0) {
+ val insertionPoint = -(index + 1)
+ visiblePinEntries.add(insertionPoint, it)
+ }
+ }
+
+ Row(
+ modifier =
+ Modifier.heightIn(min = entryShapeSize)
+ // Pins overflowing horizontally should still be shown as scrolling.
+ .wrapContentSize(unbounded = true),
+ ) {
+ visiblePinEntries.forEachIndexed { index, entry ->
+ key(entry) {
+ val visibility = remember {
+ MutableTransitionState<EntryVisibility>(EntryVisibility.Hidden)
+ }
+ visibility.targetState =
+ when {
+ currentPinEntries.isEmpty() && visiblePinEntries.size > 1 ->
+ EntryVisibility.BulkHidden(index, visiblePinEntries.size)
+ currentPinEntries.contains(entry) -> EntryVisibility.Shown
+ else -> EntryVisibility.Hidden
+ }
+
+ ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry"))
+
+ LaunchedEffect(entry) {
+ // Remove entry from visiblePinEntries once the hide transition completed.
+ snapshotFlow {
+ visibility.currentState == visibility.targetState &&
+ visibility.targetState != EntryVisibility.Shown
+ }
+ .collect { isRemoved ->
+ if (isRemoved) {
+ visiblePinEntries.remove(entry)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+private sealed class EntryVisibility {
+ object Shown : EntryVisibility()
+
+ object Hidden : EntryVisibility()
+
+ /**
+ * Same as [Hidden], but applies when multiple entries are hidden simultaneously, without
+ * collapsing during the hide.
+ */
+ data class BulkHidden(val staggerIndex: Int, val totalEntryCount: Int) : EntryVisibility()
+}
+
+@Composable
+private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
+ // spec: http://shortn/_DEhE3Xl2bi
+ val shapePadding = 6.dp
+ val shapeOvershootSize = 22.dp
+ val dismissStaggerDelayMs = 33
+ val dismissDurationMs = 450
+ val expansionDurationMs = 250
+ val shapeExpandDurationMs = 83
+ val shapeRetractDurationMs = 167
+ val shapeCollapseDurationMs = 200
+
+ val animatedEntryWidth by
+ transition.animateDp(
+ transitionSpec = {
+ when (val target = targetState) {
+ is EntryVisibility.BulkHidden ->
+ // only collapse horizontal space once all entries are removed
+ snap(dismissDurationMs + dismissStaggerDelayMs * target.totalEntryCount)
+ else -> tween(expansionDurationMs, easing = Easings.Standard)
+ }
+ },
+ label = "entry space"
+ ) { state ->
+ if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp
+ }
+
+ val animatedShapeSize by
+ transition.animateDp(
+ transitionSpec = {
+ when {
+ EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown ->
+ keyframes {
+ durationMillis = shapeExpandDurationMs + shapeRetractDurationMs
+ 0.dp at 0 with Easings.Linear
+ shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy
+ }
+ targetState is EntryVisibility.BulkHidden -> {
+ val target = targetState as EntryVisibility.BulkHidden
+ tween(
+ dismissDurationMs,
+ delayMillis = target.staggerIndex * dismissStaggerDelayMs,
+ easing = Easings.Legacy,
+ )
+ }
+ else -> tween(shapeCollapseDurationMs, easing = Easings.StandardDecelerate)
+ }
+ },
+ label = "shape size"
+ ) { state ->
+ when (state) {
+ EntryVisibility.Shown -> entryShapeSize
+ else -> 0.dp
+ }
+ }
+
+ val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
+ Layout(
+ content = {
+ // TODO(b/282730134): add support for dot shapes.
+ Canvas(Modifier) { drawCircle(dotColor) }
+ }
+ ) { measurables, _ ->
+ val shapeSizePx = animatedShapeSize.roundToPx()
+ val placeable = measurables.single().measure(Constraints.fixed(shapeSizePx, shapeSizePx))
+
+ layout(animatedEntryWidth.roundToPx(), entryShapeSize.roundToPx()) {
+ placeable.place(
+ ((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(),
+ ((entryShapeSize - animatedShapeSize) / 2f).roundToPx()
+ )
+ }
+ }
+}
+
+@Composable
private fun PinDigit(
digit: Int,
contentColor: Color,
@@ -310,11 +428,13 @@
// TODO(b/282730134): implement.
}
+private val entryShapeSize = 16.dp
+
private val pinButtonSize = 84.dp
// Pin button motion spec: http://shortn/_9TTIG6SoEa
private val pinButtonPressedDuration = 100.milliseconds
-private val pinButtonPressedEasing = LinearEasing
+private val pinButtonPressedEasing = Easings.Linear
private val pinButtonHoldTime = 33.milliseconds
private val pinButtonReleasedDuration = 420.milliseconds
-private val pinButtonReleasedEasing = Easings.StandardEasing
+private val pinButtonReleasedEasing = Easings.Standard
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 7c76281..0e20444 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -45,6 +45,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
private val KEY_TIMESTAMP = "appliedTimestamp"
@@ -320,20 +321,20 @@
}
}
- public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
- scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
+ public suspend fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
+ withContext(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
}
var currentClockId: ClockId
get() = settings?.clockId ?: fallbackClockId
set(value) {
- mutateSetting { it.copy(clockId = value) }
+ scope.launch(bgDispatcher) { mutateSetting { it.copy(clockId = value) } }
}
var seedColor: Int?
get() = settings?.seedColor
set(value) {
- mutateSetting { it.copy(seedColor = value) }
+ scope.launch(bgDispatcher) { mutateSetting { it.copy(seedColor = value) } }
}
init {
@@ -501,11 +502,25 @@
fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
- fun registerClockChangeListener(listener: ClockChangeListener) =
+ /**
+ * Adds [listener] to receive future clock changes.
+ *
+ * Calling from main thread to make sure the access is thread safe.
+ */
+ fun registerClockChangeListener(listener: ClockChangeListener) {
+ assertMainThread()
clockChangeListeners.add(listener)
+ }
- fun unregisterClockChangeListener(listener: ClockChangeListener) =
+ /**
+ * Removes [listener] from future clock changes.
+ *
+ * Calling from main thread to make sure the access is thread safe.
+ */
+ fun unregisterClockChangeListener(listener: ClockChangeListener) {
+ assertMainThread()
clockChangeListeners.remove(listener)
+ }
fun createCurrentClock(): ClockController {
val clockId = currentClockId
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 96f720d..e557c8e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockSettings
+import com.android.systemui.plugins.WeatherData
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
@@ -227,6 +228,8 @@
clocks.forEach { it.refreshFormat() }
}
+
+ override fun onWeatherDataChanged(data: WeatherData) {}
}
open inner class DefaultClockAnimations(
@@ -273,6 +276,8 @@
// the top margin change in recomputePadding to make clock be centered
view.translationY = 0.5f * view.bottom * (1 - swipingFraction)
}
+
+ override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
}
inner class LargeClockAnimations(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
index 6f363a4..08ee602 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
@@ -20,8 +20,6 @@
object KeyguardPreviewConstants {
const val MESSAGE_ID_HIDE_SMART_SPACE = 1111
const val KEY_HIDE_SMART_SPACE = "hide_smart_space"
- const val MESSAGE_ID_COLOR_OVERRIDE = 1234
- const val KEY_COLOR_OVERRIDE = "color_override" // ColorInt Encoded as string
const val MESSAGE_ID_SLOT_SELECTED = 1337
const val KEY_SLOT_ID = "slot_id"
const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
index 21218a2..9f075e5 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
@@ -16,13 +16,15 @@
package com.android.systemui.monet.dynamiccolor;
+import android.annotation.NonNull;
+
import com.android.systemui.monet.dislike.DislikeAnalyzer;
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.hct.ViewingConditions;
import com.android.systemui.monet.scheme.DynamicScheme;
import com.android.systemui.monet.scheme.Variant;
-/** Named colors, otherwise known as tokens, or roles, in the Material Design system.*/
+/** Named colors, otherwise known as tokens, or roles, in the Material Design system. */
// Prevent lint for Function.apply not being available on Android before API level 14 (4.0.1).
// "AndroidJdkLibsChecker" for Function, "NewApi" for Function.apply().
// A java_library Bazel rule with an Android constraint cannot skip these warnings without this
@@ -32,54 +34,557 @@
public final class MaterialDynamicColors {
private static final double CONTAINER_ACCENT_TONE_DELTA = 15.0;
+ public MaterialDynamicColors() {}
- public MaterialDynamicColors() {
+ // Compatibility Keys Colors for Android
+ public DynamicColor primaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone());
+ }
+
+ public DynamicColor secondaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone());
+ }
+
+ public DynamicColor tertiaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone());
+ }
+
+ public DynamicColor neutralPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone());
+ }
+
+ public DynamicColor neutralVariantPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette,
+ (s) -> s.neutralVariantPalette.getKeyColor().getTone());
+ }
+
+ @NonNull
+ public DynamicColor highestSurface(@NonNull DynamicScheme s) {
+ return s.isDark ? surfaceBright() : surfaceDim();
+ }
+
+ @NonNull
+ public DynamicColor background() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
+ }
+
+ @NonNull
+ public DynamicColor onBackground() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background());
+ }
+
+ @NonNull
+ public DynamicColor surface() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
+ }
+
+ @NonNull
+ public DynamicColor inverseSurface() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 20.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceBright() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceDim() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceContainerLowest() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceContainerLow() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceContainer() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceContainerHigh() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceContainerHighest() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0);
+ }
+
+ @NonNull
+ public DynamicColor onSurface() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor inverseOnSurface() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> inverseSurface());
+ }
+
+ @NonNull
+ public DynamicColor surfaceVariant() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 90.0);
+ }
+
+ @NonNull
+ public DynamicColor onSurfaceVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0, (s) -> surfaceVariant());
+ }
+
+ @NonNull
+ public DynamicColor outline() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 60.0 : 50.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor outlineVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor shadow() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> 0.0);
+ }
+
+ @NonNull
+ public DynamicColor scrim() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> 0.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceTint() {
+ return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 80.0 : 40.0);
+ }
+
+ @NonNull
+ public DynamicColor primaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isFidelity(s)) {
+ return performAlbers(s.sourceColorHct, s);
+ }
+ if (isMonochrome(s)) {
+ return s.isDark ? 85.0 : 25.0;
+ }
+ return s.isDark ? 30.0 : 90.0;
+ },
+ this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onPrimaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isFidelity(s)) {
+ return DynamicColor.contrastingTone(primaryContainer().tone.apply(s), 4.5);
+ }
+ if (isMonochrome(s)) {
+ return s.isDark ? 0.0 : 100.0;
+ }
+ return s.isDark ? 90.0 : 10.0;
+ },
+ (s) -> primaryContainer(),
+ null);
+ }
+
+ @NonNull
+ public DynamicColor primary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 100.0 : 0.0;
+ }
+ return s.isDark ? 80.0 : 40.0;
+ },
+ this::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ primaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ @NonNull
+ public DynamicColor inversePrimary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> inverseSurface());
+ }
+
+ @NonNull
+ public DynamicColor onPrimary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 90.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> primary());
+ }
+
+ @NonNull
+ public DynamicColor secondaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 30.0 : 85.0;
+ }
+ final double initialTone = s.isDark ? 30.0 : 90.0;
+ if (!isFidelity(s)) {
+ return initialTone;
+ }
+ double answer =
+ findDesiredChromaByTone(
+ s.secondaryPalette.getHue(),
+ s.secondaryPalette.getChroma(),
+ initialTone,
+ !s.isDark);
+ answer = performAlbers(s.secondaryPalette.getHct(answer), s);
+ return answer;
+ },
+ this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onSecondaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (!isFidelity(s)) {
+ return s.isDark ? 90.0 : 10.0;
+ }
+ return DynamicColor.contrastingTone(secondaryContainer().tone.apply(s), 4.5);
+ },
+ (s) -> secondaryContainer());
+ }
+
+ @NonNull
+ public DynamicColor secondary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> s.isDark ? 80.0 : 40.0,
+ this::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ secondaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ @NonNull
+ public DynamicColor onSecondary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 100.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> secondary());
+ }
+
+ @NonNull
+ public DynamicColor tertiaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 60.0 : 49.0;
+ }
+ if (!isFidelity(s)) {
+ return s.isDark ? 30.0 : 90.0;
+ }
+ final double albersTone =
+ performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()), s);
+ final Hct proposedHct = s.tertiaryPalette.getHct(albersTone);
+ return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone();
+ },
+ this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onTertiaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 0.0 : 100.0;
+ }
+ if (!isFidelity(s)) {
+ return s.isDark ? 90.0 : 10.0;
+ }
+ return DynamicColor.contrastingTone(tertiaryContainer().tone.apply(s), 4.5);
+ },
+ (s) -> tertiaryContainer());
+ }
+
+ @NonNull
+ public DynamicColor tertiary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 90.0 : 25.0;
+ }
+ return s.isDark ? 80.0 : 40.0;
+ },
+ this::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ tertiaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ @NonNull
+ public DynamicColor onTertiary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 90.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> tertiary());
+ }
+
+ @NonNull
+ public DynamicColor errorContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onErrorContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer());
+ }
+
+ @NonNull
+ public DynamicColor error() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette,
+ (s) -> s.isDark ? 80.0 : 40.0,
+ this::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ errorContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ @NonNull
+ public DynamicColor onError() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> error());
+ }
+
+ @NonNull
+ public DynamicColor primaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return 40.0;
+ }
+ return 90.0;
+ },
+ this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor primaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return 30.0;
+ }
+ return 80.0;
+ },
+ this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onPrimaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return 100.0;
+ }
+ return 10.0;
+ },
+ (s) -> primaryFixedDim());
+ }
+
+ @NonNull
+ public DynamicColor onPrimaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return 90.0;
+ }
+ return 30.0;
+ },
+ (s) -> primaryFixedDim());
+ }
+
+ @NonNull
+ public DynamicColor secondaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor secondaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onSecondaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> 10.0, (s) -> secondaryFixedDim());
+ }
+
+ @NonNull
+ public DynamicColor onSecondaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> isMonochrome(s) ? 25.0 : 30.0,
+ (s) -> secondaryFixedDim());
+ }
+
+ @NonNull
+ public DynamicColor tertiaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor tertiaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor onTertiaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 100.0 : 10.0, (s) -> tertiaryFixedDim());
+ }
+
+ @NonNull
+ public DynamicColor onTertiaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 30.0, (s) -> tertiaryFixedDim());
}
/**
- * These colors were present in Android framework before Android U, and used by MDC controls.
- * They
+ * These colors were present in Android framework before Android U, and used by MDC controls. They
* should be avoided, if possible. It's unclear if they're used on multiple backgrounds, and if
* they are, they can't be adjusted for contrast.* For now, they will be set with no background,
* and those won't adjust for contrast, avoiding issues.
*
- * <p>* For example, if the same color is on a white background _and_ black background,
- * there's no
+ * <p>* For example, if the same color is on a white background _and_ black background, there's no
* way to increase contrast with either without losing contrast with the other.
*/
// colorControlActivated documented as colorAccent in M3 & GM3.
// colorAccent documented as colorSecondary in M3 and colorPrimary in GM3.
// Android used Material's Container as Primary/Secondary/Tertiary at launch.
// Therefore, this is a duplicated version of Primary Container.
- public static DynamicColor controlActivated() {
+ @NonNull
+ public DynamicColor controlActivated() {
return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null);
}
- // Compatibility Keys Colors for Android
- public static DynamicColor primaryPaletteKeyColor() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone());
+ // colorControlNormal documented as textColorSecondary in M3 & GM3.
+ // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state,
+ // which is Neutral Variant T30/80 in light/dark.
+ @NonNull
+ public DynamicColor controlNormal() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0);
}
- public static DynamicColor secondaryPaletteKeyColor() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone());
+ // colorControlHighlight documented, in both M3 & GM3:
+ // Light mode: #1f000000 dark mode: #33ffffff.
+ // These are black and white with some alpha.
+ // 1F hex = 31 decimal; 31 / 255 = 12% alpha.
+ // 33 hex = 51 decimal; 51 / 255 = 20% alpha.
+ // DynamicColors do not support alpha currently, and _may_ not need it for this use case,
+ // depending on how MDC resolved alpha for the other cases.
+ // Returning black in dark mode, white in light mode.
+ @NonNull
+ public DynamicColor controlHighlight() {
+ return new DynamicColor(
+ s -> 0.0,
+ s -> 0.0,
+ s -> s.isDark ? 100.0 : 0.0,
+ s -> s.isDark ? 0.20 : 0.12,
+ null,
+ scheme ->
+ DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
+ scheme ->
+ DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
+ null);
}
- public static DynamicColor tertiaryPaletteKeyColor() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone());
+ // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90.
+ @NonNull
+ public DynamicColor textPrimaryInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
- public static DynamicColor neutralPaletteKeyColor() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone());
+ // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as
+ // NV30/NV80
+ @NonNull
+ public DynamicColor textSecondaryAndTertiaryInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0);
}
- public static DynamicColor neutralVariantPaletteKeyColor() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette,
- (s) -> s.neutralVariantPalette.getKeyColor().getTone());
+ // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90
+ @NonNull
+ public DynamicColor textPrimaryInverseDisableOnly() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
+
+ // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented,
+ // in both M3 & GM3, as N10/N90
+ @NonNull
+ public DynamicColor textSecondaryAndTertiaryInverseDisabled() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
+
+ // textColorHintInverse documented, in both M3 & GM3, as N10/N90
+ @NonNull
+ public DynamicColor textHintInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
private static ViewingConditions viewingConditionsForAlbers(DynamicScheme scheme) {
@@ -132,457 +637,4 @@
return DynamicColor.enableLightForeground(albersd.getTone());
}
}
-
- public static DynamicColor highestSurface(DynamicScheme s) {
- return s.isDark ? surfaceBright() : surfaceDim();
- }
-
- public static DynamicColor background() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
- }
-
- public static DynamicColor onBackground() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background());
- }
-
- public static DynamicColor surface() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
- }
-
- public static DynamicColor inverseSurface() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 20.0);
- }
-
- public static DynamicColor surfaceBright() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0);
- }
-
- public static DynamicColor surfaceDim() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0);
- }
-
- public static DynamicColor surfaceContainerLowest() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0);
- }
-
- public static DynamicColor surfaceContainerLow() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0);
- }
-
- public static DynamicColor surfaceContainer() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0);
- }
-
- public static DynamicColor surfaceContainerHigh() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0);
- }
-
- public static DynamicColor surfaceContainerHighest() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0);
- }
-
- public static DynamicColor onSurface() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor inverseOnSurface() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> inverseSurface());
- }
-
- public static DynamicColor surfaceVariant() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 30.0 : 90.0);
- }
-
- public static DynamicColor onSurfaceVariant() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0,
- (s) -> surfaceVariant());
- }
-
- public static DynamicColor outline() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> 50.0, MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor outlineVariant() {
- return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor primaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isFidelity(s)) {
- return performAlbers(s.sourceColorHct, s);
- }
- if (isMonochrome(s)) {
- return s.isDark ? 85.0 : 25.0;
- }
- return s.isDark ? 30.0 : 90.0;
- },
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onPrimaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isFidelity(s)) {
- return DynamicColor.contrastingTone(primaryContainer().tone.apply(s), 4.5);
- }
- if (isMonochrome(s)) {
- return s.isDark ? 0.0 : 100.0;
- }
- return s.isDark ? 90.0 : 10.0;
- },
- (s) -> primaryContainer(),
- null);
- }
-
- public static DynamicColor primary() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 100.0 : 0.0;
- }
- return s.isDark ? 80.0 : 40.0;
- },
- MaterialDynamicColors::highestSurface,
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- primaryContainer(),
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
- }
-
- public static DynamicColor inversePrimary() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> inverseSurface());
- }
-
- public static DynamicColor onPrimary() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 10.0 : 90.0;
- }
- return s.isDark ? 20.0 : 100.0;
- },
- (s) -> primary());
- }
-
- public static DynamicColor secondaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 30.0 : 85.0;
- }
- final double initialTone = s.isDark ? 30.0 : 90.0;
- if (!isFidelity(s)) {
- return initialTone;
- }
- double answer =
- findDesiredChromaByTone(
- s.secondaryPalette.getHue(),
- s.secondaryPalette.getChroma(),
- initialTone,
- !s.isDark);
- answer = performAlbers(s.secondaryPalette.getHct(answer), s);
- return answer;
- },
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onSecondaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 90.0 : 10.0;
- }
- return DynamicColor.contrastingTone(secondaryContainer().tone.apply(s), 4.5);
- },
- (s) -> secondaryContainer());
- }
-
- public static DynamicColor secondary() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- MaterialDynamicColors::highestSurface,
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- secondaryContainer(),
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
- }
-
- public static DynamicColor onSecondary() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 10.0 : 100.0;
- }
- return s.isDark ? 20.0 : 100.0;
- },
- (s) -> secondary());
- }
-
- public static DynamicColor tertiaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 60.0 : 49.0;
- }
- if (!isFidelity(s)) {
- return s.isDark ? 30.0 : 90.0;
- }
- final double albersTone =
- performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()), s);
- final Hct proposedHct = s.tertiaryPalette.getHct(albersTone);
- return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone();
- },
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onTertiaryContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 0.0 : 100.0;
- }
- if (!isFidelity(s)) {
- return s.isDark ? 90.0 : 10.0;
- }
- return DynamicColor.contrastingTone(tertiaryContainer().tone.apply(s), 4.5);
- },
- (s) -> tertiaryContainer());
- }
-
- public static DynamicColor tertiary() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 90.0 : 25.0;
- }
- return s.isDark ? 80.0 : 40.0;
- },
- MaterialDynamicColors::highestSurface,
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- tertiaryContainer(),
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
- }
-
- public static DynamicColor onTertiary() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 10.0 : 90.0;
- }
- return s.isDark ? 20.0 : 100.0;
- },
- (s) -> tertiary());
- }
-
- public static DynamicColor errorContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onErrorContainer() {
- return DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer());
- }
-
- public static DynamicColor error() {
- return DynamicColor.fromPalette(
- (s) -> s.errorPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- MaterialDynamicColors::highestSurface,
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- errorContainer(),
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
- }
-
- public static DynamicColor onError() {
- return DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> error());
- }
-
- public static DynamicColor primaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 100.0 : 10.0;
- }
- return 90.0;
- },
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor primaryFixedDim() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 90.0 : 20.0;
- }
- return 80.0;
- },
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onPrimaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 10.0 : 90.0;
- }
- return 10.0;
- },
- (s) -> primaryFixedDim());
- }
-
- public static DynamicColor onPrimaryFixedVariant() {
- return DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (isMonochrome(s)) {
- return s.isDark ? 30.0 : 70.0;
- }
- return 30.0;
- },
- (s) -> primaryFixedDim());
- }
-
- public static DynamicColor secondaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor secondaryFixedDim() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onSecondaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> 10.0, (s) -> secondaryFixedDim());
- }
-
- public static DynamicColor onSecondaryFixedVariant() {
- return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> isMonochrome(s) ? 25.0 : 30.0,
- (s) -> secondaryFixedDim());
- }
-
- public static DynamicColor tertiaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor tertiaryFixedDim() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0,
- MaterialDynamicColors::highestSurface);
- }
-
- public static DynamicColor onTertiaryFixed() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 10.0,
- (s) -> tertiaryFixedDim());
- }
-
- public static DynamicColor onTertiaryFixedVariant() {
- return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 70.0 : 30.0,
- (s) -> tertiaryFixedDim());
- }
-
- // colorControlNormal documented as textColorSecondary in M3 & GM3.
- // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state,
- // which is Neutral Variant T30/80 in light/dark.
- public static DynamicColor controlNormal() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 80.0 : 30.0);
- }
-
- // colorControlHighlight documented, in both M3 & GM3:
- // Light mode: #1f000000 dark mode: #33ffffff.
- // These are black and white with some alpha.
- // 1F hex = 31 decimal; 31 / 255 = 12% alpha.
- // 33 hex = 51 decimal; 51 / 255 = 20% alpha.
- // DynamicColors do not support alpha currently, and _may_ not need it for this use case,
- // depending on how MDC resolved alpha for the other cases.
- // Returning black in dark mode, white in light mode.
- public static DynamicColor controlHighlight() {
- return new DynamicColor(
- s -> 0.0,
- s -> 0.0,
- s -> s.isDark ? 100.0 : 0.0,
- s -> s.isDark ? 0.20 : 0.12,
- null,
- scheme ->
-
- DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
- scheme, null),
- scheme ->
- DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
- scheme, null),
- null);
- }
-
- // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90.
- public static DynamicColor textPrimaryInverse() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
- }
-
- // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as
- // NV30/NV80
- public static DynamicColor textSecondaryAndTertiaryInverse() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 30.0 : 80.0);
- }
-
- // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90
- public static DynamicColor textPrimaryInverseDisableOnly() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
- }
-
- // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented,
- // in both M3 & GM3, as N10/N90
- public static DynamicColor textSecondaryAndTertiaryInverseDisabled() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
- }
-
- // textColorHintInverse documented, in both M3 & GM3, as N10/N90
- public static DynamicColor textHintInverse() {
- return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
- }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 5d0a3af..3ae328e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -74,18 +74,10 @@
resources: Resources,
dozeFraction: Float,
foldFraction: Float,
- ) {
- events.onColorPaletteChanged(resources)
- smallClock.animations.doze(dozeFraction)
- largeClock.animations.doze(dozeFraction)
- smallClock.animations.fold(foldFraction)
- largeClock.animations.fold(foldFraction)
- smallClock.events.onTimeTick()
- largeClock.events.onTimeTick()
- }
+ )
/** Optional method for dumping debug information */
- fun dump(pw: PrintWriter) {}
+ fun dump(pw: PrintWriter)
}
/** Interface for a specific clock face version rendered by the clock */
@@ -109,37 +101,37 @@
/** Events that should call when various rendering parameters change */
interface ClockEvents {
/** Call whenever timezone changes */
- fun onTimeZoneChanged(timeZone: TimeZone) {}
+ fun onTimeZoneChanged(timeZone: TimeZone)
/** Call whenever the text time format changes (12hr vs 24hr) */
- fun onTimeFormatChanged(is24Hr: Boolean) {}
+ fun onTimeFormatChanged(is24Hr: Boolean)
/** Call whenever the locale changes */
- fun onLocaleChanged(locale: Locale) {}
+ fun onLocaleChanged(locale: Locale)
/** Call whenever the color palette should update */
- fun onColorPaletteChanged(resources: Resources) {}
+ fun onColorPaletteChanged(resources: Resources)
/** Call if the seed color has changed and should be updated */
- fun onSeedColorChanged(seedColor: Int?) {}
+ fun onSeedColorChanged(seedColor: Int?)
/** Call whenever the weather data should update */
- fun onWeatherDataChanged(data: WeatherData) {}
+ fun onWeatherDataChanged(data: WeatherData)
}
/** Methods which trigger various clock animations */
interface ClockAnimations {
/** Runs an enter animation (if any) */
- fun enter() {}
+ fun enter()
/** Sets how far into AOD the device currently is. */
- fun doze(fraction: Float) {}
+ fun doze(fraction: Float)
/** Sets how far into the folding animation the device is. */
- fun fold(fraction: Float) {}
+ fun fold(fraction: Float)
/** Runs the battery animation (if any). */
- fun charge() {}
+ fun charge()
/**
* Runs when the clock's position changed during the move animation.
@@ -150,32 +142,32 @@
* @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means
* it finished moving.
*/
- fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
+ fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float)
/**
* Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview,
* 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize
*/
- fun onPickerCarouselSwiping(swipingFraction: Float) {}
+ fun onPickerCarouselSwiping(swipingFraction: Float)
}
/** Events that have specific data about the related face */
interface ClockFaceEvents {
/** Call every time tick */
- fun onTimeTick() {}
+ fun onTimeTick()
/**
* Region Darkness specific to the clock face.
* - isRegionDark = dark theme -> clock should be light
* - !isRegionDark = light theme -> clock should be dark
*/
- fun onRegionDarknessChanged(isRegionDark: Boolean) {}
+ fun onRegionDarknessChanged(isRegionDark: Boolean)
/**
* Call whenever font settings change. Pass in a target font size in pixels. The specific clock
* design is allowed to ignore this target size on a case-by-case basis.
*/
- fun onFontSettingChanged(fontSizePx: Float) {}
+ fun onFontSettingChanged(fontSizePx: Float)
/**
* Target region information for the clock face. For small clock, this will match the bounds of
@@ -184,7 +176,7 @@
* render within the centered targetRect to avoid obstructing other elements. The specified
* targetRegion is relative to the parent view.
*/
- fun onTargetRegionChanged(targetRegion: Rect?) {}
+ fun onTargetRegionChanged(targetRegion: Rect?)
}
/** Tick rates for clocks */
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 02d5510..1f47e72 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -64,15 +64,16 @@
# The plugins, log & common subpackages act as shared libraries that might be referenced in
# dynamically-loaded plugin APKs.
--keep class com.android.systemui.plugins.** {
- *;
-}
--keep class com.android.systemui.log.** {
- *;
-}
--keep class com.android.systemui.common.** {
- *;
-}
+-keep class com.android.systemui.plugins.** { *; }
+-keep class com.android.systemui.log.ConstantStringsLoggerImpl { *; }
+-keep class com.android.systemui.log.ConstantStringsLogger { *; }
+-keep class com.android.systemui.log.LogBuffer { *; }
+-keep class com.android.systemui.log.LogcatEchoTrackerDebug { *; }
+-keep class com.android.systemui.log.LogcatEchoTracker { *; }
+-keep class com.android.systemui.log.LogcatEchoTrackerProd { *; }
+-keep class com.android.systemui.log.LogLevel { *; }
+-keep class com.android.systemui.log.LogMessageImpl { *; }
+-keep class com.android.systemui.log.LogMessage { *; }
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
*;
}
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index a3a7135..66c57fc 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -23,7 +23,7 @@
android:outlineProvider="none" >
<LinearLayout
- android:id="@+id/keyguard_indication_area"
+ android:id="@id/keyguard_indication_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
@@ -31,7 +31,7 @@
android:orientation="vertical">
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
+ android:id="@id/keyguard_indication_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@@ -41,13 +41,12 @@
android:accessibilityLiveRegion="polite"/>
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text_bottom"
+ android:id="@id/keyguard_indication_text_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:minHeight="48dp"
+ android:minHeight="@dimen/keyguard_indication_text_min_height"
android:layout_gravity="center_horizontal"
- android:layout_centerHorizontal="true"
android:paddingStart="@dimen/keyguard_indication_text_padding"
android:paddingEnd="@dimen/keyguard_indication_text_padding"
android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 01465d7..6601e63 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -70,6 +70,12 @@
android:layout_height="match_parent"
android:visibility="invisible" />
+ <!-- Root for all keyguard content. It was previously located within the shade. -->
+ <com.android.systemui.keyguard.ui.view.KeyguardRootView
+ android:id="@id/keyguard_root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<include layout="@layout/brightness_mirror_container" />
<com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8d3ba36..50d33c6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -275,6 +275,9 @@
<!-- The padding at start and end of indication text shown on AOD -->
<dimen name="keyguard_indication_text_padding">16dp</dimen>
+ <!-- The min height on the indication text shown on AOD -->
+ <dimen name="keyguard_indication_text_min_height">48dp</dimen>
+
<!-- Shadows under the clock, date and other keyguard text fields -->
<dimen name="keyguard_shadow_radius">5</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e5c9461..d651a21 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -206,5 +206,9 @@
<!-- keyboard backlight indicator-->
<item type="id" name="backlight_icon" />
-</resources>
+ <item type="id" name="keyguard_root_view" />
+ <item type="id" name="keyguard_indication_area" />
+ <item type="id" name="keyguard_indication_text" />
+ <item type="id" name="keyguard_indication_text_bottom" />
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index 9a00447..0ca2f7a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -263,6 +263,8 @@
(colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
WallpaperColors.HINT_SUPPORTS_DARK_TEXT
)
+ if (DEBUG)
+ Log.d(TAG, "onColorsChanged() | region darkness = $regionDarkness for region $area")
updateForegroundColor()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 8ea4c31a..84a2c25 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,18 +15,18 @@
*/
package com.android.keyguard
-import android.app.WallpaperManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.text.format.DateFormat
-import android.util.TypedValue
import android.util.Log
+import android.util.TypedValue
import android.view.View
import android.view.View.OnAttachStateChangeListener
import android.view.ViewTreeObserver
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -99,6 +99,28 @@
if (!regionSamplingEnabled) {
updateColors()
+ } else {
+ clock?.let {
+ smallRegionSampler = createRegionSampler(
+ it.smallClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )?.apply { startRegionSampler() }
+
+ largeRegionSampler = createRegionSampler(
+ it.largeClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )?.apply { startRegionSampler() }
+
+ updateColors()
+ }
}
updateFontSizes()
updateTimeListeners()
@@ -110,8 +132,25 @@
}
value.smallClock.view.addOnAttachStateChangeListener(
object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(p0: View?) {
+ var pastVisibility: Int? = null
+ override fun onViewAttachedToWindow(view: View?) {
value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ if (view != null) {
+ val smallClockFrame = view.parent as FrameLayout
+ pastVisibility = smallClockFrame.visibility
+ smallClockFrame.viewTreeObserver.addOnGlobalLayoutListener(
+ ViewTreeObserver.OnGlobalLayoutListener {
+ val currentVisibility = smallClockFrame.visibility
+ if (pastVisibility != currentVisibility) {
+ pastVisibility = currentVisibility
+ // when small clock visible, recalculate bounds and sample
+ if (currentVisibility == View.VISIBLE) {
+ smallRegionSampler?.stopRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ }
+ }
+ })
+ }
}
override fun onViewDetachedFromWindow(p0: View?) {
@@ -141,21 +180,19 @@
private fun updateColors() {
- val wallpaperManager = WallpaperManager.getInstance(context)
if (regionSamplingEnabled) {
- regionSampler?.let { regionSampler ->
- clock?.let { clock ->
- if (regionSampler.sampledView == clock.smallClock.view) {
- smallClockIsDark = regionSampler.currentRegionDarkness().isDark
- clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
- return@updateColors
- } else if (regionSampler.sampledView == clock.largeClock.view) {
- largeClockIsDark = regionSampler.currentRegionDarkness().isDark
- clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
- return@updateColors
- }
+ clock?.let { clock ->
+ smallRegionSampler?.let {
+ smallClockIsDark = it.currentRegionDarkness().isDark
+ clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+ }
+
+ largeRegionSampler?.let {
+ largeClockIsDark = it.currentRegionDarkness().isDark
+ clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
}
}
+ return
}
val isLightTheme = TypedValue()
@@ -168,23 +205,6 @@
largeClock.events.onRegionDarknessChanged(largeClockIsDark)
}
}
-
- private fun updateRegionSampler(sampledRegion: View) {
- regionSampler?.stopRegionSampler()
- regionSampler =
- createRegionSampler(
- sampledRegion,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )
- ?.apply { startRegionSampler() }
-
- updateColors()
- }
-
protected open fun createRegionSampler(
sampledView: View,
mainExecutor: Executor?,
@@ -202,7 +222,10 @@
) { updateColors() }
}
- var regionSampler: RegionSampler? = null
+ var smallRegionSampler: RegionSampler? = null
+ private set
+ var largeRegionSampler: RegionSampler? = null
+ private set
var smallTimeListener: TimeListener? = null
var largeTimeListener: TimeListener? = null
val shouldTimeListenerRun: Boolean
@@ -319,7 +342,8 @@
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
- regionSampler?.stopRegionSampler()
+ smallRegionSampler?.stopRegionSampler()
+ largeRegionSampler?.stopRegionSampler()
smallTimeListener?.stop()
largeTimeListener?.stop()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 8f17edd..25ad3af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -531,9 +531,13 @@
if (clock != null) {
clock.dump(pw);
}
- final RegionSampler regionSampler = mClockEventController.getRegionSampler();
- if (regionSampler != null) {
- regionSampler.dump(pw);
+ final RegionSampler smallRegionSampler = mClockEventController.getSmallRegionSampler();
+ if (smallRegionSampler != null) {
+ smallRegionSampler.dump(pw);
+ }
+ final RegionSampler largeRegionSampler = mClockEventController.getLargeRegionSampler();
+ if (largeRegionSampler != null) {
+ largeRegionSampler.dump(pw);
}
}
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 c684dc5..c4ebee2 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
@@ -77,7 +77,7 @@
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
private val _authenticationMethod =
- MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.PIN(1234))
+ MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.Pin(1234))
override val authenticationMethod: StateFlow<AuthenticationMethodModel> =
_authenticationMethod.asStateFlow()
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 3984627..dd9dcbe 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
@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
+import android.app.admin.DevicePolicyManager
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
@@ -129,7 +130,7 @@
fun authenticate(input: List<Any>): Boolean {
val isSuccessful =
when (val authMethod = this.authenticationMethod.value) {
- is AuthenticationMethodModel.PIN -> input.asCode() == authMethod.code
+ is AuthenticationMethodModel.Pin -> input.asCode() == authMethod.code
is AuthenticationMethodModel.Password -> input.asPassword() == authMethod.password
is AuthenticationMethodModel.Pattern -> input.asPattern() == authMethod.coordinates
else -> true
@@ -177,15 +178,21 @@
/**
* Returns a PIN code from the given list. It's assumed the given list elements are all
- * [Int].
+ * [Int] in the range [0-9].
*/
- private fun List<Any>.asCode(): Int? {
- if (isEmpty()) {
+ private fun List<Any>.asCode(): Long? {
+ if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) {
return null
}
- var code = 0
- map { it as Int }.forEach { integer -> code = code * 10 + integer }
+ var code = 0L
+ map {
+ require(it is Int && it in 0..9) {
+ "Pin is required to be Int in range [0..9], but got $it"
+ }
+ it
+ }
+ .forEach { integer -> code = code * 10 + integer }
return code
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 6f008c3..e4fbf9a 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -32,7 +32,13 @@
/** The most basic authentication method. The lock screen can be swiped away when displayed. */
object Swipe : AuthenticationMethodModel(isSecure = false)
- data class PIN(val code: Int) : AuthenticationMethodModel(isSecure = true)
+ /**
+ * Authentication method using a PIN.
+ *
+ * In practice, a pin is restricted to 16 decimal digits , see
+ * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH]
+ */
+ data class Pin(val code: Long) : AuthenticationMethodModel(isSecure = true)
data class Password(val password: String) : AuthenticationMethodModel(isSecure = true)
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 1d2fce7..a24a421 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
@@ -197,7 +197,7 @@
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.PIN ->
+ is AuthenticationMethodModel.Pin ->
applicationContext.getString(R.string.keyguard_enter_your_pin)
is AuthenticationMethodModel.Password ->
applicationContext.getString(R.string.keyguard_enter_your_password)
@@ -209,7 +209,7 @@
private fun errorMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.PIN -> applicationContext.getString(R.string.kg_wrong_pin)
+ is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.kg_wrong_pin)
is AuthenticationMethodModel.Password ->
applicationContext.getString(R.string.kg_wrong_password)
is AuthenticationMethodModel.Pattern ->
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 984d9ab..527fe6e 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -126,7 +126,7 @@
.map { model ->
model?.let {
when (interactor.authenticationMethod.value) {
- is AuthenticationMethodModel.PIN ->
+ is AuthenticationMethodModel.Pin ->
R.string.kg_too_many_failed_pin_attempts_dialog_message
is AuthenticationMethodModel.Password ->
R.string.kg_too_many_failed_password_attempts_dialog_message
@@ -165,7 +165,7 @@
authMethod: AuthenticationMethodModel,
): AuthMethodBouncerViewModel? {
return when (authMethod) {
- is AuthenticationMethodModel.PIN -> pin
+ is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 5c0fd92..94d3d19 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -16,18 +16,10 @@
package com.android.systemui.bouncer.ui.viewmodel
-import androidx.annotation.VisibleForTesting
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.util.kotlin.pairwise
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
@@ -39,21 +31,8 @@
isInputEnabled = isInputEnabled,
) {
- private val entered = MutableStateFlow<List<Int>>(emptyList())
- /**
- * The length of the PIN digits that were input so far, two values are supplied the previous and
- * the current.
- */
- val pinLengths: StateFlow<Pair<Int, Int>> =
- entered
- .pairwise()
- .map { it.previousValue.size to it.newValue.size }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = 0 to 0,
- )
- private var resetPinJob: Job? = null
+ private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
+ val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries
/** Notifies that the UI has been shown to the user. */
fun onShown() {
@@ -62,47 +41,48 @@
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
- resetPinJob?.cancel()
- resetPinJob = null
-
- if (entered.value.isEmpty()) {
+ if (mutablePinEntries.value.isEmpty()) {
interactor.clearMessage()
}
- entered.value += input
+ mutablePinEntries.value += EnteredKey(input)
}
/** Notifies that the user clicked the backspace button. */
fun onBackspaceButtonClicked() {
- if (entered.value.isEmpty()) {
+ if (mutablePinEntries.value.isEmpty()) {
return
}
-
- entered.value = entered.value.toMutableList().apply { removeLast() }
+ mutablePinEntries.value = mutablePinEntries.value.toMutableList().apply { removeLast() }
}
/** Notifies that the user long-pressed the backspace button. */
fun onBackspaceButtonLongPressed() {
- resetPinJob?.cancel()
- resetPinJob =
- applicationScope.launch {
- while (entered.value.isNotEmpty()) {
- onBackspaceButtonClicked()
- delay(BACKSPACE_LONG_PRESS_DELAY_MS)
- }
- }
+ mutablePinEntries.value = emptyList()
}
/** Notifies that the user clicked the "enter" button. */
fun onAuthenticateButtonClicked() {
- if (!interactor.authenticate(entered.value)) {
+ if (!interactor.authenticate(mutablePinEntries.value.map { it.input })) {
showFailureAnimation()
}
- entered.value = emptyList()
+ mutablePinEntries.value = emptyList()
}
+}
- companion object {
- @VisibleForTesting const val BACKSPACE_LONG_PRESS_DELAY_MS = 80L
- }
+private var nextSequenceNumber = 1
+
+/**
+ * The pin bouncer [input] as digits 0-9, together with a [sequenceNumber] to indicate the ordering.
+ *
+ * Since the model only allows appending/removing [EnteredKey]s from the end, the [sequenceNumber]
+ * is strictly increasing in input order of the pin, but not guaranteed to be monotonic or start at
+ * a specific number.
+ */
+data class EnteredKey
+internal constructor(val input: Int, val sequenceNumber: Int = nextSequenceNumber++) :
+ Comparable<EnteredKey> {
+ override fun compareTo(other: EnteredKey): Int =
+ compareValuesBy(this, other, EnteredKey::sequenceNumber)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index e600632..fb19ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -217,7 +217,6 @@
)
}
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
- animateExitAndFinish()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 7cbd1f5..be50a14 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -162,7 +162,11 @@
broadcastSender.closeSystemDialogs()
// not sent as interactive, lest the higher-importance activity launch
// be impacted
- pendingIntent.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pendingIntent.send(options)
false
}
if (keyguardStateController.isUnlocked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
index 116f3ca..84cda5a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.app.ActivityOptions
import android.app.AlertDialog
import android.app.PendingIntent
import android.content.DialogInterface
@@ -74,7 +75,11 @@
R.string.controls_open_app,
DialogInterface.OnClickListener { dialog, _ ->
try {
- cws.control?.getAppIntent()?.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ cws.control?.getAppIntent()?.send(options)
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
} catch (e: PendingIntent.CanceledException) {
cvh.setErrorStatus()
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 25634f0..76002d3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
import com.android.systemui.log.SessionTracker
import com.android.systemui.media.RingtonePlayer
@@ -295,4 +296,9 @@
@IntoMap
@ClassKey(AssistantAttentionMonitor::class)
abstract fun bindAssistantAttentionMonitor(sysui: AssistantAttentionMonitor): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardViewConfigurator::class)
+ abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 870fff7..432ec8a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -134,7 +134,7 @@
// TODO(b/275694445): Tracking Bug
@JvmField
- val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208,
+ val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208,
"lockscreen_without_secure_lock_when_dreaming")
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
new file mode 100644
index 0000000..05c23ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.keyguard
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.CoreStartable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.statusbar.KeyguardIndicationController
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+/** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
+@SysUISingleton
+class KeyguardViewConfigurator
+@Inject
+constructor(
+ private val keyguardRootView: KeyguardRootView,
+ private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ private val notificationShadeWindowView: NotificationShadeWindowView,
+ private val featureFlags: FeatureFlags,
+ private val indicationController: KeyguardIndicationController,
+) : CoreStartable {
+
+ private var indicationAreaHandle: DisposableHandle? = null
+
+ override fun start() {
+ bindIndicationArea(
+ notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
+ )
+ }
+
+ fun bindIndicationArea(legacyParent: ViewGroup) {
+ indicationAreaHandle?.dispose()
+
+ // At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
+ // Disable one of them
+ if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
+ legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let {
+ legacyParent.removeView(it)
+ }
+ } else {
+ keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let {
+ keyguardRootView.removeView(it)
+ }
+ }
+
+ indicationAreaHandle =
+ KeyguardIndicationAreaBinder.bind(
+ notificationShadeWindowView,
+ keyguardIndicationAreaViewModel,
+ indicationController
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 641e20b..16ad29a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -21,12 +21,17 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.plugins.ClockId
+import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withContext
@@ -35,6 +40,7 @@
@Inject
constructor(
private val secureSettings: SecureSettings,
+ private val clockRegistry: ClockRegistry,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
@@ -47,6 +53,24 @@
.onStart { emit(Unit) } // Forces an initial update.
.map { getClockSize() }
+ val currentClockId: Flow<ClockId> =
+ callbackFlow {
+ fun send() {
+ trySend(clockRegistry.currentClockId)
+ }
+
+ val listener =
+ object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() {
+ send()
+ }
+ }
+ clockRegistry.registerClockChangeListener(listener)
+ send()
+ awaitClose { clockRegistry.unregisterClockChangeListener(listener) }
+ }
+ .mapNotNull { it }
+
private suspend fun getClockSize(): SettingsClockSize {
return withContext(backgroundDispatcher) {
if (
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index 98f445c..dad5831 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.plugins.ClockId
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -31,4 +32,6 @@
repository: KeyguardClockRepository,
) {
val selectedClockSize: Flow<SettingsClockSize> = repository.selectedClockSize
+
+ val currentClockId: Flow<ClockId> = repository.currentClockId
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 3cf9a9e..f99b8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -21,6 +21,7 @@
import android.graphics.Point
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -59,6 +60,7 @@
private val commandQueue: CommandQueue,
featureFlags: FeatureFlags,
bouncerRepository: KeyguardBouncerRepository,
+ configurationRepository: ConfigurationRepository,
) {
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -172,6 +174,9 @@
/** The approximate location on the screen of the face unlock sensor, if one is available. */
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+ /** Notifies when a new configuration is set */
+ val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
+
fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { states.contains(it.to) }
}
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 a8d662c..7d14198 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
@@ -21,12 +21,10 @@
import android.graphics.Rect
import android.graphics.drawable.Animatable2
import android.util.Size
-import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.ImageView
-import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -108,14 +106,10 @@
activityStarter: ActivityStarter?,
messageDisplayer: (Int) -> Unit,
): Binding {
- val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area)
val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container)
val startButton: ImageView = view.requireViewById(R.id.start_button)
val endButton: ImageView = view.requireViewById(R.id.end_button)
val overlayContainer: View = view.requireViewById(R.id.overlay_container)
- val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text)
- val indicationTextBottom: TextView =
- view.requireViewById(R.id.keyguard_indication_text_bottom)
val settingsMenu: LaunchableLinearLayout =
view.requireViewById(R.id.keyguard_settings_button)
@@ -183,7 +177,6 @@
}
ambientIndicationArea?.alpha = alpha
- indicationArea.alpha = alpha
}
}
@@ -205,50 +198,23 @@
launch {
viewModel.indicationAreaTranslationX.collect { translationX ->
- indicationArea.translationX = translationX
ambientIndicationArea?.translationX = translationX
}
}
launch {
- combine(
- viewModel.isIndicationAreaPadded,
- configurationBasedDimensions.map { it.indicationAreaPaddingPx },
- ) { isPadded, paddingIfPaddedPx ->
- if (isPadded) {
- paddingIfPaddedPx
- } else {
- 0
- }
- }
- .collect { paddingPx ->
- indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
- }
- }
-
- launch {
configurationBasedDimensions
.map { it.defaultBurnInPreventionYOffsetPx }
.flatMapLatest { defaultBurnInOffsetY ->
viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
}
.collect { translationY ->
- indicationArea.translationY = translationY
ambientIndicationArea?.translationY = translationY
}
}
launch {
configurationBasedDimensions.collect { dimensions ->
- indicationText.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- dimensions.indicationTextSizePx.toFloat(),
- )
- indicationTextBottom.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- dimensions.indicationTextSizePx.toFloat(),
- )
-
startButton.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
height = dimensions.buttonSizePx.height
@@ -305,7 +271,7 @@
return object : Binding {
override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
- return listOf(indicationArea, ambientIndicationArea).mapNotNull { it?.animate() }
+ return listOf(ambientIndicationArea).mapNotNull { it?.animate() }
}
override fun onConfigurationChanged() {
@@ -517,12 +483,6 @@
return ConfigurationBasedDimensions(
defaultBurnInPreventionYOffsetPx =
view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
- indicationAreaPaddingPx =
- view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
- indicationTextSizePx =
- view.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material,
- ),
buttonSizePx =
Size(
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
@@ -552,8 +512,6 @@
private data class ConfigurationBasedDimensions(
val defaultBurnInPreventionYOffsetPx: Int,
- val indicationAreaPaddingPx: Int,
- val indicationTextSizePx: Int,
val buttonSizePx: Size,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
new file mode 100644
index 0000000..02e6765
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.KeyguardIndicationController
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Binds a keyguard indication area view to its view-model.
+ *
+ * To use this properly, users should maintain a one-to-one relationship between the [View] and the
+ * view-binding, binding each view only once. It is okay and expected for the same instance of the
+ * view-model to be reused for multiple view/view-binder bindings.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+object KeyguardIndicationAreaBinder {
+
+ /** Binds the view to the view-model, continuing to update the former based on the latter. */
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: KeyguardIndicationAreaViewModel,
+ indicationController: KeyguardIndicationController,
+ ): DisposableHandle {
+ val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area)
+ indicationController.setIndicationArea(indicationArea)
+
+ val indicationText: TextView = indicationArea.requireViewById(R.id.keyguard_indication_text)
+ val indicationTextBottom: TextView =
+ indicationArea.requireViewById(R.id.keyguard_indication_text_bottom)
+
+ view.clipChildren = false
+ view.clipToPadding = false
+
+ val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.alpha.collect { alpha ->
+ view.importantForAccessibility =
+ if (alpha == 0f) {
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ } else {
+ View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ }
+
+ indicationArea.alpha = alpha
+ }
+ }
+
+ launch {
+ viewModel.indicationAreaTranslationX.collect { translationX ->
+ indicationArea.translationX = translationX
+ }
+ }
+
+ launch {
+ combine(
+ viewModel.isIndicationAreaPadded,
+ configurationBasedDimensions.map { it.indicationAreaPaddingPx },
+ ) { isPadded, paddingIfPaddedPx ->
+ if (isPadded) {
+ paddingIfPaddedPx
+ } else {
+ 0
+ }
+ }
+ .collect { paddingPx ->
+ indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
+ }
+ }
+
+ launch {
+ configurationBasedDimensions
+ .map { it.defaultBurnInPreventionYOffsetPx }
+ .flatMapLatest { defaultBurnInOffsetY ->
+ viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
+ }
+ .collect { translationY -> indicationArea.translationY = translationY }
+ }
+
+ launch {
+ configurationBasedDimensions.collect { dimensions ->
+ indicationText.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ dimensions.indicationTextSizePx.toFloat(),
+ )
+ indicationTextBottom.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ dimensions.indicationTextSizePx.toFloat(),
+ )
+ }
+ }
+
+ launch {
+ viewModel.configurationChange.collect {
+ configurationBasedDimensions.value = loadFromResources(view)
+ }
+ }
+ }
+ }
+ return disposableHandle
+ }
+
+ private fun loadFromResources(view: View): ConfigurationBasedDimensions {
+ return ConfigurationBasedDimensions(
+ defaultBurnInPreventionYOffsetPx =
+ view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
+ indicationAreaPaddingPx =
+ view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
+ indicationTextSizePx =
+ view.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.text_size_small_material,
+ ),
+ )
+ }
+
+ private data class ConfigurationBasedDimensions(
+ val defaultBurnInPreventionYOffsetPx: Int,
+ val indicationAreaPaddingPx: Int,
+ val indicationTextSizePx: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index 387e9a6..f5e4c6a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -18,10 +18,12 @@
package com.android.systemui.keyguard.ui.binder
import android.view.View
+import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
/** Binder for the small clock view, large clock view and smartspace. */
object KeyguardPreviewSmartspaceViewBinder {
@@ -31,10 +33,11 @@
smartspace: View,
viewModel: KeyguardPreviewSmartspaceViewModel,
) {
-
smartspace.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.smartSpaceTopPadding.collect { smartspace.setTopPadding(it) }
+ launch { viewModel.smartspaceTopPadding.collect { smartspace.setTopPadding(it) } }
+
+ launch { viewModel.shouldHideSmartspace.collect { smartspace.isInvisible = it } }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index f9a8b98..db23109 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.preview
-import android.annotation.ColorInt
+import android.app.WallpaperColors
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
+import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
@@ -91,6 +92,7 @@
/** [shouldHideClock] here means that we never create and bind the clock views */
private val shouldHideClock: Boolean =
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
+ private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS)
private var host: SurfaceControlViewHost
@@ -100,7 +102,6 @@
private lateinit var largeClockHostView: FrameLayout
private lateinit var smallClockHostView: FrameLayout
private var smartSpaceView: View? = null
- private var colorOverride: Int? = null
private val disposables = mutableSetOf<DisposableHandle>()
private var isDestroyed = false
@@ -172,6 +173,10 @@
rootView.translationX = (width - scale * rootView.width) / 2
rootView.translationY = (height - scale * rootView.height) / 2
+ if (isDestroyed) {
+ return@post
+ }
+
host.setView(rootView, rootView.measuredWidth, rootView.measuredHeight)
}
}
@@ -195,14 +200,6 @@
mainHandler.post { smartSpaceView?.visibility = if (hide) View.INVISIBLE else View.VISIBLE }
}
- /** Sets the clock's color to the overridden seed color. */
- fun onColorOverridden(@ColorInt color: Int?) {
- mainHandler.post {
- colorOverride = color
- clockController.clock?.run { events.onSeedColorChanged(color) }
- }
- }
-
/**
* This sets up and shows a non-interactive smart space
*
@@ -240,7 +237,7 @@
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
it.isClickable = false
-
+ it.isInvisible = true
parentView.addView(
it,
FrameLayout.LayoutParams(
@@ -395,13 +392,27 @@
val clock = clockRegistry.createCurrentClock()
clockController.clock = clock
- colorOverride?.let { clock.events.onSeedColorChanged(it) }
+ if (clockRegistry.seedColor == null) {
+ // Seed color null means users do override any color on the clock. The default color
+ // will need to use wallpaper's extracted color and consider if the wallpaper's color
+ // is dark or a light.
+ // TODO(b/277832214) we can potentially simplify this code by checking for
+ // wallpaperColors being null in the if clause above and removing the many ?.
+ val wallpaperColorScheme =
+ wallpaperColors?.let { ColorScheme(it, /* darkTheme= */ false) }
+ val lightClockColor = wallpaperColorScheme?.accent1?.s100
+ val darkClockColor = wallpaperColorScheme?.accent2?.s600
+ /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
+ val isWallpaperDark: Boolean =
+ (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
+ WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+ clock.events.onSeedColorChanged(
+ if (isWallpaperDark) lightClockColor else darkClockColor
+ )
+ }
updateLargeClock(clock)
updateSmallClock(clock)
-
- // Hide smart space if the clock has weather display; otherwise show it
- hideSmartspace(clock.largeClock.config.hasCustomWeatherDataDisplay)
}
private fun updateLargeClock(clock: ClockController) {
@@ -431,6 +442,7 @@
private const val KEY_VIEW_WIDTH = "width"
private const val KEY_VIEW_HEIGHT = "height"
private const val KEY_DISPLAY_ID = "display_id"
+ private const val KEY_COLORS = "wallpaper_colors"
private const val DIM_ALPHA = 0.3f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 79712f9c..dafeace 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -124,13 +124,6 @@
message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
)
}
- KeyguardPreviewConstants.MESSAGE_ID_COLOR_OVERRIDE -> {
- renderer.onColorOverridden(
- message.data
- .getString(KeyguardPreviewConstants.KEY_COLOR_OVERRIDE)
- ?.toIntOrNull()
- )
- }
else -> requestDestruction(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
new file mode 100644
index 0000000..890d565
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.keyguard.ui.view
+
+import android.content.Context
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+
+class KeyguardIndicationArea(
+ context: Context,
+ private val attrs: AttributeSet?,
+) :
+ LinearLayout(
+ context,
+ attrs,
+ ) {
+
+ init {
+ setId(R.id.keyguard_indication_area)
+ orientation = LinearLayout.VERTICAL
+
+ addView(indicationTopRow(), LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
+ addView(
+ indicationBottomRow(),
+ LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
+ gravity = Gravity.CENTER_HORIZONTAL
+ }
+ )
+ }
+
+ private fun indicationTopRow(): KeyguardIndicationTextView {
+ return KeyguardIndicationTextView(context, attrs).apply {
+ id = R.id.keyguard_indication_text
+ gravity = Gravity.CENTER
+ accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
+ setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea)
+
+ val padding = R.dimen.keyguard_indication_text_padding.dp()
+ setPaddingRelative(padding, 0, padding, 0)
+ }
+ }
+
+ private fun indicationBottomRow(): KeyguardIndicationTextView {
+ return KeyguardIndicationTextView(context, attrs).apply {
+ id = R.id.keyguard_indication_text_bottom
+ gravity = Gravity.CENTER
+ accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
+
+ setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea)
+ setEllipsize(TextUtils.TruncateAt.END)
+ setAlpha(0.8f)
+ setMinHeight(R.dimen.keyguard_indication_text_min_height.dp())
+ setMaxLines(2)
+ setVisibility(View.GONE)
+
+ val padding = R.dimen.keyguard_indication_text_padding.dp()
+ setPaddingRelative(padding, 0, padding, 0)
+ }
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
new file mode 100644
index 0000000..abf0e80
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.keyguard.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+/** Provides a container for all keyguard ui content. */
+class KeyguardRootView(
+ context: Context,
+ private val attrs: AttributeSet?,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ ) {
+
+ init {
+ addIndicationTextArea()
+ }
+
+ private fun addIndicationTextArea() {
+ val view = KeyguardIndicationArea(context, attrs)
+ addView(
+ view,
+ FrameLayout.LayoutParams(
+ MATCH_PARENT,
+ WRAP_CONTENT,
+ )
+ .apply {
+ gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp()
+ }
+ )
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 62a1a9e..3e6f8e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -57,7 +57,7 @@
* in the wallpaper picker application. This should _always_ be `false` for the real lock screen
* experience.
*/
- private val previewMode = MutableStateFlow(PreviewMode())
+ val previewMode = MutableStateFlow(PreviewMode())
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -101,12 +101,6 @@
bottomAreaInteractor.alpha.distinctUntilChanged()
}
}
- /** An observable for whether the indication area should be padded. */
- val isIndicationAreaPadded: Flow<Boolean> =
- combine(startButton, endButton) { startButtonModel, endButtonModel ->
- startButtonModel.isVisible || endButtonModel.isVisible
- }
- .distinctUntilChanged()
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
new file mode 100644
index 0000000..389cf76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** View-model for the keyguard indication area view */
+@OptIn(ExperimentalCoroutinesApi::class)
+class KeyguardIndicationAreaViewModel
+@Inject
+constructor(
+ private val keyguardInteractor: KeyguardInteractor,
+ private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
+ private val keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
+ private val burnInHelperWrapper: BurnInHelperWrapper,
+) {
+
+ /** Notifies when a new configuration is set */
+ val configurationChange: Flow<Unit> = keyguardInteractor.configurationChange
+
+ /** An observable for the alpha level for the entire bottom area. */
+ val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+
+ /** An observable for whether the indication area should be padded. */
+ val isIndicationAreaPadded: Flow<Boolean> =
+ combine(keyguardBottomAreaViewModel.startButton, keyguardBottomAreaViewModel.endButton) {
+ startButtonModel,
+ endButtonModel ->
+ startButtonModel.isVisible || endButtonModel.isVisible
+ }
+ .distinctUntilChanged()
+ /** An observable for the x-offset by which the indication area should be translated. */
+ val indicationAreaTranslationX: Flow<Float> =
+ bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
+
+ /** Returns an observable for the y-offset by which the indication area should be translated. */
+ fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
+ return keyguardInteractor.dozeAmount
+ .map { dozeAmount ->
+ dozeAmount *
+ (burnInHelperWrapper.burnInOffset(
+ /* amplitude = */ defaultBurnInOffset * 2,
+ /* xAxis= */ false,
+ ) - defaultBurnInOffset)
+ }
+ .distinctUntilChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index e60bb34..bf51976 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
/** View model for the smartspace. */
@@ -34,7 +35,7 @@
interactor: KeyguardClockInteractor,
) {
- val smartSpaceTopPadding: Flow<Int> =
+ val smartspaceTopPadding: Flow<Int> =
interactor.selectedClockSize.map {
when (it) {
SettingsClockSize.DYNAMIC -> getLargeClockSmartspaceTopPadding(context.resources)
@@ -42,6 +43,22 @@
}
}
+ val shouldHideSmartspace: Flow<Boolean> =
+ combine(
+ interactor.selectedClockSize,
+ interactor.currentClockId,
+ ::Pair,
+ )
+ .map { (size, currentClockId) ->
+ when (size) {
+ // TODO (b/284122375) This is temporary. We should use clockController
+ // .largeClock.config.hasCustomWeatherDataDisplay instead, but
+ // ClockRegistry.createCurrentClock is not reliable.
+ SettingsClockSize.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER"
+ SettingsClockSize.SMALL -> false
+ }
+ }
+
companion object {
fun getLargeClockSmartspaceTopPadding(resources: Resources): Int {
return with(resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index bfaf3d0..604d449 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -93,6 +93,7 @@
}
dismiss()
}
+ setCancelButtonOnClickListener { dismiss() }
initRecordOptionsView()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 047fea1..7f4c2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -125,6 +125,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
@@ -598,6 +599,7 @@
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
+ private final KeyguardViewConfigurator mKeyguardViewConfigurator;
private final @Nullable MultiShadeInteractor mMultiShadeInteractor;
private final CoroutineDispatcher mMainDispatcher;
private boolean mIsAnyMultiShadeExpanded;
@@ -740,6 +742,7 @@
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
+ KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mInteractionJankMonitor = interactionJankMonitor;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -762,6 +765,7 @@
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mKeyguardInteractor = keyguardInteractor;
+ mKeyguardViewConfigurator = keyguardViewConfigurator;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1319,7 +1323,6 @@
mKeyguardBottomArea.initFrom(oldBottomArea);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
@@ -1361,6 +1364,9 @@
mKeyguardIndicationController.showTransientIndication(stringResourceId),
mVibratorHelper,
mActivityStarter);
+
+ // Rebind (for now), as a new bottom area and indication area may have been created
+ mKeyguardViewConfigurator.bindIndicationArea(mKeyguardBottomArea);
}
@VisibleForTesting
@@ -1398,7 +1404,6 @@
private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
mKeyguardBottomArea = keyguardBottomArea;
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
void setOpenCloseListener(OpenCloseListener openCloseListener) {
@@ -3023,12 +3028,6 @@
mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
}
- //TODO(b/254875405): this should be removed.
- @Override
- public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mKeyguardBottomArea;
- }
-
@Override
public void applyLaunchAnimationProgress(float linearProgress) {
boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 44c732d..1752ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.settings.UserTracker
@@ -71,9 +72,9 @@
layoutInflater.inflate(R.layout.scene_window_root, null)
} else {
layoutInflater.inflate(R.layout.super_notification_shade, null)
- } as WindowRootView? ?: throw IllegalStateException(
- "Window root view could not be properly inflated"
- )
+ }
+ as WindowRootView?
+ ?: throw IllegalStateException("Window root view could not be properly inflated")
}
@Provides
@@ -89,9 +90,7 @@
return root.findViewById(R.id.legacy_window_root)
}
return root as NotificationShadeWindowView?
- ?: throw IllegalStateException(
- "root view not a NotificationShadeWindowView"
- )
+ ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@@ -120,6 +119,14 @@
return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
}
+ @Provides
+ @SysUISingleton
+ fun providesKeyguardRootView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): KeyguardRootView {
+ return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
+ }
+
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index f75047c..203355e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -21,7 +21,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.KeyguardStatusBarView
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController
import java.util.function.Consumer
@@ -120,13 +119,6 @@
/** Returns the StatusBarState. */
val barState: Int
- /**
- * Returns the bottom part of the keyguard, which contains quick affordances.
- *
- * TODO(b/275550429): this should be removed.
- */
- val keyguardBottomAreaView: KeyguardBottomAreaView?
-
/** Returns the NSSL controller. */
val notificationStackScrollLayoutController: NotificationStackScrollLayoutController
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index ba9d13d..6d951bf 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.smartspace.dagger
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
import android.view.View
@@ -81,7 +82,11 @@
showOnLockscreen: Boolean
) {
if (showOnLockscreen) {
- pi.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pi.send(options)
} else {
activityStarter.startPendingIntentDismissingKeyguard(pi)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 7cc917f..518825c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.lockscreen
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
@@ -358,7 +359,11 @@
showOnLockscreen: Boolean
) {
if (showOnLockscreen) {
- pi.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pi.send(options)
} else {
activityStarter.postStartActivityDismissingKeyguard(pi)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index df1a47a..1827a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -572,6 +572,9 @@
// TODO b/221255671: restrict this to only be set for
// notifications
options.isEligibleForLegacyPermissionPrompt = true
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
return intent.sendAndReturnResult(
null,
0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index e23ac7d..226df76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -31,7 +31,6 @@
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.View;
-import android.view.ViewGroup;
import android.window.RemoteTransition;
import android.window.SplashScreen;
@@ -40,7 +39,6 @@
import androidx.lifecycle.LifecycleOwner;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -52,7 +50,6 @@
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
@@ -70,14 +67,11 @@
String TAG = "CentralSurfaces";
boolean DEBUG = false;
boolean SPEW = false;
- boolean DUMPTRUCK = true; // extra dumpsys info
boolean DEBUG_GESTURES = false;
boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
boolean DEBUG_CAMERA_LIFT = false;
boolean DEBUG_WINDOW_STATE = false;
boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
- // additional instrumentation for testing purposes; intended to be left on during development
- boolean CHATTY = DEBUG;
boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
String ACTION_FAKE_ARTWORK = "fake_artwork";
int FADE_KEYGUARD_START_DELAY = 100;
@@ -240,8 +234,6 @@
void onKeyguardViewManagerStatesUpdated();
- ViewGroup getNotificationScrollLayout();
-
boolean isPulsing();
boolean isOccluded();
@@ -303,8 +295,6 @@
@Override
void dump(PrintWriter pwOriginal, String[] args);
- void createAndAddWindows(@Nullable RegisterStatusBarResult result);
-
float getDisplayWidth();
float getDisplayHeight();
@@ -354,8 +344,6 @@
void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
Runnable cancelAction);
- LightRevealScrim getLightRevealScrim();
-
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
@@ -469,8 +457,6 @@
void extendDozePulse();
- boolean shouldDelayWakeUpAnimation();
-
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 2177bed..cab95c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1825,11 +1825,6 @@
}
@Override
- public ViewGroup getNotificationScrollLayout() {
- return mStackScroller;
- }
-
- @Override
public boolean isPulsing() {
return mDozeServiceHost.isPulsing();
}
@@ -2253,8 +2248,7 @@
+ CameraIntents.getOverrideCameraPackage(mContext));
}
- @Override
- public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
+ private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
@@ -3035,11 +3029,6 @@
}
}
- @Override
- public LightRevealScrim getLightRevealScrim() {
- return mLightRevealScrim;
- }
-
// TODO: Figure out way to remove these.
@Override
public NavigationBarView getNavigationBarView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index dfaee4c..5624e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -40,7 +40,6 @@
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -86,7 +85,6 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final KeyguardIndicationController mKeyguardIndicationController;
private final CentralSurfaces mCentralSurfaces;
private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
@@ -115,7 +113,6 @@
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- KeyguardIndicationController keyguardIndicationController,
CentralSurfaces centralSurfaces,
LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
@@ -137,7 +134,6 @@
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- mKeyguardIndicationController = keyguardIndicationController;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mCentralSurfaces = centralSurfaces;
mShadeTransitionController = shadeTransitionController;
@@ -173,7 +169,6 @@
mNotificationPanel.getShadeNotificationPresenter().createRemoteInputDelegate());
initController.addPostInitTask(() -> {
- mKeyguardIndicationController.init();
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index 57b9f91..ae48208 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -18,10 +18,11 @@
import android.util.Pair
import com.android.systemui.monet.dynamiccolor.DynamicColor
-import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors as MDC
+import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors
class DynamicColors {
companion object {
+ private val MDC = MaterialDynamicColors()
@JvmField
val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> =
arrayListOf(
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c1999b2..b78329c 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -659,6 +659,7 @@
Resources res = userHandle.isSystem()
? mResources : mContext.createContextAsUser(userHandle, 0).getResources();
Resources.Theme theme = mContext.getTheme();
+ MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
if (!(res.getColor(android.R.color.system_accent1_500, theme)
== mColorScheme.getAccent1().getS500()
&& res.getColor(android.R.color.system_accent2_500, theme)
@@ -670,15 +671,15 @@
&& res.getColor(android.R.color.system_neutral2_500, theme)
== mColorScheme.getNeutral2().getS500()
&& res.getColor(android.R.color.system_outline_variant_dark, theme)
- == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
+ == dynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
&& res.getColor(android.R.color.system_outline_variant_light, theme)
- == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
+ == dynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
&& res.getColor(android.R.color.system_primary_container_dark, theme)
- == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
+ == dynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
&& res.getColor(android.R.color.system_primary_container_light, theme)
- == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
+ == dynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
&& res.getColor(android.R.color.system_primary_fixed, theme)
- == MaterialDynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
+ == dynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index dfc6392..cab47a3 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -307,6 +307,8 @@
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
+ options.setPendingIntentBackgroundActivityStartMode(
+ BroadcastOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
walletCard.getPendingIntent().send(options.toBundle());
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Error sending pending intent for wallet card.");
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 3f1560b..8c71392 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -53,6 +53,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -62,6 +63,7 @@
import org.mockito.junit.MockitoJUnit
import java.util.TimeZone
import java.util.concurrent.Executor
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@@ -117,6 +119,7 @@
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
+ configurationRepository = FakeConfigurationRepository(),
),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
@@ -155,9 +158,8 @@
@Test
fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
- // TODO(b/266103601): delete this test and add more coverage for updateColors()
- // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 456702b..bdf6bee 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -18,6 +18,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
+import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -42,15 +43,12 @@
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -91,10 +89,9 @@
protected @Mock ConfigurationController mConfigurationController;
protected @Mock VibratorHelper mVibrator;
protected @Mock AuthRippleController mAuthRippleController;
- protected @Mock FeatureFlags mFeatureFlags;
protected @Mock KeyguardTransitionRepository mTransitionRepository;
- protected @Mock CommandQueue mCommandQueue;
protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
+ protected FakeFeatureFlags mFeatureFlags;
protected LockIconViewController mUnderTest;
@@ -144,6 +141,8 @@
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mFeatureFlags = new FakeFeatureFlags();
+ mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
mUnderTest = new LockIconViewController(
mLockIconView,
mStatusBarStateController,
@@ -160,12 +159,7 @@
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
- new KeyguardInteractor(
- new FakeKeyguardRepository(),
- mCommandQueue,
- mFeatureFlags,
- new FakeKeyguardBouncerRepository()
- ),
+ KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags
);
}
@@ -226,7 +220,7 @@
}
protected void init(boolean useMigrationFlag) {
- when(mFeatureFlags.isEnabled(DOZING_MIGRATION_1)).thenReturn(useMigrationFlag);
+ mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
mUnderTest.init();
verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 1990c8f..3a93e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
+import android.app.admin.DevicePolicyManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.AuthenticationRepository
@@ -48,7 +49,7 @@
fun authMethod() =
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
- assertThat(authMethod).isEqualTo(AuthenticationMethodModel.PIN(1234))
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin(1234))
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password("password"))
@@ -147,7 +148,7 @@
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
- underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
@@ -160,7 +161,7 @@
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
- underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
@@ -169,6 +170,51 @@
}
@Test
+ fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() =
+ testScope.runTest {
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(listOf())).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(failedAttemptCount).isEqualTo(1)
+ }
+
+ @Test
+ fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() =
+ testScope.runTest {
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
+ assertThat(isUnlocked).isTrue()
+ assertThat(failedAttemptCount).isEqualTo(0)
+ }
+
+ @Test
+ fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() =
+ testScope.runTest {
+ // Max pin length is 16 digits. To avoid issues with overflows, this test ensures
+ // that all pins > 16 decimal digits are rejected.
+
+ // If the policy changes, there is work to do in SysUI.
+ assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
+
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(failedAttemptCount).isEqualTo(1)
+ }
+
+ @Test
fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 374c28d6..6a63c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -69,7 +69,7 @@
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -167,7 +167,7 @@
fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -211,7 +211,7 @@
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(throttling).isNull()
assertThat(message).isEqualTo("")
assertThat(isUnlocked).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 1642410..b53e034 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -55,7 +55,7 @@
@Test
fun animateFailure() =
testScope.runTest {
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
val animateFailure by collectLastValue(underTest.animateFailure)
assertThat(animateFailure).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index e8c946c..c607496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -97,7 +97,7 @@
testScope.runTest {
val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(message?.isUpdateAnimated).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -120,7 +120,7 @@
}
)
val throttling by collectLastValue(bouncerInteractor.throttling)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isInputEnabled).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -137,7 +137,7 @@
fun throttlingDialogMessage() =
testScope.runTest {
val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
repeat(BouncerInteractor.THROTTLE_EVERY) {
// Wrong PIN.
@@ -154,7 +154,7 @@
return listOf(
AuthenticationMethodModel.None,
AuthenticationMethodModel.Swipe,
- AuthenticationMethodModel.PIN(1234),
+ AuthenticationMethodModel.Pin(1234),
AuthenticationMethodModel.Password("password"),
AuthenticationMethodModel.Pattern(
listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 3bdaf05..7b6bb37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -25,12 +25,12 @@
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,8 +85,8 @@
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -95,7 +95,7 @@
underTest.onShown()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -106,8 +106,8 @@
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -117,7 +117,8 @@
underTest.onPinButtonClicked(1)
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(0 to 1)
+ assertThat(entries).hasSize(1)
+ assertThat(entries?.map { it.input }).containsExactly(1)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -128,32 +129,59 @@
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
- assertThat(pinLengths).isEqualTo(0 to 1)
+ assertThat(entries).hasSize(1)
underTest.onBackspaceButtonClicked()
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(1 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
+ fun onPinEdit() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
+ val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
+ val message by collectLastValue(bouncerViewModel.message)
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
+ authenticationInteractor.lockDevice()
+ sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+
+ underTest.onPinButtonClicked(1)
+ underTest.onPinButtonClicked(2)
+ underTest.onPinButtonClicked(3)
+ underTest.onBackspaceButtonClicked()
+ underTest.onBackspaceButtonClicked()
+ underTest.onPinButtonClicked(4)
+ underTest.onPinButtonClicked(5)
+
+ assertThat(entries).hasSize(3)
+ assertThat(entries?.map { it.input }).containsExactly(1, 4, 5).inOrder()
+ assertThat(entries?.map { it.sequenceNumber }).isInStrictOrder()
+ }
+
+ @Test
fun onBackspaceButtonLongPressed() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -165,13 +193,9 @@
underTest.onPinButtonClicked(4)
underTest.onBackspaceButtonLongPressed()
- repeat(4) { index ->
- assertThat(pinLengths).isEqualTo(4 - index to 3 - index)
- advanceTimeBy(PinBouncerViewModel.BACKSPACE_LONG_PRESS_DELAY_MS)
- }
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(1 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -181,7 +205,7 @@
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -204,8 +228,8 @@
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -219,7 +243,7 @@
underTest.onAuthenticateButtonClicked()
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -231,8 +255,8 @@
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -245,7 +269,7 @@
underTest.onPinButtonClicked(5) // PIN is now wrong!
underTest.onAuthenticateButtonClicked()
assertThat(message?.text).isEqualTo(WRONG_PIN)
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -266,5 +290,11 @@
private const val CONTAINER_NAME = "container1"
private const val ENTER_YOUR_PIN = "Enter your pin"
private const val WRONG_PIN = "Wrong pin"
+
+ val KEY_CODE =
+ Correspondence.transforming<EnteredKey, Int>(
+ { it?.input },
+ "has a eventId of",
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index b2e37cc..4ba6718 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -145,7 +145,7 @@
assertThat(activityRule.activity.lastStartedActivity?.component?.className)
.isEqualTo(ControlsFavoritingActivity::class.java.name)
- assertThat(activityRule.activity.triedToFinish).isTrue()
+ assertThat(activityRule.activity.triedToFinish).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 8ee7d3e..c9470bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -185,6 +186,7 @@
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
+ configurationRepository = FakeConfigurationRepository(),
),
registry = mock(),
lockPatternUtils = lockPatternUtils,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 78a65a8..eb97022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -7,11 +7,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -60,13 +58,12 @@
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
- val keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- FakeCommandQueue(),
- featureFlags,
- FakeKeyguardBouncerRepository()
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ repository = keyguardRepository,
+ featureFlags = featureFlags,
)
+ val keyguardInteractor = withDeps.keyguardInteractor
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index e61620b..3ea74e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -47,6 +47,7 @@
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
@@ -159,17 +160,16 @@
biometricSettingsRepository = FakeBiometricSettingsRepository()
deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
trustRepository = FakeTrustRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- fakeCommandQueue = FakeCommandQueue()
- keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
)
+ keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
+ bouncerRepository = withDeps.bouncerRepository
+ fakeCommandQueue = withDeps.commandQueue
+
alternateBouncerInteractor =
AlternateBouncerInteractor(
bouncerRepository = bouncerRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 0d695aa..a4f19b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
@@ -50,6 +51,7 @@
private lateinit var underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var configurationRepository: FakeConfigurationRepository
@Before
fun setUp() {
@@ -59,12 +61,14 @@
testScope = TestScope()
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
+ configurationRepository = FakeConfigurationRepository()
underTest =
KeyguardInteractor(
repository,
commandQueue,
featureFlags,
bouncerRepository,
+ configurationRepository,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index fb21847..8f6bbc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -38,8 +38,6 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -48,7 +46,6 @@
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
@@ -225,7 +222,6 @@
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -309,12 +305,10 @@
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(
- repository = FakeKeyguardRepository(),
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor,
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
@@ -368,11 +362,11 @@
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
- underTest.onQuickAffordanceTriggered(
- configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
- expandable = expandable,
- slotId = "",
- )
+ underTest.onQuickAffordanceTriggered(
+ configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+ expandable = expandable,
+ slotId = "",
+ )
if (startActivity) {
if (needsToUnlockFirst) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 895c1cd..1c0b06c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -40,7 +40,6 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -53,7 +52,6 @@
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
@@ -84,7 +82,6 @@
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -168,15 +165,14 @@
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = repository,
+ )
underTest =
KeyguardQuickAffordanceInteractor(
- keyguardInteractor =
- KeyguardInteractor(
- repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ keyguardInteractor = withDeps.keyguardInteractor,
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 603f199..c53d430 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -21,7 +21,6 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -39,7 +38,6 @@
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -74,10 +72,10 @@
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var shadeRepository: ShadeRepository
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var featureFlags: FakeFeatureFlags
// Used to verify transition requests for test output
@Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
@@ -103,11 +101,11 @@
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
- val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -117,7 +115,7 @@
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -126,7 +124,7 @@
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -135,7 +133,7 @@
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -144,7 +142,7 @@
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -153,7 +151,7 @@
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -162,7 +160,7 @@
fromAlternateBouncerTransitionInteractor =
FromAlternateBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -171,7 +169,7 @@
fromPrimaryBouncerTransitionInteractor =
FromPrimaryBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardSecurityModel = keyguardSecurityModel,
@@ -882,13 +880,13 @@
WakeSleepReason.OTHER
)
- private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor {
- return KeyguardInteractor(
- keyguardRepository,
- commandQueue,
- featureFlags,
- bouncerRepository,
- )
+ private fun createKeyguardInteractor(): KeyguardInteractor {
+ return KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = keyguardRepository,
+ bouncerRepository = bouncerRepository,
+ )
+ .keyguardInteractor
}
private suspend fun TestScope.runTransition(from: KeyguardState, to: KeyguardState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
index d622f1c..65781c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
@@ -95,7 +95,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -108,7 +108,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.unlockDevice()
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -195,7 +195,7 @@
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 8a36dbc..a493b1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -39,12 +39,11 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -56,7 +55,6 @@
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -95,7 +93,6 @@
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@@ -140,7 +137,6 @@
),
),
)
- repository = FakeKeyguardRepository()
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
@@ -149,13 +145,10 @@
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
}
- val keyguardInteractor =
- KeyguardInteractor(
- repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- )
+ val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ val keyguardInteractor = withDeps.keyguardInteractor
+ repository = withDeps.repository
+
whenever(userTracker.userHandle).thenReturn(mock())
whenever(userTracker.userId).thenReturn(10)
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
@@ -550,91 +543,6 @@
}
@Test
- fun isIndicationAreaPadded() =
- testScope.runTest {
- repository.setKeyguardShowing(true)
- val value = collectLastValue(underTest.isIndicationAreaPadded)
-
- assertThat(value()).isFalse()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig =
- TestConfig(
- isVisible = true,
- isClickable = true,
- icon = mock(),
- canShowWhileLocked = true,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_END,
- testConfig =
- TestConfig(
- isVisible = true,
- isClickable = true,
- icon = mock(),
- canShowWhileLocked = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig =
- TestConfig(
- isVisible = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_END,
- testConfig =
- TestConfig(
- isVisible = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
- )
- )
- assertThat(value()).isFalse()
- }
-
- @Test
- fun indicationAreaTranslationX() =
- testScope.runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationX)
-
- assertThat(value()).isEqualTo(0f)
- repository.setClockPosition(100, 100)
- assertThat(value()).isEqualTo(100f)
- repository.setClockPosition(200, 100)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(200, 200)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(300, 100)
- assertThat(value()).isEqualTo(300f)
- }
-
- @Test
- fun indicationAreaTranslationY() =
- testScope.runTest {
- val value =
- collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
-
- // Negative 0 - apparently there's a difference in floating point arithmetic - FML
- assertThat(value()).isEqualTo(-0f)
- val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
- assertThat(value()).isEqualTo(expected1)
- val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
- assertThat(value()).isEqualTo(expected2)
- val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
- assertThat(value()).isEqualTo(expected3)
- val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
- assertThat(value()).isEqualTo(expected4)
- }
-
- @Test
fun isClickable_trueWhenAlphaAtThreshold() =
testScope.runTest {
repository.setKeyguardShowing(true)
@@ -757,11 +665,6 @@
)
}
- private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
- repository.setDozeAmount(dozeAmount)
- return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
- }
-
private suspend fun setUpQuickAffordanceModel(
position: KeyguardQuickAffordancePosition,
testConfig: TestConfig,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
new file mode 100644
index 0000000..dff0f29
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
+
+ private lateinit var underTest: KeyguardIndicationAreaViewModel
+ private lateinit var repository: FakeKeyguardRepository
+
+ private val startButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
+ )
+ )
+ private val endButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
+ )
+ )
+ private val alphaFlow = MutableStateFlow<Float>(1f)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
+ .thenReturn(RETURNED_BURN_IN_OFFSET)
+
+ val withDeps = KeyguardInteractorFactory.create()
+ val keyguardInteractor = withDeps.keyguardInteractor
+ repository = withDeps.repository
+
+ val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock()
+ whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
+ whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
+ whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
+ underTest =
+ KeyguardIndicationAreaViewModel(
+ keyguardInteractor = keyguardInteractor,
+ bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
+ keyguardBottomAreaViewModel = bottomAreaViewModel,
+ burnInHelperWrapper = burnInHelperWrapper,
+ )
+ }
+
+ @Test
+ fun alpha() = runTest {
+ val value = collectLastValue(underTest.alpha)
+
+ assertThat(value()).isEqualTo(1f)
+ alphaFlow.value = 0.1f
+ assertThat(value()).isEqualTo(0.1f)
+ alphaFlow.value = 0.5f
+ assertThat(value()).isEqualTo(0.5f)
+ alphaFlow.value = 0.2f
+ assertThat(value()).isEqualTo(0.2f)
+ alphaFlow.value = 0f
+ assertThat(value()).isEqualTo(0f)
+ }
+
+ @Test
+ fun isIndicationAreaPadded() = runTest {
+ repository.setKeyguardShowing(true)
+ val value = collectLastValue(underTest.isIndicationAreaPadded)
+
+ assertThat(value()).isFalse()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isFalse()
+ }
+
+ @Test
+ fun indicationAreaTranslationX() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationX)
+
+ assertThat(value()).isEqualTo(0f)
+ repository.setClockPosition(100, 100)
+ assertThat(value()).isEqualTo(100f)
+ repository.setClockPosition(200, 100)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(200, 200)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(300, 100)
+ assertThat(value()).isEqualTo(300f)
+ }
+
+ @Test
+ fun indicationAreaTranslationY() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
+
+ // Negative 0 - apparently there's a difference in floating point arithmetic - FML
+ assertThat(value()).isEqualTo(-0f)
+ val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
+ assertThat(value()).isEqualTo(expected1)
+ val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
+ assertThat(value()).isEqualTo(expected2)
+ val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
+ assertThat(value()).isEqualTo(expected3)
+ val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
+ assertThat(value()).isEqualTo(expected4)
+ }
+
+ private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
+ repository.setDozeAmount(dozeAmount)
+ return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
+ }
+
+ companion object {
+ private const val DEFAULT_BURN_IN_OFFSET = 5
+ private const val RETURNED_BURN_IN_OFFSET = 3
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 8ba3f0f..f0ea007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -109,7 +109,7 @@
fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -119,7 +119,7 @@
fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
@@ -132,7 +132,7 @@
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -145,7 +145,7 @@
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
@@ -158,7 +158,7 @@
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 105387d..05a1699 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -70,7 +70,7 @@
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -83,7 +83,7 @@
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 5b094c9..07feedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -104,6 +104,26 @@
assertThat(visibility).isEqualTo(View.VISIBLE)
}
+ @Test
+ fun showDialog_dialogIsShowing() {
+ dialog.show()
+
+ assertThat(dialog.isShowing).isTrue()
+ }
+
+ @Test
+ fun showDialog_cancelClicked_dialogIsDismissed() {
+ dialog.show()
+
+ clickOnCancel()
+
+ assertThat(dialog.isShowing).isFalse()
+ }
+
+ private fun clickOnCancel() {
+ dialog.requireViewById<View>(android.R.id.button2).performClick()
+ }
+
private fun onSpinnerItemSelected(position: Int) {
val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
spinner.onItemSelectedListener.onItemSelected(spinner, mock(), position, /* id= */ 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index fe89a14..41351e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -89,12 +89,13 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -253,6 +254,7 @@
@Mock protected UserManager mUserManager;
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected LockIconViewController mLockIconViewController;
+ @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
@Mock protected KeyguardMediaController mKeyguardMediaController;
@Mock protected NavigationModeController mNavigationModeController;
@Mock protected NavigationBarController mNavigationBarController;
@@ -342,8 +344,8 @@
mMainDispatcher = getMainDispatcher();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(
new FakeKeyguardRepository());
- mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue,
- mFeatureFlags, new FakeKeyguardBouncerRepository());
+
+ mKeyguardInteractor = KeyguardInteractorFactory.create().getKeyguardInteractor();
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
mInteractionJankMonitor, mShadeExpansionStateManager);
@@ -611,6 +613,7 @@
mKeyuardLongPressViewModel,
mKeyguardInteractor,
mActivityStarter,
+ mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 69d03d9..f8e1a9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -71,7 +71,7 @@
fun upTransitionSceneKey_deviceLocked_lockScreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
@@ -81,7 +81,7 @@
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -91,7 +91,7 @@
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -104,7 +104,7 @@
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index fdfe028..1724f27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -44,7 +44,6 @@
import com.android.systemui.shade.ShadeNotificationPresenter;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -122,7 +121,6 @@
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mock(KeyguardIndicationController.class),
mCentralSurfaces,
mock(LockscreenShadeTransitionController.class),
mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index d9ee081..813597a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -28,12 +28,10 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.shade.ShadeFoldAnimator
import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
@@ -80,8 +78,6 @@
@Mock lateinit var viewTreeObserver: ViewTreeObserver
- @Mock private lateinit var commandQueue: CommandQueue
-
@Mock lateinit var shadeFoldAnimator: ShadeFoldAnimator
@Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
@@ -111,15 +107,10 @@
onActionStarted.run()
}
- keyguardRepository = FakeKeyguardRepository()
val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- val keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- )
+ val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ val keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
// Needs to be run on the main thread
runBlocking(IMMEDIATE) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index ca83d49..3fbbeda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -39,12 +39,10 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -97,7 +95,6 @@
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: UserInteractor
@@ -126,8 +123,9 @@
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ keyguardRepository = reply.repository
userRepository = FakeUserRepository()
- keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository()
val testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
@@ -142,13 +140,7 @@
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ keyguardInteractor = reply.keyguardInteractor,
manager = manager,
headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index fd8c6c7..9cb26e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -32,11 +32,8 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -80,13 +77,11 @@
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: StatusBarUserChipViewModel
private val userRepository = FakeUserRepository()
- private val keyguardRepository = FakeKeyguardRepository()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
@@ -250,12 +245,8 @@
repository = userRepository,
activityStarter = activityStarter,
keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ .keyguardInteractor,
featureFlags = featureFlags,
manager = manager,
headlessSystemUserMode = headlessSystemUserMode,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 9155084..e3f9fac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -30,11 +30,9 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -79,7 +77,6 @@
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: UserSwitcherViewModel
@@ -112,7 +109,6 @@
)
}
- keyguardRepository = FakeKeyguardRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
@@ -140,38 +136,35 @@
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ keyguardRepository = reply.repository
+
underTest =
UserSwitcherViewModel(
- userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
- featureFlags = featureFlags,
- manager = manager,
- headlessSystemUserMode = headlessSystemUserMode,
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = fakeBroadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = testDispatcher,
- activityManager = activityManager,
- refreshUsersScheduler = refreshUsersScheduler,
- guestUserInteractor = guestUserInteractor,
- uiEventLogger = uiEventLogger,
- ),
- guestUserInteractor = guestUserInteractor,
- )
+ userInteractor =
+ UserInteractor(
+ applicationContext = context,
+ repository = userRepository,
+ activityStarter = activityStarter,
+ keyguardInteractor = reply.keyguardInteractor,
+ featureFlags = featureFlags,
+ manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
+ applicationScope = testScope.backgroundScope,
+ telephonyInteractor =
+ TelephonyInteractor(
+ repository = FakeTelephonyRepository(),
+ ),
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ backgroundDispatcher = testDispatcher,
+ activityManager = activityManager,
+ refreshUsersScheduler = refreshUsersScheduler,
+ guestUserInteractor = guestUserInteractor,
+ uiEventLogger = uiEventLogger,
+ ),
+ guestUserInteractor = guestUserInteractor,
+ )
}
@Test
@@ -323,7 +316,7 @@
setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job =
- launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
+ launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
assertThat(isFinishRequested.last()).isFalse()
underTest.onCancelButtonClicked()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
new file mode 100644
index 0000000..c7ca7ce
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+
+/**
+ * Simply put, I got tired of adding a constructor argument and then having to tweak dozens of
+ * files. This should alleviate some of the burden by providing defaults for testing.
+ */
+object KeyguardInteractorFactory {
+
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ featureFlags: FakeFeatureFlags = createFakeFeatureFlags(),
+ repository: FakeKeyguardRepository = FakeKeyguardRepository(),
+ commandQueue: FakeCommandQueue = FakeCommandQueue(),
+ bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
+ configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
+ ): WithDependencies {
+ return WithDependencies(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ )
+ )
+ }
+
+ /** Provide defaults, otherwise tests will throw an error */
+ fun createFakeFeatureFlags(): FakeFeatureFlags {
+ return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
+ }
+
+ data class WithDependencies(
+ val repository: FakeKeyguardRepository,
+ val commandQueue: FakeCommandQueue,
+ val featureFlags: FakeFeatureFlags,
+ val bouncerRepository: FakeKeyguardBouncerRepository,
+ val configurationRepository: FakeConfigurationRepository,
+ val keyguardInteractor: KeyguardInteractor,
+ )
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 61fc32d..51359ad 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -163,13 +163,35 @@
private boolean mDisabledByDeviceConfig;
// Device-config settings that are cached and passed back to apps
- @GuardedBy("mLock") int mDevCfgLoggingLevel;
- @GuardedBy("mLock") int mDevCfgMaxBufferSize;
- @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs;
- @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs;
- @GuardedBy("mLock") int mDevCfgLogHistorySize;
- @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs;
- @GuardedBy("mLock") boolean mDevCfgDisableFlushForViewTreeAppearing;
+ @GuardedBy("mLock")
+ int mDevCfgLoggingLevel;
+
+ @GuardedBy("mLock")
+ int mDevCfgMaxBufferSize;
+
+ @GuardedBy("mLock")
+ int mDevCfgIdleFlushingFrequencyMs;
+
+ @GuardedBy("mLock")
+ int mDevCfgTextChangeFlushingFrequencyMs;
+
+ @GuardedBy("mLock")
+ int mDevCfgLogHistorySize;
+
+ @GuardedBy("mLock")
+ int mDevCfgIdleUnbindTimeoutMs;
+
+ @GuardedBy("mLock")
+ boolean mDevCfgDisableFlushForViewTreeAppearing;
+
+ @GuardedBy("mLock")
+ boolean mDevCfgEnableContentProtectionReceiver;
+
+ @GuardedBy("mLock")
+ int mDevCfgContentProtectionAppsBlocklistSize;
+
+ @GuardedBy("mLock")
+ int mDevCfgContentProtectionBufferSize;
private final Executor mDataShareExecutor = Executors.newCachedThreadPool();
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -362,6 +384,11 @@
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
case ContentCaptureManager
.DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING:
+ case ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER:
+ case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE:
+ case ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE:
setFineTuneParamsFromDeviceConfig();
return;
default:
@@ -372,39 +399,78 @@
private void setFineTuneParamsFromDeviceConfig() {
synchronized (mLock) {
- mDevCfgMaxBufferSize = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
- ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
- mDevCfgIdleFlushingFrequencyMs = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
- ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
- mDevCfgTextChangeFlushingFrequencyMs = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
- ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
- mDevCfgLogHistorySize = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
- mDevCfgIdleUnbindTimeoutMs = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT,
- (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
- mDevCfgDisableFlushForViewTreeAppearing = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
- ContentCaptureManager
- .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
- false);
+ mDevCfgMaxBufferSize =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
+ ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
+ mDevCfgIdleFlushingFrequencyMs =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
+ ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
+ mDevCfgTextChangeFlushingFrequencyMs =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
+ ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
+ mDevCfgLogHistorySize =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE,
+ 20);
+ mDevCfgIdleUnbindTimeoutMs =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT,
+ (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
+ mDevCfgDisableFlushForViewTreeAppearing =
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
+ false);
+ mDevCfgEnableContentProtectionReceiver =
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER,
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER);
+ mDevCfgContentProtectionAppsBlocklistSize =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE);
+ mDevCfgContentProtectionBufferSize =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager
+ .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE);
if (verbose) {
- Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): "
- + "bufferSize=" + mDevCfgMaxBufferSize
- + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
- + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
- + ", logHistory=" + mDevCfgLogHistorySize
- + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs
- + ", disableFlushForViewTreeAppearing="
- + mDevCfgDisableFlushForViewTreeAppearing);
+ Slog.v(
+ TAG,
+ "setFineTuneParamsFromDeviceConfig(): "
+ + "bufferSize="
+ + mDevCfgMaxBufferSize
+ + ", idleFlush="
+ + mDevCfgIdleFlushingFrequencyMs
+ + ", textFluxh="
+ + mDevCfgTextChangeFlushingFrequencyMs
+ + ", logHistory="
+ + mDevCfgLogHistorySize
+ + ", idleUnbindTimeoutMs="
+ + mDevCfgIdleUnbindTimeoutMs
+ + ", disableFlushForViewTreeAppearing="
+ + mDevCfgDisableFlushForViewTreeAppearing
+ + ", enableContentProtectionReceiver="
+ + mDevCfgEnableContentProtectionReceiver
+ + ", contentProtectionAppsBlocklistSize="
+ + mDevCfgContentProtectionAppsBlocklistSize
+ + ", contentProtectionBufferSize="
+ + mDevCfgContentProtectionBufferSize);
}
}
}
@@ -645,21 +711,46 @@
final String prefix2 = prefix + " ";
- pw.print(prefix); pw.print("Users disabled by Settings: "); pw.println(mDisabledBySettings);
- pw.print(prefix); pw.println("DeviceConfig Settings: ");
- pw.print(prefix2); pw.print("disabled: "); pw.println(mDisabledByDeviceConfig);
- pw.print(prefix2); pw.print("loggingLevel: "); pw.println(mDevCfgLoggingLevel);
- pw.print(prefix2); pw.print("maxBufferSize: "); pw.println(mDevCfgMaxBufferSize);
- pw.print(prefix2); pw.print("idleFlushingFrequencyMs: ");
+ pw.print(prefix);
+ pw.print("Users disabled by Settings: ");
+ pw.println(mDisabledBySettings);
+ pw.print(prefix);
+ pw.println("DeviceConfig Settings: ");
+ pw.print(prefix2);
+ pw.print("disabled: ");
+ pw.println(mDisabledByDeviceConfig);
+ pw.print(prefix2);
+ pw.print("loggingLevel: ");
+ pw.println(mDevCfgLoggingLevel);
+ pw.print(prefix2);
+ pw.print("maxBufferSize: ");
+ pw.println(mDevCfgMaxBufferSize);
+ pw.print(prefix2);
+ pw.print("idleFlushingFrequencyMs: ");
pw.println(mDevCfgIdleFlushingFrequencyMs);
- pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: ");
+ pw.print(prefix2);
+ pw.print("textChangeFlushingFrequencyMs: ");
pw.println(mDevCfgTextChangeFlushingFrequencyMs);
- pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize);
- pw.print(prefix2); pw.print("idleUnbindTimeoutMs: ");
+ pw.print(prefix2);
+ pw.print("logHistorySize: ");
+ pw.println(mDevCfgLogHistorySize);
+ pw.print(prefix2);
+ pw.print("idleUnbindTimeoutMs: ");
pw.println(mDevCfgIdleUnbindTimeoutMs);
- pw.print(prefix2); pw.print("disableFlushForViewTreeAppearing: ");
+ pw.print(prefix2);
+ pw.print("disableFlushForViewTreeAppearing: ");
pw.println(mDevCfgDisableFlushForViewTreeAppearing);
- pw.print(prefix); pw.println("Global Options:");
+ pw.print(prefix2);
+ pw.print("enableContentProtectionReceiver: ");
+ pw.println(mDevCfgEnableContentProtectionReceiver);
+ pw.print(prefix2);
+ pw.print("contentProtectionAppsBlocklistSize: ");
+ pw.println(mDevCfgContentProtectionAppsBlocklistSize);
+ pw.print(prefix2);
+ pw.print("contentProtectionBufferSize: ");
+ pw.println(mDevCfgContentProtectionBufferSize);
+ pw.print(prefix);
+ pw.println("Global Options:");
mGlobalContentCaptureOptions.dump(prefix2, pw);
}
@@ -1019,11 +1110,19 @@
}
synchronized (mLock) {
- final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel,
- mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs,
- mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize,
- mDevCfgDisableFlushForViewTreeAppearing,
- whitelistedComponents);
+ final ContentCaptureOptions options =
+ new ContentCaptureOptions(
+ mDevCfgLoggingLevel,
+ mDevCfgMaxBufferSize,
+ mDevCfgIdleFlushingFrequencyMs,
+ mDevCfgTextChangeFlushingFrequencyMs,
+ mDevCfgLogHistorySize,
+ mDevCfgDisableFlushForViewTreeAppearing,
+ /* enableReceiver= */ true,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ mDevCfgEnableContentProtectionReceiver,
+ mDevCfgContentProtectionBufferSize),
+ whitelistedComponents);
if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options);
return options;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 00dbb97..6db8ea7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2156,7 +2156,6 @@
}
}
- final boolean enableFgsWhileInUseFix = mAm.mConstants.mEnableFgsWhileInUseFix;
final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs;
if (fgsTypeChangingFromShortFgs) {
@@ -2214,19 +2213,7 @@
}
}
- boolean resetNeededForLogging = false;
-
- // Re-evaluate mAllowWhileInUsePermissionInFgs and mAllowStartForeground
- // (i.e. while-in-use and BFSL flags) if needed.
- //
- // Consider the below if-else section to be in the else of the above
- // `if (fgsTypeChangingFromShortFgs)`.
- // Using an else would increase the indent further, so we don't use it here
- // and instead just add !fgsTypeChangingFromShortFgs to all if's.
- //
- // The first if's are for the original while-in-use logic.
- if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
- && r.mStartForegroundCount == 0) {
+ if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount == 0) {
/*
If the service was started with startService(), not
startForegroundService(), and if startForeground() isn't called within
@@ -2257,8 +2244,7 @@
r.mLoggedInfoAllowStartForeground = false;
}
}
- } else if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
- && r.mStartForegroundCount >= 1) {
+ } else if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount >= 1) {
// We get here if startForeground() is called multiple times
// on the same service after it's created, regardless of whether
// stopForeground() has been called or not.
@@ -2269,100 +2255,6 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */, false /* isStartService */);
- } else if (!fgsTypeChangingFromShortFgs && enableFgsWhileInUseFix) {
- // The new while-in-use logic.
- //
- // When startForeground() is called, we _always_ call
- // setFgsRestrictionLocked() to set the restrictions according to the
- // current state of the app.
- // (So if the app is now in TOP, for example, the service will now always
- // get while-in-use permissions.)
- //
- // Note, setFgsRestrictionLocked() will never disallow
- // mAllowWhileInUsePermissionInFgs nor mAllowStartForeground
- // (i.e. while-in-use and BFSL flags) once they're set to "allowed".
- //
- // HOWEVER, if these flags were set to "allowed" in Context.startService()
- // (as opposed to startForegroundService()), when the service wasn't yet
- // a foreground service, then we may not always
- // want to trust them -- for example, if the service has been running as a
- // BG service or a bound service for a long time when the app is not longer
- // in the foreground, then we shouldn't grant while-in-user nor BFSL.
- // So in that case, we need to reset it first.
-
- final long delayMs =
- (r.mLastUntrustedSetFgsRestrictionAllowedTime == 0) ? 0
- : (SystemClock.elapsedRealtime()
- - r.mLastUntrustedSetFgsRestrictionAllowedTime);
- final boolean resetNeeded =
- !r.isForeground
- && delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs;
- if (resetNeeded) {
- // We don't want to reset mDebugWhileInUseReasonInBindService here --
- // we'll instead reset it in the following code, using the simulated
- // legacy logic.
- resetFgsRestrictionLocked(r,
- /*resetDebugWhileInUseReasonInBindService=*/ false);
- }
-
- // Simulate the reset flow in the legacy logic to reset
- // mDebugWhileInUseReasonInBindService.
- // (Which is only used to compare to the old logic.)
- final long legacyDelayMs = SystemClock.elapsedRealtime() - r.createRealTime;
- if ((r.mStartForegroundCount == 0)
- && (legacyDelayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs)) {
- r.mDebugWhileInUseReasonInBindService = REASON_DENIED;
- }
-
- setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, r.userId,
- BackgroundStartPrivileges.NONE,
- false /* isBindService */, false /* isStartService */);
-
- final String temp = "startForegroundDelayMs:" + delayMs
- + "; started: " + r.startRequested
- + "; num_bindings: " + r.getConnections().size()
- + "; wasForeground: " + r.isForeground
- + "; resetNeeded:" + resetNeeded;
- if (r.mInfoAllowStartForeground != null) {
- r.mInfoAllowStartForeground += "; " + temp;
- } else {
- r.mInfoAllowStartForeground = temp;
- }
- r.mLoggedInfoAllowStartForeground = false;
-
- resetNeededForLogging = resetNeeded;
- }
-
- // If the service has any bindings and it's not yet a FGS
- // we compare the new and old while-in-use logics.
- // (If it's not the first startForeground() call, we already reset the
- // while-in-use and BFSL flags, so the logic change wouldn't matter.)
- //
- // Note, mDebugWhileInUseReasonInBindService does *not* fully simulate the
- // legacy logic, because we'll only set it in bindService(), but the actual
- // mAllowWhileInUsePermissionInFgsReason can change afterwards, in a subsequent
- // Service.startForeground(). This check will only provide "rough" check.
- // But if mDebugWhileInUseReasonInBindService is _not_ DENIED, and
- // mDebugWhileInUseReasonInStartForeground _is_ DENIED, then that means we'd
- // now detected a behavior change.
- // OTOH, if it's changing from non-DENIED to another non-DENIED, that may
- // not be a problem.
- if (enableFgsWhileInUseFix
- && !r.isForeground
- && (r.getConnections().size() > 0)
- && (r.mDebugWhileInUseReasonInBindService
- != r.mDebugWhileInUseReasonInStartForeground)) {
- logWhileInUseChangeWtf("FGS while-in-use changed (b/276963716): old="
- + reasonCodeToString(r.mDebugWhileInUseReasonInBindService)
- + " new="
- + reasonCodeToString(r.mDebugWhileInUseReasonInStartForeground)
- + " startForegroundCount=" + r.mStartForegroundCount
- + " started=" + r.startRequested
- + " num_bindings=" + r.getConnections().size()
- + " resetNeeded=" + resetNeededForLogging
- + " "
- + r.shortInstanceName);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -2471,10 +2363,6 @@
}
r.isForeground = true;
- // Once the service becomes a foreground service,
- // the FGS restriction information always becomes "trustable".
- r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
-
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -2620,13 +2508,6 @@
}
}
- /**
- * It just does a wtf, but extracted to a method, so we can do a signature search on pitot.
- */
- private void logWhileInUseChangeWtf(String message) {
- Slog.wtf(TAG, message);
- }
-
private boolean withinFgsDeferRateLimit(ServiceRecord sr, final long now) {
// If we're still within the service's deferral period, then by definition
// deferral is not rate limited.
@@ -3839,25 +3720,9 @@
return 0;
}
}
- if (!mAm.mConstants.mEnableFgsWhileInUseFix) {
- // Old while-in-use logic.
- setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
- BackgroundStartPrivileges.NONE, true /* isBindService */,
- false /* isStartService */);
- } else {
- // New logic will not call setFgsRestrictionLocked() here, but we still
- // keep track of the allow reason from the old logic here, so we can compare to
- // the new logic.
- // Once we're confident enough in the new logic, we should remove it.
- if (s.mDebugWhileInUseReasonInBindService == REASON_DENIED) {
- s.mDebugWhileInUseReasonInBindService =
- shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, s.app,
- BackgroundStartPrivileges.NONE,
- true /* isBindService */,
- false /* DO NOT enableFgsWhileInUseFix; use the old logic */);
- }
- }
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+ BackgroundStartPrivileges.NONE, true /* isBindService */,
+ false /* isStartService */);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7554,15 +7419,13 @@
* @param r the service to start.
* @param isStartService True if it's called from Context.startService().
* False if it's called from Context.startForegroundService() or
- * Service.startService().
+ * Service.startForeground().
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
boolean isStartService) {
- final long now = SystemClock.elapsedRealtime();
-
// Check DeviceConfig flag.
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
r.mAllowWhileInUsePermissionInFgs = true;
@@ -7580,60 +7443,30 @@
isBindService);
// We store them to compare the old and new while-in-use logics to each other.
// (They're not used for any other purposes.)
- if (isBindService) {
- r.mDebugWhileInUseReasonInBindService = allowWhileInUse;
- } else {
- r.mDebugWhileInUseReasonInStartForeground = allowWhileInUse;
- }
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
- newlyAllowed |= r.mAllowWhileInUsePermissionInFgs;
}
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
backgroundStartPrivileges, isBindService);
- newlyAllowed |= r.mAllowStartForeground != REASON_DENIED;
}
} else {
allowWhileInUse = REASON_UNKNOWN;
}
r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
-
- if (isStartService && !r.isForeground && newlyAllowed) {
- // If it's called by Context.startService() (not by startForegroundService()),
- // and we're setting "allowed", then we can't fully trust it yet -- we'll need to reset
- // the restrictions if startForeground() is called after the grace period.
- r.mLastUntrustedSetFgsRestrictionAllowedTime = now;
- }
}
/**
* Reset various while-in-use and BFSL related information.
*/
void resetFgsRestrictionLocked(ServiceRecord r) {
- resetFgsRestrictionLocked(r, /*resetDebugWhileInUseReasonInBindService=*/ true);
- }
-
- /**
- * Reset various while-in-use and BFSL related information.
- */
- void resetFgsRestrictionLocked(ServiceRecord r,
- boolean resetDebugWhileInUseReasonInBindService) {
r.mAllowWhileInUsePermissionInFgs = false;
r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
- r.mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
-
- // In Service.startForeground(), we reset this field using a legacy logic,
- // so resetting this field is optional.
- if (resetDebugWhileInUseReasonInBindService) {
- r.mDebugWhileInUseReasonInBindService = REASON_DENIED;
- }
r.mAllowStartForeground = REASON_DENIED;
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
r.mLoggedInfoAllowStartForeground = false;
- r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
}
@@ -7668,22 +7501,11 @@
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
- return shouldAllowFgsWhileInUsePermissionLocked(callingPackage,
- callingPid, callingUid, targetProcess, backgroundStartPrivileges, isBindService,
- /* enableFgsWhileInUseFix =*/ mAm.mConstants.mEnableFgsWhileInUseFix);
- }
-
- private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
- int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
- boolean enableFgsWhileInUseFix) {
int ret = REASON_DENIED;
- // Define some local variables for better readability...
- final boolean useOldLogic = !enableFgsWhileInUseFix;
final boolean forStartForeground = !isBindService;
- if (useOldLogic || forStartForeground) {
+ if (forStartForeground) {
final int uidState = mAm.getUidStateLocked(callingUid);
if (ret == REASON_DENIED) {
// Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
@@ -7733,10 +7555,6 @@
}
}
- if (enableFgsWhileInUseFix && ret == REASON_DENIED) {
- ret = shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
- }
-
if (ret == REASON_DENIED) {
// Allow FGS while-in-use if the WindowManager allows background activity start.
// This is mainly to get the 10 seconds grace period if any activity in the caller has
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3b44633..44e198b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1058,13 +1058,6 @@
/** @see #KEY_USE_MODERN_TRIM */
public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
- private static final String KEY_ENABLE_FGS_WHILE_IN_USE_FIX =
- "key_enable_fgs_while_in_use_fix";
-
- private static final boolean DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX = false;
-
- public volatile boolean mEnableFgsWhileInUseFix = DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX;
-
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1233,9 +1226,6 @@
case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
updateEnableWaitForFinishAttachApplication();
break;
- case KEY_ENABLE_FGS_WHILE_IN_USE_FIX:
- updateEnableFgsWhileInUseFix();
- break;
case KEY_MAX_PREVIOUS_TIME:
updateMaxPreviousTime();
break;
@@ -2005,12 +1995,6 @@
DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
}
- private void updateEnableFgsWhileInUseFix() {
- mEnableFgsWhileInUseFix = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_ENABLE_FGS_WHILE_IN_USE_FIX,
- DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX);
- }
private void updateUseTieredCachedAdj() {
USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2211,9 +2195,6 @@
pw.print(" "); pw.print(KEY_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
pw.print("="); pw.println(mFlagSystemExemptPowerRestrictionsEnabled);
- pw.print(" "); pw.print(KEY_ENABLE_FGS_WHILE_IN_USE_FIX);
- pw.print("="); pw.println(mEnableFgsWhileInUseFix);
-
pw.print(" "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION);
pw.print("="); pw.println(mShortFgsTimeoutDuration);
pw.print(" "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 460ce44..1bd0675 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4854,7 +4854,7 @@
}
checkTime(startTime, "finishAttachApplicationInner: "
+ "after dispatching broadcasts");
- } catch (Exception e) {
+ } catch (BroadcastDeliveryFailedException e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
diff --git a/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java b/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java
new file mode 100644
index 0000000..9c92816
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.am;
+
+import android.util.AndroidException;
+
+/**
+ * Exception to represent that broadcast could not be delivered.
+ */
+public class BroadcastDeliveryFailedException extends AndroidException {
+ public BroadcastDeliveryFailedException(String name) {
+ super(name);
+ }
+
+ public BroadcastDeliveryFailedException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 9b53af2..0b5b1cb 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -280,6 +280,25 @@
}
/**
+ * Re-enqueue the active broadcast so that it can be made active and delivered again. In order
+ * to keep its previous position same to avoid issues with reordering, insert it at the head
+ * of the queue.
+ *
+ * Callers are responsible for clearing the active broadcast by calling
+ * {@link #makeActiveIdle()} after re-enqueuing it.
+ */
+ public void reEnqueueActiveBroadcast() {
+ final BroadcastRecord record = getActive();
+ final int recordIndex = getActiveIndex();
+
+ final SomeArgs broadcastArgs = SomeArgs.obtain();
+ broadcastArgs.arg1 = record;
+ broadcastArgs.argi1 = recordIndex;
+ getQueueForBroadcast(record).addFirst(broadcastArgs);
+ onBroadcastEnqueued(record, recordIndex);
+ }
+
+ /**
* Searches from newest to oldest in the pending broadcast queues, and at the first matching
* pending broadcast it finds, replaces it in-place and returns -- does not attempt to handle
* "duplicate" broadcasts in the queue.
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8e76e5b..e38a2ee 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -148,7 +148,8 @@
* dispatching a pending broadcast
*/
@GuardedBy("mService")
- public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
+ public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
+ throws BroadcastDeliveryFailedException;
/**
* Signal from OS internals that the given process has timed out during
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index e389821..4eedfe2 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -438,7 +438,8 @@
scheduleBroadcastsLocked();
}
- public boolean onApplicationAttachedLocked(ProcessRecord app) {
+ public boolean onApplicationAttachedLocked(ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
updateUidReadyForBootCompletedBroadcastLocked(app.uid);
if (mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
@@ -460,7 +461,8 @@
skipCurrentOrPendingReceiverLocked(app);
}
- public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ public boolean sendPendingBroadcastsLocked(ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
@@ -483,7 +485,7 @@
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
- throw new RuntimeException(e.getMessage());
+ throw new BroadcastDeliveryFailedException(e);
}
}
return didSomething;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 22c0855..bb5fcbe 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -469,10 +469,15 @@
if (DEBUG_BROADCAST) logv("Promoting " + queue
+ " from runnable to running; process is " + queue.app);
promoteToRunningLocked(queue);
- final boolean completed;
+ boolean completed;
if (processWarm) {
updateOomAdj |= queue.runningOomAdjusted;
- completed = scheduleReceiverWarmLocked(queue);
+ try {
+ completed = scheduleReceiverWarmLocked(queue);
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
+ completed = true;
+ }
} else {
completed = scheduleReceiverColdLocked(queue);
}
@@ -513,7 +518,9 @@
private void clearInvalidPendingColdStart() {
logw("Clearing invalid pending cold start: " + mRunningColdStart);
- onApplicationCleanupLocked(mRunningColdStart.app);
+ mRunningColdStart.reEnqueueActiveBroadcast();
+ demoteFromRunningLocked(mRunningColdStart);
+ clearRunningColdStart();
}
private void checkPendingColdStartValidity() {
@@ -535,8 +542,19 @@
}
}
+ private void reEnqueueActiveBroadcast(@NonNull BroadcastProcessQueue queue) {
+ checkState(queue.isActive(), "isActive");
+
+ final BroadcastRecord record = queue.getActive();
+ final int index = queue.getActiveIndex();
+ setDeliveryState(queue, queue.app, record, index, record.receivers.get(index),
+ BroadcastRecord.DELIVERY_PENDING, "reEnqueueActiveBroadcast");
+ queue.reEnqueueActiveBroadcast();
+ }
+
@Override
- public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app) {
+ public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
// Process records can be recycled, so always start by looking up the
// relevant per-process queue
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -557,8 +575,14 @@
queue.traceProcessEnd();
queue.traceProcessRunningBegin();
- if (scheduleReceiverWarmLocked(queue)) {
+ try {
+ if (scheduleReceiverWarmLocked(queue)) {
+ demoteFromRunningLocked(queue);
+ }
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
+ throw e;
}
// We might be willing to kick off another cold start
@@ -588,14 +612,7 @@
}
if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
- // We've been waiting for this app to cold start, and it had
- // trouble; clear the slot and fail delivery below
- mRunningColdStart = null;
-
- queue.traceProcessEnd();
-
- // We might be willing to kick off another cold start
- enqueueUpdateRunningList();
+ clearRunningColdStart();
}
if (queue != null) {
@@ -618,6 +635,17 @@
}
}
+ private void clearRunningColdStart() {
+ mRunningColdStart.traceProcessEnd();
+
+ // We've been waiting for this app to cold start, and it had
+ // trouble; clear the slot and fail delivery below
+ mRunningColdStart = null;
+
+ // We might be willing to kick off another cold start
+ enqueueUpdateRunningList();
+ }
+
@Override
public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -836,7 +864,8 @@
*/
@CheckResult
@GuardedBy("mService")
- private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
+ private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue)
+ throws BroadcastDeliveryFailedException {
checkState(queue.isActive(), "isActive");
final int cookie = traceBegin("scheduleReceiverWarmLocked");
@@ -918,7 +947,7 @@
*/
@CheckResult
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index) {
+ @NonNull BroadcastRecord r, int index) throws BroadcastDeliveryFailedException {
final ProcessRecord app = queue.app;
final Object receiver = r.receivers.get(index);
@@ -1005,6 +1034,11 @@
logw(msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
+ // If we were trying to deliver a manifest broadcast, throw the error as we need
+ // to try redelivering the broadcast to this receiver.
+ if (receiver instanceof ResolveInfo) {
+ throw new BroadcastDeliveryFailedException(e);
+ }
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"remote app");
return false;
@@ -1092,7 +1126,7 @@
boolean waitForServices) {
final BroadcastProcessQueue queue = getProcessQueue(app);
if ((queue == null) || !queue.isActive()) {
- logw("Ignoring finish; no active broadcast for " + queue);
+ logw("Ignoring finishReceiverLocked; no active broadcast for " + queue);
return false;
}
@@ -1129,7 +1163,13 @@
// We're on a roll; move onto the next broadcast for this process
queue.makeActiveNextPending();
- if (scheduleReceiverWarmLocked(queue)) {
+ try {
+ if (scheduleReceiverWarmLocked(queue)) {
+ demoteFromRunningLocked(queue);
+ return true;
+ }
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
return true;
}
@@ -1166,7 +1206,7 @@
private void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,
@DeliveryState int deliveryState, @NonNull String reason) {
if (!queue.isActive()) {
- logw("Ignoring finish; no active broadcast for " + queue);
+ logw("Ignoring finishReceiverActiveLocked; no active broadcast for " + queue);
return;
}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 564b1fe..863dd63 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -699,6 +699,9 @@
break;
}
switch (newDeliveryState) {
+ case DELIVERY_PENDING:
+ scheduledTime[index] = 0;
+ break;
case DELIVERY_SCHEDULED:
scheduledTime[index] = SystemClock.uptimeMillis();
break;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 1f39d1b..dccbb0a 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -177,12 +177,6 @@
@PowerExemptionManager.ReasonCode
int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
- // Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare
- // the old and new logics.
- // TODO: Remove them once we're confident in the new logic.
- int mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
- int mDebugWhileInUseReasonInBindService = REASON_DENIED;
-
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
/** Allow scheduling user-initiated jobs from the background. */
@@ -224,13 +218,6 @@
// is called.
int mStartForegroundCount;
- // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground was set to "allowed"
- // from "disallowed" when the service was _not_ already a foreground service.
- // this means they're set in startService(). (not startForegroundService)
- // In startForeground(), if this timestamp is too old, we can't trust these flags, so
- // we need to reset them.
- long mLastUntrustedSetFgsRestrictionAllowedTime;
-
// This is a service record of a FGS delegate (not a service record of a real service)
boolean mIsFgsDelegate;
@Nullable ForegroundServiceDelegation mFgsDelegation;
@@ -624,12 +611,6 @@
pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
pw.println(PowerExemptionManager.reasonCodeToString(mAllowWhileInUsePermissionInFgsReason));
- pw.print(prefix); pw.print("debugWhileInUseReasonInStartForeground=");
- pw.println(PowerExemptionManager.reasonCodeToString(
- mDebugWhileInUseReasonInStartForeground));
- pw.print(prefix); pw.print("debugWhileInUseReasonInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mDebugWhileInUseReasonInBindService));
-
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
@@ -642,10 +623,6 @@
pw.print(prefix); pw.print("infoAllowStartForeground=");
pw.println(mInfoAllowStartForeground);
- pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime=");
- TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw);
- pw.println();
-
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index de42370..651828b 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -40,6 +40,7 @@
private final LogicalDisplay mLogicalDisplay;
+ private int mUserSerial;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -56,13 +57,15 @@
@GuardedBy("mSyncRoot")
private float mBrightness;
- BrightnessSetting(@NonNull PersistentDataStore persistentDataStore,
+ BrightnessSetting(int userSerial,
+ @NonNull PersistentDataStore persistentDataStore,
@NonNull LogicalDisplay logicalDisplay,
DisplayManagerService.SyncRoot syncRoot) {
mPersistentDataStore = persistentDataStore;
mLogicalDisplay = logicalDisplay;
+ mUserSerial = userSerial;
mBrightness = mPersistentDataStore.getBrightness(
- mLogicalDisplay.getPrimaryDisplayDeviceLocked());
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked(), userSerial);
mSyncRoot = syncRoot;
}
@@ -96,8 +99,13 @@
mListeners.remove(l);
}
+ /** Sets the user serial for the brightness setting */
+ public void setUserSerial(int userSerial) {
+ mUserSerial = userSerial;
+ }
+
/**
- * Sets the brigtness and broadcasts the change to the listeners.
+ * Sets the brightness and broadcasts the change to the listeners.
* @param brightness The value to which the brightness is to be set.
*/
public void setBrightness(float brightness) {
@@ -112,7 +120,8 @@
// changed.
if (brightness != mBrightness) {
mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
- brightness);
+ brightness, mUserSerial
+ );
}
mBrightness = brightness;
int toSend = Float.floatToIntBits(mBrightness);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index ca482dc..7a797dd 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -889,6 +889,13 @@
}
/**
+ * @return true if there is sdrHdrRatioMap, false otherwise.
+ */
+ public boolean hasSdrToHdrRatioSpline() {
+ return mSdrToHdrRatioSpline != null;
+ }
+
+ /**
* Calculate the HDR brightness for the specified SDR brightenss, restricted by the
* maxDesiredHdrSdrRatio (the ratio between the HDR luminance and SDR luminance)
*
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3844529..be65c53 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -652,6 +652,12 @@
logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(),
userSerial);
dpc.setBrightnessConfiguration(config, /* shouldResetShortTermModel= */ true);
+ // change the brightness value according to the selected user.
+ final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked();
+ if (device != null) {
+ dpc.setBrightness(
+ mPersistentDataStore.getBrightness(device, userSerial), userSerial);
+ }
}
dpc.onSwitchUser(newUserId);
});
@@ -3134,8 +3140,9 @@
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
- final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
- display, mSyncRoot);
+ final int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
+ final BrightnessSetting brightnessSetting = new BrightnessSetting(userSerial,
+ mPersistentDataStore, display, mSyncRoot);
final DisplayPowerControllerInterface displayPowerController;
// If display is internal and has a HighBrightnessModeMetadata mapping, use that.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2b5523a..7d8bde9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2664,9 +2664,10 @@
}
@Override
- public void setBrightness(float brightnessValue) {
+ public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
+ mBrightnessSetting.setUserSerial(userSerial);
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index cc9f539..040cecc 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -2172,8 +2172,8 @@
}
@Override
- public void setBrightness(float brightnessValue) {
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ public void setBrightness(float brightnessValue, int userSerial) {
+ mDisplayBrightnessController.setBrightness(brightnessValue, userSerial);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 73edb97..5fbbcbd 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -29,7 +29,7 @@
* An interface to manage the display's power state and brightness
*/
public interface DisplayPowerControllerInterface {
-
+ int DEFAULT_USER_SERIAL = -1;
/**
* Notified when the display is changed.
*
@@ -98,7 +98,17 @@
* Set the screen brightness of the associated display
* @param brightness The value to which the brightness is to be set
*/
- void setBrightness(float brightness);
+ default void setBrightness(float brightness) {
+ setBrightness(brightness, DEFAULT_USER_SERIAL);
+ }
+
+ /**
+ * Set the screen brightness of the associated display
+ * @param brightness The value to which the brightness is to be set
+ * @param userSerial The user for which the brightness value is to be set. Use userSerial = -1,
+ * if brightness needs to be updated for the current user.
+ */
+ void setBrightness(float brightness, int userSerial);
/**
* Checks if the proximity sensor is available
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 79984c9..c7c0fab 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -888,7 +888,9 @@
BrightnessSynchronizer.brightnessFloatToInt(
sdrBrightnessState));
- handleHdrSdrNitsChanged(nits, sdrNits);
+ if (getDisplayDeviceConfig().hasSdrToHdrRatioSpline()) {
+ handleHdrSdrNitsChanged(nits, sdrNits);
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 6d6ed72..2d7792d 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -133,6 +133,7 @@
private static final String TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY =
"brightness-nits-for-default-display";
+ public static final int DEFAULT_USER_ID = -1;
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -294,7 +295,7 @@
return false;
}
- public float getBrightness(DisplayDevice device) {
+ public float getBrightness(DisplayDevice device, int userSerial) {
if (device == null || !device.hasStableUniqueId()) {
return Float.NaN;
}
@@ -302,10 +303,10 @@
if (state == null) {
return Float.NaN;
}
- return state.getBrightness();
+ return state.getBrightness(userSerial);
}
- public boolean setBrightness(DisplayDevice displayDevice, float brightness) {
+ public boolean setBrightness(DisplayDevice displayDevice, float brightness, int userSerial) {
if (displayDevice == null || !displayDevice.hasStableUniqueId()) {
return false;
}
@@ -314,7 +315,7 @@
return false;
}
final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
- if (state.setBrightness(brightness)) {
+ if (state.setBrightness(brightness, userSerial)) {
setDirty();
return true;
}
@@ -611,6 +612,7 @@
state.saveToXml(serializer);
serializer.endTag(null, TAG_DISPLAY);
}
+
serializer.endTag(null, TAG_DISPLAY_STATES);
serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
mStableDeviceValues.saveToXml(serializer);
@@ -649,7 +651,8 @@
private static final class DisplayState {
private int mColorMode;
- private float mBrightness = Float.NaN;
+
+ private SparseArray<Float> mPerUserBrightness = new SparseArray<>();
private int mWidth;
private int mHeight;
private float mRefreshRate;
@@ -670,16 +673,25 @@
return mColorMode;
}
- public boolean setBrightness(float brightness) {
- if (brightness == mBrightness) {
+ public boolean setBrightness(float brightness, int userSerial) {
+ // Remove the default user brightness, before setting a new user-specific value.
+ // This is a one-time operation, required to restructure the config after user-specific
+ // brightness was introduced.
+ mPerUserBrightness.remove(DEFAULT_USER_ID);
+
+ if (getBrightness(userSerial) == brightness) {
return false;
}
- mBrightness = brightness;
+ mPerUserBrightness.set(userSerial, brightness);
return true;
}
- public float getBrightness() {
- return mBrightness;
+ public float getBrightness(int userSerial) {
+ float brightness = mPerUserBrightness.get(userSerial, Float.NaN);
+ if (Float.isNaN(brightness)) {
+ brightness = mPerUserBrightness.get(DEFAULT_USER_ID, Float.NaN);
+ }
+ return brightness;
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -729,12 +741,7 @@
mColorMode = Integer.parseInt(value);
break;
case TAG_BRIGHTNESS_VALUE:
- String brightness = parser.nextText();
- try {
- mBrightness = Float.parseFloat(brightness);
- } catch (NumberFormatException e) {
- mBrightness = Float.NaN;
- }
+ loadBrightnessFromXml(parser);
break;
case TAG_BRIGHTNESS_CONFIGURATIONS:
mDisplayBrightnessConfigurations.loadFromXml(parser);
@@ -760,11 +767,12 @@
serializer.text(Integer.toString(mColorMode));
serializer.endTag(null, TAG_COLOR_MODE);
- serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
- if (!Float.isNaN(mBrightness)) {
- serializer.text(Float.toString(mBrightness));
+ for (int i = 0; i < mPerUserBrightness.size(); i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
+ serializer.attributeInt(null, ATTR_USER_SERIAL, mPerUserBrightness.keyAt(i));
+ serializer.text(Float.toString(mPerUserBrightness.valueAt(i)));
+ serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
}
- serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
mDisplayBrightnessConfigurations.saveToXml(serializer);
@@ -785,12 +793,33 @@
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "ColorMode=" + mColorMode);
- pw.println(prefix + "BrightnessValue=" + mBrightness);
+ pw.println(prefix + "BrightnessValues: ");
+ for (int i = 0; i < mPerUserBrightness.size(); i++) {
+ pw.println("User: " + mPerUserBrightness.keyAt(i)
+ + " Value: " + mPerUserBrightness.valueAt(i));
+ }
pw.println(prefix + "DisplayBrightnessConfigurations: ");
mDisplayBrightnessConfigurations.dump(pw, prefix);
pw.println(prefix + "Resolution=" + mWidth + " " + mHeight);
pw.println(prefix + "RefreshRate=" + mRefreshRate);
}
+
+ private void loadBrightnessFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int userSerial;
+ try {
+ userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL);
+ } catch (NumberFormatException | XmlPullParserException e) {
+ userSerial = DEFAULT_USER_ID;
+ Slog.e(TAG, "Failed to read user serial", e);
+ }
+ String brightness = parser.nextText();
+ try {
+ mPerUserBrightness.set(userSerial, Float.parseFloat(brightness));
+ } catch (NumberFormatException nfe) {
+ Slog.e(TAG, "Failed to read brightness", nfe);
+ }
+ }
}
private static final class StableDeviceValues {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 7574de8..2f52b70 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -38,6 +38,8 @@
* display. Applies the chosen brightness.
*/
public final class DisplayBrightnessController {
+ private static final int DEFAULT_USER_SERIAL = -1;
+
// The ID of the display tied to this DisplayBrightnessController
private final int mDisplayId;
@@ -274,8 +276,16 @@
* Notifies the brightnessSetting to persist the supplied brightness value.
*/
public void setBrightness(float brightnessValue) {
+ setBrightness(brightnessValue, DEFAULT_USER_SERIAL);
+ }
+
+ /**
+ * Notifies the brightnessSetting to persist the supplied brightness value for a user.
+ */
+ public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually
// update the display with the new value.
+ mBrightnessSetting.setUserSerial(userSerial);
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b1a289f..2806a11 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6053,12 +6053,6 @@
}
@Override
- @WindowManagerFuncs.LidState
- public int getLidState() {
- return mDefaultDisplayPolicy.getLidState();
- }
-
- @Override
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3da7812..887f946 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -218,14 +218,6 @@
* between it and the policy.
*/
public interface WindowManagerFuncs {
- @IntDef(prefix = { "LID_" }, value = {
- LID_ABSENT,
- LID_CLOSED,
- LID_OPEN,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface LidState{}
-
public static final int LID_ABSENT = -1;
public static final int LID_CLOSED = 0;
public static final int LID_OPEN = 1;
@@ -239,9 +231,8 @@
public static final int CAMERA_LENS_COVERED = 1;
/**
- * Returns a {@link LidState} that describes the current state of the lid switch.
+ * Returns a code that describes the current state of the lid switch.
*/
- @LidState
public int getLidState();
/**
@@ -291,7 +282,7 @@
/**
* Convert the lid state to a human readable format.
*/
- static String lidStateToString(@LidState int lid) {
+ static String lidStateToString(int lid) {
switch (lid) {
case LID_ABSENT:
return "LID_ABSENT";
@@ -1250,11 +1241,4 @@
* @return {@code true} if the key will be handled globally.
*/
boolean isGlobalKey(int keyCode);
-
- /**
- * Returns a {@link WindowManagerFuncs.LidState} that describes the current state of
- * the lid switch.
- */
- @WindowManagerFuncs.LidState
- int getLidState();
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 712be36..b8c5b3f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -33,7 +33,6 @@
import static android.os.PowerManagerInternal.wakefulnessToString;
import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -5734,11 +5733,6 @@
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
- if (mPolicy.getLidState() == LID_CLOSED) {
- Slog.d(TAG, "Ignoring wake up call due to the lid being closed");
- return;
- }
-
final long now = mClock.uptimeMillis();
if (eventTime > now) {
Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e825215..9128974 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1004,7 +1004,8 @@
}
private void initAndRegisterDeferredPullers() {
- mUwbManager = mContext.getSystemService(UwbManager.class);
+ mUwbManager = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
+ ? mContext.getSystemService(UwbManager.class) : null;
registerUwbActivityInfo();
}
@@ -2172,6 +2173,9 @@
}
private void registerUwbActivityInfo() {
+ if (mUwbManager == null) {
+ return;
+ }
int tagId = FrameworkStatsLog.UWB_ACTIVITY_INFO;
mStatsManager.setPullAtomCallback(
tagId,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3db0315..2b2100e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5630,11 +5630,18 @@
setClientVisible(visible);
}
+ final DisplayContent displayContent = getDisplayContent();
if (!visible) {
mImeInsetsFrozenUntilStartInput = true;
+ if (usingShellTransitions) {
+ final WindowState wallpaperTarget =
+ displayContent.mWallpaperController.getWallpaperTarget();
+ if (wallpaperTarget != null && wallpaperTarget.mActivityRecord == this) {
+ displayContent.mWallpaperController.hideWallpapers(wallpaperTarget);
+ }
+ }
}
- final DisplayContent displayContent = getDisplayContent();
if (!displayContent.mClosingApps.contains(this)
&& !displayContent.mOpeningApps.contains(this)
&& !fromTransition) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7f9352b..53a4752 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1821,7 +1821,8 @@
// If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
// Note that mStartActivity and source should be in the same Task at this point.
if (mOptions != null && mOptions.isLaunchIntoPip()
- && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
+ && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
+ && balCode != BAL_BLOCK) {
mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
sourceRecord, "launch-into-pip");
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8dcb042..9468861 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -225,7 +225,6 @@
/** Currently it can only be non-null when physical display switch happens. */
private DecorInsets.Cache mCachedDecorInsets;
- @WindowManagerFuncs.LidState
private volatile int mLidState = LID_ABSENT;
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private volatile boolean mHdmiPlugged;
@@ -753,11 +752,10 @@
return mNavigationBarCanMove;
}
- public void setLidState(@WindowManagerFuncs.LidState int lidState) {
+ public void setLidState(int lidState) {
mLidState = lidState;
}
- @WindowManagerFuncs.LidState
public int getLidState() {
return mLidState;
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index edafe06..20ce98c 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -146,11 +146,10 @@
}
} else {
final ActivityRecord ar = w.mActivityRecord;
- final TransitionController tc = w.mTransitionController;
// The animating window can still be visible on screen if it is in transition, so we
// should check whether this window can be wallpaper target even when visibleRequested
// is false.
- if (ar != null && !ar.isVisibleRequested() && !tc.inTransition(ar)) {
+ if (ar != null && !ar.isVisibleRequested() && !ar.isVisible()) {
// An activity that is not going to remain visible shouldn't be the target.
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 79c0349..5ba2283 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -454,8 +454,8 @@
onGlobalPolicyChanged(policyDefinition, enforcingAdmin);
}
- applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
- policyDefinition, enforcingAdmin, /* value= */ null, /* enforcePolicy= */ true);
+ applyGlobalPolicyOnUsersWithLocalPoliciesLocked(policyDefinition, enforcingAdmin,
+ /* value= */ null, /* skipEnforcePolicy= */ false);
sendPolicyResultToAdmin(
enforcingAdmin,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6020711..edb8e0c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11113,7 +11113,7 @@
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
}
- private boolean canUserUseLockTaskLocked(int userId) {
+ private boolean canDPCManagedUserUseLockTaskLocked(int userId) {
if (isUserAffiliatedWithDeviceLocked(userId)) {
return true;
}
@@ -11122,19 +11122,16 @@
if (mOwners.hasDeviceOwner()) {
return false;
}
-
- if (!isPermissionCheckFlagEnabled() && !isPolicyEngineForFinanceFlagEnabled()) {
- final ComponentName profileOwner = getProfileOwnerAsUser(userId);
- if (profileOwner == null) {
- return false;
- }
+
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
+ if (profileOwner == null) {
+ return false;
}
-
// Managed profiles are not allowed to use lock task
if (isManagedProfile(userId)) {
return false;
}
-
+
return true;
}
private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
@@ -11142,7 +11139,8 @@
final int userId = caller.getUserId();
enforceCanQuery(MANAGE_DEVICE_POLICY_LOCK_TASK, caller.getPackageName(), userId);
- if (!canUserUseLockTaskLocked(userId)) {
+ if ((isDeviceOwner(caller) || isProfileOwner(caller))
+ && !canDPCManagedUserUseLockTaskLocked(userId)) {
throw new SecurityException("User " + userId + " is not allowed to use lock task");
}
}
@@ -11158,7 +11156,8 @@
caller.getPackageName(),
userId
);
- if (!canUserUseLockTaskLocked(userId)) {
+ if ((isDeviceOwner(caller) || isProfileOwner(caller))
+ && !canDPCManagedUserUseLockTaskLocked(userId)) {
throw new SecurityException("User " + userId + " is not allowed to use lock task");
}
return enforcingAdmin;
@@ -11169,7 +11168,7 @@
|| isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
final int userId = caller.getUserId();
- if (!canUserUseLockTaskLocked(userId)) {
+ if (!canDPCManagedUserUseLockTaskLocked(userId)) {
throw new SecurityException("User " + userId + " is not allowed to use lock task");
}
}
@@ -14907,8 +14906,7 @@
policy = new LockTaskPolicy(currentPolicy);
policy.setPackages(Set.of(packages));
}
- if (policy.getPackages().isEmpty()
- && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
+ if (policy.getPackages().isEmpty()) {
mDevicePolicyEngine.removeLocalPolicy(
PolicyDefinition.LOCK_TASK,
enforcingAdmin,
@@ -15101,7 +15099,7 @@
final List<UserInfo> userInfos = mUserManager.getAliveUsers();
for (int i = userInfos.size() - 1; i >= 0; i--) {
int userId = userInfos.get(i).id;
- if (canUserUseLockTaskLocked(userId)) {
+ if (canDPCManagedUserUseLockTaskLocked(userId)) {
continue;
}
@@ -20690,7 +20688,7 @@
private void addUserControlDisabledPackages(CallerIdentity caller,
EnforcingAdmin enforcingAdmin, Set<String> packages) {
- if (isCallerDeviceOwner(caller)) {
+ if (isDeviceOwner(caller)) {
mDevicePolicyEngine.setGlobalPolicy(
PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
enforcingAdmin,
@@ -20706,7 +20704,7 @@
private void removeUserControlDisabledPackages(CallerIdentity caller,
EnforcingAdmin enforcingAdmin) {
- if (isCallerDeviceOwner(caller)) {
+ if (isDeviceOwner(caller)) {
mDevicePolicyEngine.removeGlobalPolicy(
PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
enforcingAdmin);
@@ -20718,12 +20716,6 @@
}
}
- private boolean isCallerDeviceOwner(CallerIdentity caller) {
- synchronized (getLockObject()) {
- return getDeviceOwnerUserIdUncheckedLocked() == caller.getUserId();
- }
- }
-
@Override
public List<String> getUserControlDisabledPackages(ComponentName who,
String callerPackageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 454337f..3b048b2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -167,7 +167,7 @@
packages == null ? null : packages.stream().toList());
LocalServices.getService(UsageStatsManagerInternal.class)
.setAdminProtectedPackages(
- packages == null ? null : new ArraySet(packages), userId);
+ packages == null ? null : new ArraySet<>(packages), userId);
});
return true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 6365764..4989f84 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -276,7 +276,11 @@
switch (behavior) {
case SUCCESS:
case SUCCESS_PREDECESSOR:
- mQueue.onApplicationAttachedLocked(deliverRes);
+ try {
+ mQueue.onApplicationAttachedLocked(deliverRes);
+ } catch (BroadcastDeliveryFailedException e) {
+ Log.v(TAG, "Error while invoking onApplicationAttachedLocked", e);
+ }
break;
case FAIL_TIMEOUT:
case FAIL_TIMEOUT_PREDECESSOR:
@@ -1120,6 +1124,7 @@
final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
getUidForPackage(PACKAGE_GREEN));
assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, airplane);
verifyScheduleReceiver(restartedReceiverApp, timezone);
}
@@ -1304,12 +1309,7 @@
final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
getUidForPackage(PACKAGE_ORANGE));
- if (mImpl == Impl.MODERN) {
- // Modern queue does not retry sending a broadcast once any broadcast delivery fails.
- assertNull(receiverGreenApp);
- } else {
- verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
- }
+ verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 5f82ec1..b7dbaf9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -981,6 +982,7 @@
DisplayDevice displayDevice = mListener.addedDisplays.get(0);
// Turn on / initialize
+ assumeTrue(displayDevice.getDisplayDeviceConfig().hasSdrToHdrRatioSpline());
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
0);
changeStateRunnable.run();
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
new file mode 100644
index 0000000..c872a11
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 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.server.contentcapture;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.pm.UserInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test for {@link ContentCaptureManagerService}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksServicesTests:com.android.server.contentcapture.ContentCaptureManagerServiceTest}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SuppressWarnings("GuardedBy") // Service not really running, no need to expose locks
+public class ContentCaptureManagerServiceTest {
+
+ private static final int USER_ID = 1234;
+
+ private static final String PACKAGE_NAME = "com.test.package";
+
+ private static final Context sContext = ApplicationProvider.getApplicationContext();
+
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock private UserManagerInternal mMockUserManagerInternal;
+
+ private ContentCaptureManagerService mContentCaptureManagerService;
+
+ @Before
+ public void setup() {
+ when(mMockUserManagerInternal.getUserInfos()).thenReturn(new UserInfo[0]);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
+ mContentCaptureManagerService = new ContentCaptureManagerService(sContext);
+ }
+
+ @Test
+ public void getOptions_notAllowlisted() {
+ ContentCaptureOptions actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isNull();
+ }
+
+ @Test
+ public void getOptions_allowlisted_contentCaptureReceiver() {
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist(
+ USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null);
+
+ ContentCaptureOptions actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isNotNull();
+ assertThat(actual.enableReceiver).isTrue();
+ assertThat(actual.contentProtectionOptions.enableReceiver).isFalse();
+ assertThat(actual.whitelistedComponents).isNull();
+ }
+
+ @Test
+ public void getOptions_allowlisted_bothReceivers() {
+ mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = true;
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist(
+ USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null);
+
+ ContentCaptureOptions actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isNotNull();
+ assertThat(actual.enableReceiver).isTrue();
+ assertThat(actual.contentProtectionOptions.enableReceiver).isTrue();
+ assertThat(actual.whitelistedComponents).isNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 817b245..642f54c 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -60,6 +60,114 @@
}
@Test
+ public void testLoadBrightness() {
+ final String uniqueDisplayId = "test:123";
+ final DisplayDevice testDisplayDevice = new DisplayDevice(
+ null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <display-states>\n"
+ + " <display unique-id=\"test:123\">\n"
+ + " <brightness-value user-serial=\"1\">0.1</brightness-value>\n"
+ + " <brightness-value user-serial=\"2\">0.2</brightness-value>\n"
+ + " </display>\n"
+ + " </display-states>\n"
+ + "</display-manager-state>\n";
+
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+
+ float brightness = mDataStore.getBrightness(testDisplayDevice, 1);
+ assertEquals(0.1, brightness, 0.01);
+
+ brightness = mDataStore.getBrightness(testDisplayDevice, 2);
+ assertEquals(0.2, brightness, 0.01);
+ }
+
+ @Test
+ public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
+ final String uniqueDisplayId = "test:123";
+ final DisplayDevice testDisplayDevice =
+ new DisplayDevice(null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <display-states>\n"
+ + " <color-mode>0</color-mode>\n"
+ + " <display unique-id=\"test:123\">\n"
+ + " <brightness-value>0.5</brightness-value>\n"
+ + " </display>\n"
+ + " </display-states>\n"
+ + "</display-manager-state>\n";
+
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+
+ float user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ float user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertEquals(0.5, user1Brightness, 0.01);
+ assertEquals(0.5, user2Brightness, 0.01);
+
+ // Override the value for user 2. Default user must have been removed.
+ mDataStore.setBrightness(testDisplayDevice, 0.2f, 2 /* userSerial */ /* brightness*/);
+
+ user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertTrue(Float.isNaN(user1Brightness));
+ assertEquals(0.2f, user2Brightness, 0.01);
+
+ // Override the value for user 1. User-specific brightness values should co-exist.
+ mDataStore.setBrightness(testDisplayDevice, 0.1f, 1 /* userSerial */ /* brightness*/);
+ user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertEquals(0.1f, user1Brightness, 0.01);
+ assertEquals(0.2f, user2Brightness, 0.01);
+
+ // Validate saveIfNeeded writes user-specific brightnes.
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mInjector.setWriteStream(baos);
+ mDataStore.saveIfNeeded();
+ mTestLooper.dispatchAll();
+ assertTrue(mInjector.wasWriteSuccessful());
+ TestInjector newInjector = new TestInjector();
+ PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ newInjector.setReadStream(bais);
+ newDataStore.loadIfNeeded();
+
+ user1Brightness = newDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = newDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ float unknownUserBrightness =
+ newDataStore.getBrightness(testDisplayDevice, 999 /* userSerial */);
+ assertEquals(0.1f, user1Brightness, 0.01);
+ assertEquals(0.2f, user2Brightness, 0.01);
+ assertTrue(Float.isNaN(unknownUserBrightness));
+ }
+
+ @Test
public void testLoadingBrightnessConfigurations() {
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<display-manager-state>\n"
@@ -374,7 +482,7 @@
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
newInjector.setReadStream(bais);
newDataStore.loadIfNeeded();
- assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice)));
+ assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */)));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index e6d3bbc..c4f4838 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -247,6 +248,7 @@
0.0f);
verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable);
verify(mBrightnessSetting).setBrightness(brightnessValue);
+ verify(mBrightnessSetting).setUserSerial(anyInt());
// Does nothing if the value is invalid
mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
@@ -358,4 +360,28 @@
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
verify(mBrightnessSetting, never()).setBrightness(brightness);
}
+
+ @Test
+ public void testChangeBrightnessNitsWhenUserChanges() {
+ float brightnessValue1 = 0.3f;
+ float nits1 = 200f;
+ float brightnessValue2 = 0.5f;
+ float nits2 = 300f;
+ AutomaticBrightnessController automaticBrightnessController =
+ mock(AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1);
+ when(automaticBrightnessController.convertToNits(brightnessValue2)).thenReturn(nits2);
+ mDisplayBrightnessController.setAutomaticBrightnessController(
+ automaticBrightnessController);
+
+ mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */);
+ verify(mBrightnessSetting).setUserSerial(1);
+ verify(mBrightnessSetting).setBrightness(brightnessValue1);
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1);
+
+ mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */);
+ verify(mBrightnessSetting).setUserSerial(2);
+ verify(mBrightnessSetting).setBrightness(brightnessValue2);
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 7aec045..933f002 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -26,9 +26,6 @@
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -148,7 +145,6 @@
@Mock private ActivityManagerInternal mActivityManagerInternalMock;
@Mock private AttentionManagerInternal mAttentionManagerInternalMock;
@Mock private DreamManagerInternal mDreamManagerInternalMock;
- @Mock private WindowManagerPolicy mPolicyMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -209,7 +205,6 @@
.thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
- when(mPolicyMock.getLidState()).thenReturn(LID_ABSENT);
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
@@ -217,7 +212,6 @@
addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
- addLocalServiceMock(WindowManagerPolicy.class, mPolicyMock);
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResourcesSpy = spy(mContextSpy.getResources());
@@ -684,20 +678,6 @@
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
}
- @Test
- public void testWakefulnessAwake_ShouldNotWakeUpWhenLidClosed() {
- when(mPolicyMock.getLidState()).thenReturn(LID_CLOSED);
- createService();
- startSystem();
- forceSleep();
-
- mService.getBinderServiceInstance().wakeUp(mClock.now(),
- PowerManager.WAKE_REASON_POWER_BUTTON,
- "testing IPowerManager.wakeUp()", "pkg.name");
-
- assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
- }
-
/**
* Tests a series of variants that control whether a device wakes-up when it is plugged in
* or docked.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 2cdb497..dd90e04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -400,10 +400,12 @@
new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
.setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
};
+ bar2.mAttrs.setFitInsetsTypes(0);
bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
final int doubleHeightFor90 = STATUS_BAR_HEIGHT * 2;
for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.setFitInsetsTypes(0);
if (i == Surface.ROTATION_90) {
params.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 192632c..adf3f39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -18,8 +18,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -356,9 +354,4 @@
public boolean isGlobalKey(int keyCode) {
return false;
}
-
- @Override
- public int getLidState() {
- return LID_ABSENT;
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 863523f..ddc729f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -485,6 +485,7 @@
new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars())
.setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
};
+ statusBar.mAttrs.setFitInsetsTypes(0);
dc.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
return statusBar;
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 1f120d4..ed9e14f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -97,19 +97,19 @@
if (allStates) {
assertLayers {
this.invoke("entireScreenCovered") { entry ->
- entry.entry.displays.forEach { display ->
+ entry.entry.displays.filter { it.isOn }.forEach { display ->
entry.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
}
} else {
assertLayersStart {
- this.entry.displays.forEach { display ->
+ this.entry.displays.filter { it.isOn }.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
assertLayersEnd {
- this.entry.displays.forEach { display ->
+ this.entry.displays.filter { it.isOn }.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
deleted file mode 100644
index 8b89a8b..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
index e6594c9..a87fae8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
@@ -57,7 +57,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ActivitiesTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ActivityTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
similarity index 94%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
index ac05c76..85344a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
@@ -27,7 +27,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+class ActivityTransitionTestCfArm(flicker: FlickerTest) : ActivityTransitionTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index 3a80c66..5752065 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -55,7 +55,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIconColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
similarity index 95%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index d33a272..d453c1a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
@@ -32,7 +32,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) {
+class OpenAppFromIconColdTestCfArm(flicker: FlickerTest) : OpenAppFromIconColdTest(flicker) {
@Test
@FlakyTest
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
similarity index 95%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index 549183f..e747315 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -38,7 +38,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppAfterCameraTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdAfterCameraTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
similarity index 92%
copy from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
copy to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
index ac05c76..177ad7d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
@@ -27,7 +27,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: FlickerTest) :
+ OpenAppFromIntentColdAfterCameraTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
similarity index 96%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 26f88d2..f45f728 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -58,7 +58,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
similarity index 94%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index d9a99da..0d695f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
@@ -31,7 +31,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTestCfArm(flicker: FlickerTest) : OpenAppColdTest(flicker) {
+class OpenAppFromIntentColdTestCfArm(flicker: FlickerTest) : OpenAppFromIntentColdTest(flicker) {
@FlakyTest(bugId = 273696733)
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
similarity index 96%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 3385830..a42bff5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -58,7 +58,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppWarmTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentWarmTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
similarity index 94%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index d8b38b3..b6ffcb3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
@@ -29,7 +29,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) {
+class OpenAppFromIntentWarmTestCfArm(flicker: FlickerTest) : OpenAppFromIntentWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt
similarity index 96%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt
index b21777b..fd42726 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt
@@ -44,8 +44,8 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromLockNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+open class OpenAppFromLockscreenNotificationColdTest(flicker: FlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
override val openingNotificationsFromLockScreen = true
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt
index ec92ca6..fd051d5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -46,7 +46,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromLockNotificationWarm(flicker: FlickerTest) : OpenAppFromNotificationWarm(flicker) {
+class OpenAppFromLockscreenNotificationWarmTest(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
override val openingNotificationsFromLockScreen = true
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
similarity index 95%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index 009d617..37afa8d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -37,6 +37,8 @@
* Test cold launching an app from a notification from the lock screen when there is an app overlaid
* on the lock screen.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
*/
@RequiresDevice
@@ -44,8 +46,8 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) :
- OpenAppFromLockNotificationCold(flicker) {
+class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: FlickerTest) :
+ OpenAppFromLockscreenNotificationColdTest(flicker) {
private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
// Although we are technically still locked here, the overlay app means we should open the
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
index eae9ca1..30c3ec2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
@@ -28,7 +28,7 @@
import org.junit.Test
/** Base class for app launch tests from lock screen */
-abstract class OpenAppFromLockTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
+abstract class OpenAppFromLockscreenTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 1383ae3..924d03f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -63,7 +63,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTransition(flicker) {
+open class OpenAppFromLockscreenViaIntentTest(flicker: FlickerTest) :
+ OpenAppFromLockscreenTransition(flicker) {
override val testApp = NonResizeableAppHelper(instrumentation)
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt
similarity index 95%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt
index 7bcb910..d873ec5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt
@@ -35,8 +35,6 @@
/**
* Test cold launching an app from a notification.
*
- * This test assumes the device doesn't have AOD enabled
- *
* To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
*/
@RequiresDevice
@@ -44,8 +42,8 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
+open class OpenAppFromNotificationColdTest(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt
similarity index 93%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt
index 8b4a613..fb2a48c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt
@@ -29,8 +29,8 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+class OpenAppFromNotificationColdTestCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
deleted file mode 100644
index 43d28fa..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt
index 425e674d..99668ec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt
@@ -47,15 +47,13 @@
/**
* Test cold launching an app from a notification.
*
- * This test assumes the device doesn't have AOD enabled
- *
* To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromNotificationWarm(flicker: FlickerTest) : OpenAppTransition(flicker) {
+open class OpenAppFromNotificationWarmTest(flicker: FlickerTest) : OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
open val openingNotificationsFromLockScreen = false
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt
similarity index 92%
copy from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
copy to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt
index ac05c76..2a2597e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt
@@ -27,7 +27,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+class OpenAppFromNotificationWarmTestCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index ae9ca80..6ee8ae6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -60,7 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
+class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: FlickerTest) :
OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
override val testApp: StandardAppHelper
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
index 62eee02..e890c99 100644
--- a/tests/InputMethodStressTest/AndroidManifest.xml
+++ b/tests/InputMethodStressTest/AndroidManifest.xml
@@ -19,8 +19,7 @@
package="com.android.inputmethod.stresstest">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application>
- <activity android:name=".ImeStressTestUtil$TestActivity"
- android:configChanges="orientation|screenSize"/>
+ <activity android:name=".ImeStressTestUtil$TestActivity"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 7632ab0..b76a4eb 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -493,6 +493,7 @@
verifyShowBehavior(activity);
}
+ // TODO: Add tests for activities that don't handle the rotation.
@Test
public void testRotateScreenWithKeyboardOn() throws Exception {
Intent intent =
@@ -514,14 +515,14 @@
Thread.sleep(1000);
Log.i(TAG, "Rotate screen right");
assertThat(uiDevice.isNaturalOrientation()).isFalse();
- verifyShowBehavior(activity);
+ verifyRotateBehavior(activity);
uiDevice.setOrientationLeft();
uiDevice.waitForIdle();
Thread.sleep(1000);
Log.i(TAG, "Rotate screen left");
assertThat(uiDevice.isNaturalOrientation()).isFalse();
- verifyShowBehavior(activity);
+ verifyRotateBehavior(activity);
uiDevice.setOrientationNatural();
uiDevice.waitForIdle();
@@ -569,4 +570,36 @@
waitOnMainUntilImeIsShown(editText);
}
}
+
+ private static void verifyRotateBehavior(TestActivity activity) {
+ // Get the new TestActivity after recreation.
+ TestActivity newActivity = TestActivity.getLastCreatedInstance();
+ assertThat(newActivity).isNotNull();
+ assertThat(newActivity).isNotEqualTo(activity);
+
+ EditText newEditText = newActivity.getEditText();
+ int softInputMode = newActivity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+
+ if (hasUnfocusableWindowFlags(newActivity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(newActivity);
+ return;
+ }
+
+ if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+ // After rotation, the keyboard would be hidden only when the flag is
+ // SOFT_INPUT_STATE_ALWAYS_HIDDEN. However, SOFT_INPUT_STATE_HIDDEN is different because
+ // it requires appending SOFT_INPUT_IS_FORWARD_NAVIGATION flag, which won't be added
+ // when rotating the devices (rotating doesn't navigate forward to the next app window.)
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsHidden(newEditText);
+
+ } else {
+ // Other cases, keyboard would be shown.
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsShown(newEditText);
+ }
+ }
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index e16c915..f3c8194 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -45,6 +45,7 @@
import com.android.compatibility.common.util.ThrowingRunnable;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
@@ -296,6 +297,8 @@
private static final String TAG = "ImeStressTestUtil.TestActivity";
private EditText mEditText;
private boolean mIsAnimating;
+ private static WeakReference<TestActivity> sLastCreatedInstance =
+ new WeakReference<>(null);
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -336,6 +339,7 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
+ sLastCreatedInstance = new WeakReference<>(this);
boolean isUnfocusableView = getIntent().getBooleanExtra(UNFOCUSABLE_VIEW, false);
boolean requestFocus = getIntent().getBooleanExtra(REQUEST_FOCUS_ON_CREATE, false);
int softInputFlags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
@@ -378,6 +382,12 @@
}
}
+ /** Get the last created TestActivity instance. */
+ @Nullable
+ public static TestActivity getLastCreatedInstance() {
+ return sLastCreatedInstance.get();
+ }
+
/** Show IME with InputMethodManager. */
public boolean showImeWithInputMethodManager() {
boolean showResult =
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index 039bb4e..46745e9 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -11,7 +11,7 @@
name: "protologtool-lib",
srcs: [
"src/com/android/protolog/tool/**/*.kt",
- ":protolog-common-src",
+ ":protolog-common-no-android-src",
],
static_libs: [
"javaparser",