Merge "Don’t bring App to FG if only creator is allowed to do BAL(1/2)" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index f09036b..207abb2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33157,7 +33157,6 @@
public static class PerformanceHintManager.Session implements java.io.Closeable {
method public void close();
method public void reportActualWorkDuration(long);
- method @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public void reportActualWorkDuration(@NonNull android.os.WorkDuration);
method @FlaggedApi("android.os.adpf_prefer_power_efficiency") public void setPreferPowerEfficiency(boolean);
method public void setThreads(@NonNull int[]);
method public void updateTargetWorkDuration(long);
@@ -33500,7 +33499,6 @@
method public static boolean setCurrentTimeMillis(long);
method public static void sleep(long);
method public static long uptimeMillis();
- method @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public static long uptimeNanos();
}
public class TestLooperManager {
@@ -33766,22 +33764,6 @@
method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibration, @Nullable android.os.VibrationAttributes);
}
- @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public final class WorkDuration implements android.os.Parcelable {
- ctor public WorkDuration();
- ctor public WorkDuration(long, long, long, long);
- method public int describeContents();
- method public long getActualCpuDurationNanos();
- method public long getActualGpuDurationNanos();
- method public long getActualTotalDurationNanos();
- method public long getWorkPeriodStartTimestampNanos();
- method public void setActualCpuDurationNanos(long);
- method public void setActualGpuDurationNanos(long);
- method public void setActualTotalDurationNanos(long);
- method public void setWorkPeriodStartTimestampNanos(long);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.os.WorkDuration> CREATOR;
- }
-
public class WorkSource implements android.os.Parcelable {
ctor public WorkSource();
ctor public WorkSource(android.os.WorkSource);
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index fe85da2..6b43e73 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -17,8 +17,6 @@
package android.os;
-import android.os.WorkDuration;
-
/** {@hide} */
oneway interface IHintSession {
void updateTargetWorkDuration(long targetDurationNanos);
@@ -26,5 +24,4 @@
void close();
void sendHint(int hint);
void setMode(int mode, boolean enabled);
- void reportActualWorkDuration2(in WorkDuration[] workDurations);
}
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index e005910..11084b8 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -103,7 +103,7 @@
* Any call in this class will change its internal data, so you must do your own thread
* safety to protect from racing.
*
- * All timings should be in {@link SystemClock#uptimeNanos()}.
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
*/
public static class Session implements Closeable {
private long mNativeSessionPtr;
@@ -269,40 +269,6 @@
public @Nullable int[] getThreadIds() {
return nativeGetThreadIds(mNativeSessionPtr);
}
-
- /**
- * Reports the work duration for the last cycle of work.
- *
- * The system will attempt to adjust the core placement of the threads within the thread
- * group and/or the frequency of the core on which they are run to bring the actual duration
- * close to the target duration.
- *
- * @param workDuration the work duration of each component.
- * @throws IllegalArgumentException if work period start timestamp is not positive, or
- * actual total duration is not positive, or actual CPU duration is not positive,
- * or actual GPU duration is negative.
- */
- @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
- public void reportActualWorkDuration(@NonNull WorkDuration workDuration) {
- if (workDuration.mWorkPeriodStartTimestampNanos <= 0) {
- throw new IllegalArgumentException(
- "the work period start timestamp should be positive.");
- }
- if (workDuration.mActualTotalDurationNanos <= 0) {
- throw new IllegalArgumentException("the actual total duration should be positive.");
- }
- if (workDuration.mActualCpuDurationNanos <= 0) {
- throw new IllegalArgumentException("the actual CPU duration should be positive.");
- }
- if (workDuration.mActualGpuDurationNanos < 0) {
- throw new IllegalArgumentException(
- "the actual GPU duration should be non negative.");
- }
- nativeReportActualWorkDuration(mNativeSessionPtr,
- workDuration.mWorkPeriodStartTimestampNanos,
- workDuration.mActualTotalDurationNanos,
- workDuration.mActualCpuDurationNanos, workDuration.mActualGpuDurationNanos);
- }
}
private static native long nativeAcquireManager();
@@ -319,7 +285,4 @@
private static native void nativeSetThreads(long nativeSessionPtr, int[] tids);
private static native void nativeSetPreferPowerEfficiency(long nativeSessionPtr,
boolean enabled);
- private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
- long workPeriodStartTimestampNanos, long actualTotalDurationNanos,
- long actualCpuDurationNanos, long actualGpuDurationNanos);
}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index e2a5833..49a0bd3 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -16,7 +16,6 @@
package android.os;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.app.IAlarmManager;
import android.app.time.UnixEpochTime;
@@ -203,8 +202,8 @@
* Returns nanoseconds since boot, not counting time spent in deep sleep.
*
* @return nanoseconds of non-sleep uptime since boot.
+ * @hide
*/
- @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
@CriticalNative
@android.ravenwood.annotation.RavenwoodReplace
public static native long uptimeNanos();
diff --git a/core/java/android/os/WorkDuration.java b/core/java/android/os/WorkDuration.java
deleted file mode 100644
index 4fdc34f..0000000
--- a/core/java/android/os/WorkDuration.java
+++ /dev/null
@@ -1,213 +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 android.os;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-
-import java.util.Objects;
-
-/**
- * WorkDuration contains the measured time in nano seconds of the workload
- * in each component, see
- * {@link PerformanceHintManager.Session#reportActualWorkDuration(WorkDuration)}.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
-@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
-public final class WorkDuration implements Parcelable {
- long mWorkPeriodStartTimestampNanos = 0;
- long mActualTotalDurationNanos = 0;
- long mActualCpuDurationNanos = 0;
- long mActualGpuDurationNanos = 0;
- long mTimestampNanos = 0;
-
- public static final @NonNull Creator<WorkDuration> CREATOR = new Creator<>() {
- @Override
- public WorkDuration createFromParcel(Parcel in) {
- return new WorkDuration(in);
- }
-
- @Override
- public WorkDuration[] newArray(int size) {
- return new WorkDuration[size];
- }
- };
-
- public WorkDuration() {}
-
- public WorkDuration(long workPeriodStartTimestampNanos,
- long actualTotalDurationNanos,
- long actualCpuDurationNanos,
- long actualGpuDurationNanos) {
- mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
- mActualTotalDurationNanos = actualTotalDurationNanos;
- mActualCpuDurationNanos = actualCpuDurationNanos;
- mActualGpuDurationNanos = actualGpuDurationNanos;
- }
-
- /**
- * @hide
- */
- public WorkDuration(long workPeriodStartTimestampNanos,
- long actualTotalDurationNanos,
- long actualCpuDurationNanos,
- long actualGpuDurationNanos,
- long timestampNanos) {
- mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
- mActualTotalDurationNanos = actualTotalDurationNanos;
- mActualCpuDurationNanos = actualCpuDurationNanos;
- mActualGpuDurationNanos = actualGpuDurationNanos;
- mTimestampNanos = timestampNanos;
- }
-
- WorkDuration(@NonNull Parcel in) {
- mWorkPeriodStartTimestampNanos = in.readLong();
- mActualTotalDurationNanos = in.readLong();
- mActualCpuDurationNanos = in.readLong();
- mActualGpuDurationNanos = in.readLong();
- mTimestampNanos = in.readLong();
- }
-
- /**
- * Sets the work period start timestamp in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public void setWorkPeriodStartTimestampNanos(long workPeriodStartTimestampNanos) {
- if (workPeriodStartTimestampNanos <= 0) {
- throw new IllegalArgumentException(
- "the work period start timestamp should be positive.");
- }
- mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
- }
-
- /**
- * Sets the actual total duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public void setActualTotalDurationNanos(long actualTotalDurationNanos) {
- if (actualTotalDurationNanos <= 0) {
- throw new IllegalArgumentException("the actual total duration should be positive.");
- }
- mActualTotalDurationNanos = actualTotalDurationNanos;
- }
-
- /**
- * Sets the actual CPU duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public void setActualCpuDurationNanos(long actualCpuDurationNanos) {
- if (actualCpuDurationNanos <= 0) {
- throw new IllegalArgumentException("the actual CPU duration should be positive.");
- }
- mActualCpuDurationNanos = actualCpuDurationNanos;
- }
-
- /**
- * Sets the actual GPU duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public void setActualGpuDurationNanos(long actualGpuDurationNanos) {
- if (actualGpuDurationNanos < 0) {
- throw new IllegalArgumentException("the actual GPU duration should be non negative.");
- }
- mActualGpuDurationNanos = actualGpuDurationNanos;
- }
-
- /**
- * Returns the work period start timestamp based in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public long getWorkPeriodStartTimestampNanos() {
- return mWorkPeriodStartTimestampNanos;
- }
-
- /**
- * Returns the actual total duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public long getActualTotalDurationNanos() {
- return mActualTotalDurationNanos;
- }
-
- /**
- * Returns the actual CPU duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public long getActualCpuDurationNanos() {
- return mActualCpuDurationNanos;
- }
-
- /**
- * Returns the actual GPU duration in nanoseconds.
- *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
- */
- public long getActualGpuDurationNanos() {
- return mActualGpuDurationNanos;
- }
-
- /**
- * @hide
- */
- public long getTimestampNanos() {
- return mTimestampNanos;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeLong(mWorkPeriodStartTimestampNanos);
- dest.writeLong(mActualTotalDurationNanos);
- dest.writeLong(mActualCpuDurationNanos);
- dest.writeLong(mActualGpuDurationNanos);
- dest.writeLong(mTimestampNanos);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof WorkDuration)) {
- return false;
- }
- WorkDuration workDuration = (WorkDuration) obj;
- return workDuration.mTimestampNanos == this.mTimestampNanos
- && workDuration.mWorkPeriodStartTimestampNanos == this.mWorkPeriodStartTimestampNanos
- && workDuration.mActualTotalDurationNanos == this.mActualTotalDurationNanos
- && workDuration.mActualCpuDurationNanos == this.mActualCpuDurationNanos
- && workDuration.mActualGpuDurationNanos == this.mActualGpuDurationNanos;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mWorkPeriodStartTimestampNanos, mActualTotalDurationNanos,
- mActualCpuDurationNanos, mActualGpuDurationNanos, mTimestampNanos);
- }
-}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index d405d1d..a78f221 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -61,11 +61,4 @@
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
bug: "305067031"
-}
-
-flag {
- name: "adpf_gpu_report_actual_work_duration"
- namespace: "game"
- description: "Guards the ADPF GPU APIs."
- bug: "284324521"
-}
+}
\ No newline at end of file
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index aebe7ea..95bf49f 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -16,16 +16,15 @@
#define LOG_TAG "PerfHint-jni"
-#include <android/performance_hint.h>
+#include "jni.h"
+
#include <dlfcn.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <utils/Log.h>
-
#include <vector>
#include "core_jni_helpers.h"
-#include "jni.h"
namespace android {
@@ -45,11 +44,6 @@
typedef int (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
typedef void (*APH_setPreferPowerEfficiency)(APerformanceHintSession*, bool);
-typedef void (*APH_reportActualWorkDuration2)(APerformanceHintSession*, AWorkDuration*);
-
-typedef AWorkDuration* (*AWD_create)();
-typedef void (*AWD_setTimeNanos)(AWorkDuration*, int64_t);
-typedef void (*AWD_release)(AWorkDuration*);
bool gAPerformanceHintBindingInitialized = false;
APH_getManager gAPH_getManagerFn = nullptr;
@@ -62,14 +56,6 @@
APH_setThreads gAPH_setThreadsFn = nullptr;
APH_getThreadIds gAPH_getThreadIdsFn = nullptr;
APH_setPreferPowerEfficiency gAPH_setPreferPowerEfficiencyFn = nullptr;
-APH_reportActualWorkDuration2 gAPH_reportActualWorkDuration2Fn = nullptr;
-
-AWD_create gAWD_createFn = nullptr;
-AWD_setTimeNanos gAWD_setWorkPeriodStartTimestampNanosFn = nullptr;
-AWD_setTimeNanos gAWD_setActualTotalDurationNanosFn = nullptr;
-AWD_setTimeNanos gAWD_setActualCpuDurationNanosFn = nullptr;
-AWD_setTimeNanos gAWD_setActualGpuDurationNanosFn = nullptr;
-AWD_release gAWD_releaseFn = nullptr;
void ensureAPerformanceHintBindingInitialized() {
if (gAPerformanceHintBindingInitialized) return;
@@ -126,46 +112,9 @@
(APH_setPreferPowerEfficiency)dlsym(handle_,
"APerformanceHint_setPreferPowerEfficiency");
LOG_ALWAYS_FATAL_IF(gAPH_setPreferPowerEfficiencyFn == nullptr,
- "Failed to find required symbol "
+ "Failed to find required symbol"
"APerformanceHint_setPreferPowerEfficiency!");
- gAPH_reportActualWorkDuration2Fn =
- (APH_reportActualWorkDuration2)dlsym(handle_,
- "APerformanceHint_reportActualWorkDuration2");
- LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDuration2Fn == nullptr,
- "Failed to find required symbol "
- "APerformanceHint_reportActualWorkDuration2!");
-
- gAWD_createFn = (AWD_create)dlsym(handle_, "AWorkDuration_create");
- LOG_ALWAYS_FATAL_IF(gAWD_createFn == nullptr,
- "Failed to find required symbol AWorkDuration_create!");
-
- gAWD_setWorkPeriodStartTimestampNanosFn =
- (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setWorkPeriodStartTimestampNanos");
- LOG_ALWAYS_FATAL_IF(gAWD_setWorkPeriodStartTimestampNanosFn == nullptr,
- "Failed to find required symbol "
- "AWorkDuration_setWorkPeriodStartTimestampNanos!");
-
- gAWD_setActualTotalDurationNanosFn =
- (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualTotalDurationNanos");
- LOG_ALWAYS_FATAL_IF(gAWD_setActualTotalDurationNanosFn == nullptr,
- "Failed to find required symbol "
- "AWorkDuration_setActualTotalDurationNanos!");
-
- gAWD_setActualCpuDurationNanosFn =
- (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualCpuDurationNanos");
- LOG_ALWAYS_FATAL_IF(gAWD_setActualCpuDurationNanosFn == nullptr,
- "Failed to find required symbol AWorkDuration_setActualCpuDurationNanos!");
-
- gAWD_setActualGpuDurationNanosFn =
- (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualGpuDurationNanos");
- LOG_ALWAYS_FATAL_IF(gAWD_setActualGpuDurationNanosFn == nullptr,
- "Failed to find required symbol AWorkDuration_setActualGpuDurationNanos!");
-
- gAWD_releaseFn = (AWD_release)dlsym(handle_, "AWorkDuration_release");
- LOG_ALWAYS_FATAL_IF(gAWD_releaseFn == nullptr,
- "Failed to find required symbol AWorkDuration_release!");
-
gAPerformanceHintBindingInitialized = true;
}
@@ -289,25 +238,6 @@
enabled);
}
-static void nativeReportActualWorkDuration2(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
- jlong workPeriodStartTimestampNanos,
- jlong actualTotalDurationNanos,
- jlong actualCpuDurationNanos,
- jlong actualGpuDurationNanos) {
- ensureAPerformanceHintBindingInitialized();
-
- AWorkDuration* workDuration = gAWD_createFn();
- gAWD_setWorkPeriodStartTimestampNanosFn(workDuration, workPeriodStartTimestampNanos);
- gAWD_setActualTotalDurationNanosFn(workDuration, actualTotalDurationNanos);
- gAWD_setActualCpuDurationNanosFn(workDuration, actualCpuDurationNanos);
- gAWD_setActualGpuDurationNanosFn(workDuration, actualGpuDurationNanos);
-
- gAPH_reportActualWorkDuration2Fn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
- workDuration);
-
- gAWD_releaseFn(workDuration);
-}
-
static const JNINativeMethod gPerformanceHintMethods[] = {
{"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
{"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
@@ -319,7 +249,6 @@
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
{"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds},
{"nativeSetPreferPowerEfficiency", "(JZ)V", (void*)nativeSetPreferPowerEfficiency},
- {"nativeReportActualWorkDuration", "(JJJJJ)V", (void*)nativeReportActualWorkDuration2},
};
int register_android_os_PerformanceHintManager(JNIEnv* env) {
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 9b4dec4..20ba427 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -182,42 +182,4 @@
s.setPreferPowerEfficiency(true);
s.setPreferPowerEfficiency(true);
}
-
- @Test
- public void testReportActualWorkDurationWithWorkDurationClass() {
- Session s = createSession();
- assumeNotNull(s);
- s.updateTargetWorkDuration(16);
- s.reportActualWorkDuration(new WorkDuration(1, 12, 8, 6));
- s.reportActualWorkDuration(new WorkDuration(1, 33, 14, 20));
- s.reportActualWorkDuration(new WorkDuration(1, 14, 10, 6));
- }
-
- @Test
- public void testReportActualWorkDurationWithWorkDurationClass_IllegalArgument() {
- Session s = createSession();
- assumeNotNull(s);
- s.updateTargetWorkDuration(16);
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6));
- });
- assertThrows(IllegalArgumentException.class, () -> {
- s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1));
- });
- }
}
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 2ec4524..d659ddd 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -402,8 +402,8 @@
}
@Override
- public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
- @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ public final void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
+ @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
nDrawDoubleRoundRect(mNativeCanvasWrapper,
outer.left, outer.top, outer.right, outer.bottom, outerRadii,
inner.left, inner.top, inner.right, inner.bottom, innerRadii,
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9f2a9ac..fea6c5f 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -336,13 +336,6 @@
APerformanceHint_closeSession; # introduced=Tiramisu
APerformanceHint_setThreads; # introduced=UpsideDownCake
APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
- APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
- AWorkDuration_create; # introduced=VanillaIceCream
- AWorkDuration_release; # introduced=VanillaIceCream
- AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
- AWorkDuration_setActualTotalDurationNanos; # introduced=VanillaIceCream
- AWorkDuration_setActualCpuDurationNanos; # introduced=VanillaIceCream
- AWorkDuration_setActualGpuDurationNanos; # introduced=VanillaIceCream
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c4c8128..c25df6e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -18,14 +18,12 @@
#include <aidl/android/hardware/power/SessionHint.h>
#include <aidl/android/hardware/power/SessionMode.h>
-#include <android/WorkDuration.h>
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
#include <android/performance_hint.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
-#include <inttypes.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
@@ -77,13 +75,10 @@
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
- int reportActualWorkDuration(AWorkDuration* workDuration);
private:
friend struct APerformanceHintManager;
- int reportActualWorkDurationInternal(WorkDuration* workDuration);
-
sp<IHintManager> mHintManager;
sp<IHintSession> mHintSession;
// HAL preferred update rate
@@ -97,7 +92,8 @@
// Last hint reported from sendHint indexed by hint value
std::vector<int64_t> mLastHintSentTimestamp;
// Cached samples
- std::vector<WorkDuration> mActualWorkDurations;
+ std::vector<int64_t> mActualDurationsNanos;
+ std::vector<int64_t> mTimestampsNanos;
};
static IHintManager* gIHintManagerForTesting = nullptr;
@@ -199,7 +195,8 @@
* Most of the workload is target_duration dependent, so now clear the cached samples
* as they are most likely obsolete.
*/
- mActualWorkDurations.clear();
+ mActualDurationsNanos.clear();
+ mTimestampsNanos.clear();
mFirstTargetMetTimestamp = 0;
mLastTargetMetTimestamp = 0;
return 0;
@@ -210,10 +207,43 @@
ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
return EINVAL;
}
+ int64_t now = elapsedRealtimeNano();
+ mActualDurationsNanos.push_back(actualDurationNanos);
+ mTimestampsNanos.push_back(now);
- WorkDuration workDuration(0, actualDurationNanos, actualDurationNanos, 0);
+ if (actualDurationNanos >= mTargetDurationNanos) {
+ // Reset timestamps if we are equal or over the target.
+ mFirstTargetMetTimestamp = 0;
+ } else {
+ // Set mFirstTargetMetTimestamp for first time meeting target.
+ if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
+ (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
+ mFirstTargetMetTimestamp = now;
+ }
+ /**
+ * Rate limit the change if the update is over mPreferredRateNanos since first
+ * meeting target and less than mPreferredRateNanos since last meeting target.
+ */
+ if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
+ now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+ return 0;
+ }
+ mLastTargetMetTimestamp = now;
+ }
- return reportActualWorkDurationInternal(&workDuration);
+ binder::Status ret =
+ mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
+ mFirstTargetMetTimestamp = 0;
+ mLastTargetMetTimestamp = 0;
+ return EPIPE;
+ }
+ mActualDurationsNanos.clear();
+ mTimestampsNanos.clear();
+
+ return 0;
}
int APerformanceHintSession::sendHint(SessionHint hint) {
@@ -292,67 +322,6 @@
return OK;
}
-int APerformanceHintSession::reportActualWorkDuration(AWorkDuration* aWorkDuration) {
- WorkDuration* workDuration = static_cast<WorkDuration*>(aWorkDuration);
- if (workDuration->workPeriodStartTimestampNanos <= 0) {
- ALOGE("%s: workPeriodStartTimestampNanos must be positive", __FUNCTION__);
- return EINVAL;
- }
- if (workDuration->actualTotalDurationNanos <= 0) {
- ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
- return EINVAL;
- }
- if (workDuration->actualCpuDurationNanos <= 0) {
- ALOGE("%s: cpuDurationNanos must be positive", __FUNCTION__);
- return EINVAL;
- }
- if (workDuration->actualGpuDurationNanos < 0) {
- ALOGE("%s: gpuDurationNanos must be non negative", __FUNCTION__);
- return EINVAL;
- }
-
- return reportActualWorkDurationInternal(workDuration);
-}
-
-int APerformanceHintSession::reportActualWorkDurationInternal(WorkDuration* workDuration) {
- int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos;
- int64_t now = uptimeNanos();
- workDuration->timestampNanos = now;
- mActualWorkDurations.push_back(std::move(*workDuration));
-
- if (actualTotalDurationNanos >= mTargetDurationNanos) {
- // Reset timestamps if we are equal or over the target.
- mFirstTargetMetTimestamp = 0;
- } else {
- // Set mFirstTargetMetTimestamp for first time meeting target.
- if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
- (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
- mFirstTargetMetTimestamp = now;
- }
- /**
- * Rate limit the change if the update is over mPreferredRateNanos since first
- * meeting target and less than mPreferredRateNanos since last meeting target.
- */
- if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
- now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
- return 0;
- }
- mLastTargetMetTimestamp = now;
- }
-
- binder::Status ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations);
- if (!ret.isOk()) {
- ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
- ret.exceptionMessage().c_str());
- mFirstTargetMetTimestamp = 0;
- mLastTargetMetTimestamp = 0;
- return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
- }
- mActualWorkDurations.clear();
-
- return 0;
-}
-
// ===================================== C API
APerformanceHintManager* APerformanceHint_getManager() {
return APerformanceHintManager::getInstance();
@@ -407,64 +376,6 @@
return session->setPreferPowerEfficiency(enabled);
}
-int APerformanceHint_reportActualWorkDuration2(APerformanceHintSession* session,
- AWorkDuration* workDuration) {
- if (session == nullptr || workDuration == nullptr) {
- ALOGE("Invalid value: (session %p, workDuration %p)", session, workDuration);
- return EINVAL;
- }
- return session->reportActualWorkDuration(workDuration);
-}
-
-AWorkDuration* AWorkDuration_create() {
- WorkDuration* workDuration = new WorkDuration();
- return static_cast<AWorkDuration*>(workDuration);
-}
-
-void AWorkDuration_release(AWorkDuration* aWorkDuration) {
- if (aWorkDuration == nullptr) {
- ALOGE("%s: aWorkDuration is nullptr", __FUNCTION__);
- }
- delete aWorkDuration;
-}
-
-void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* aWorkDuration,
- int64_t workPeriodStartTimestampNanos) {
- if (aWorkDuration == nullptr || workPeriodStartTimestampNanos <= 0) {
- ALOGE("%s: Invalid value. (AWorkDuration: %p, workPeriodStartTimestampNanos: %" PRIi64 ")",
- __FUNCTION__, aWorkDuration, workPeriodStartTimestampNanos);
- }
- static_cast<WorkDuration*>(aWorkDuration)->workPeriodStartTimestampNanos =
- workPeriodStartTimestampNanos;
-}
-
-void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* aWorkDuration,
- int64_t actualTotalDurationNanos) {
- if (aWorkDuration == nullptr || actualTotalDurationNanos <= 0) {
- ALOGE("%s: Invalid value. (AWorkDuration: %p, actualTotalDurationNanos: %" PRIi64 ")",
- __FUNCTION__, aWorkDuration, actualTotalDurationNanos);
- }
- static_cast<WorkDuration*>(aWorkDuration)->actualTotalDurationNanos = actualTotalDurationNanos;
-}
-
-void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* aWorkDuration,
- int64_t actualCpuDurationNanos) {
- if (aWorkDuration == nullptr || actualCpuDurationNanos <= 0) {
- ALOGE("%s: Invalid value. (AWorkDuration: %p, actualCpuDurationNanos: %" PRIi64 ")",
- __FUNCTION__, aWorkDuration, actualCpuDurationNanos);
- }
- static_cast<WorkDuration*>(aWorkDuration)->actualCpuDurationNanos = actualCpuDurationNanos;
-}
-
-void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* aWorkDuration,
- int64_t actualGpuDurationNanos) {
- if (aWorkDuration == nullptr || actualGpuDurationNanos < 0) {
- ALOGE("%s: Invalid value. (AWorkDuration: %p, actualGpuDurationNanos: %" PRIi64 ")",
- __FUNCTION__, aWorkDuration, actualGpuDurationNanos);
- }
- static_cast<WorkDuration*>(aWorkDuration)->actualGpuDurationNanos = actualGpuDurationNanos;
-}
-
void APerformanceHint_setIHintManagerForTesting(void* iManager) {
delete gHintManagerForTesting;
gHintManagerForTesting = nullptr;
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 4553b49..22d33b1 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "PerformanceHintNativeTest"
-#include <android/WorkDuration.h>
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
#include <android/performance_hint.h>
@@ -61,8 +60,6 @@
MOCK_METHOD(Status, setMode, (int32_t mode, bool enabled), (override));
MOCK_METHOD(Status, close, (), (override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
- MOCK_METHOD(Status, reportActualWorkDuration2,
- (const ::std::vector<android::os::WorkDuration>& workDurations), (override));
};
class PerformanceHintTest : public Test {
@@ -123,7 +120,6 @@
std::vector<int64_t> actualDurations;
actualDurations.push_back(20);
EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1));
- EXPECT_CALL(*iSession, reportActualWorkDuration2(_)).Times(Exactly(1));
result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
EXPECT_EQ(0, result);
@@ -242,125 +238,4 @@
APerformanceHintSession* session =
APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
ASSERT_TRUE(session);
-}
-
-MATCHER_P(WorkDurationEq, expected, "") {
- if (arg.size() != expected.size()) {
- *result_listener << "WorkDuration vectors are different sizes. Expected: "
- << expected.size() << ", Actual: " << arg.size();
- return false;
- }
- for (int i = 0; i < expected.size(); ++i) {
- android::os::WorkDuration expectedWorkDuration = expected[i];
- android::os::WorkDuration actualWorkDuration = arg[i];
- if (!expectedWorkDuration.equalsWithoutTimestamp(actualWorkDuration)) {
- *result_listener << "WorkDuration at [" << i << "] is different: "
- << "Expected: " << expectedWorkDuration
- << ", Actual: " << actualWorkDuration;
- return false;
- }
- }
- return true;
-}
-
-TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) {
- APerformanceHintManager* manager = createManager();
-
- std::vector<int32_t> tids;
- tids.push_back(1);
- tids.push_back(2);
- int64_t targetDuration = 56789L;
-
- StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>();
- sp<IHintSession> session_sp(iSession);
-
- EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
- .Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status())));
-
- APerformanceHintSession* session =
- APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
- ASSERT_TRUE(session);
-
- int64_t targetDurationNanos = 10;
- EXPECT_CALL(*iSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
- int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
- EXPECT_EQ(0, result);
-
- usleep(2); // Sleep for longer than preferredUpdateRateNanos.
- {
- std::vector<android::os::WorkDuration> actualWorkDurations;
- android::os::WorkDuration workDuration(1, 20, 13, 8);
- actualWorkDurations.push_back(workDuration);
-
- EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
- result = APerformanceHint_reportActualWorkDuration2(session,
- static_cast<AWorkDuration*>(
- &workDuration));
- EXPECT_EQ(0, result);
- }
-
- {
- std::vector<android::os::WorkDuration> actualWorkDurations;
- android::os::WorkDuration workDuration(-1, 20, 13, 8);
- actualWorkDurations.push_back(workDuration);
-
- EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
- result = APerformanceHint_reportActualWorkDuration2(session,
- static_cast<AWorkDuration*>(
- &workDuration));
- EXPECT_EQ(22, result);
- }
- {
- std::vector<android::os::WorkDuration> actualWorkDurations;
- android::os::WorkDuration workDuration(1, -20, 13, 8);
- actualWorkDurations.push_back(workDuration);
-
- EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
- result = APerformanceHint_reportActualWorkDuration2(session,
- static_cast<AWorkDuration*>(
- &workDuration));
- EXPECT_EQ(22, result);
- }
- {
- std::vector<android::os::WorkDuration> actualWorkDurations;
- android::os::WorkDuration workDuration(1, 20, -13, 8);
- actualWorkDurations.push_back(workDuration);
-
- EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
- result = APerformanceHint_reportActualWorkDuration2(session,
- static_cast<AWorkDuration*>(
- &workDuration));
- EXPECT_EQ(EINVAL, result);
- }
- {
- std::vector<android::os::WorkDuration> actualWorkDurations;
- android::os::WorkDuration workDuration(1, 20, 13, -8);
- actualWorkDurations.push_back(workDuration);
-
- EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
- result = APerformanceHint_reportActualWorkDuration2(session,
- static_cast<AWorkDuration*>(
- &workDuration));
- EXPECT_EQ(EINVAL, result);
- }
-
- EXPECT_CALL(*iSession, close()).Times(Exactly(1));
- APerformanceHint_closeSession(session);
-}
-
-TEST_F(PerformanceHintTest, TestAWorkDuration) {
- AWorkDuration* aWorkDuration = AWorkDuration_create();
- ASSERT_NE(aWorkDuration, nullptr);
-
- AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1);
- AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20);
- AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13);
- AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8);
- AWorkDuration_release(aWorkDuration);
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 7eb7dac..57af2ba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,7 +19,6 @@
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
-import android.content.res.Configuration
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
@@ -54,8 +53,6 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
-import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -68,7 +65,6 @@
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow
@@ -83,8 +79,8 @@
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.transitions
import com.android.compose.modifiers.thenIf
-import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -93,8 +89,8 @@
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.fold.ui.composable.FoldPosture
import com.android.systemui.fold.ui.composable.foldPosture
+import com.android.systemui.fold.ui.helper.FoldPosture
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -149,10 +145,7 @@
) {
val backgroundColor = MaterialTheme.colorScheme.surface
val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
- val layout =
- calculateLayout(
- isSideBySideSupported = isSideBySideSupported,
- )
+ val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
@@ -163,27 +156,27 @@
val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
when (layout) {
- Layout.STANDARD ->
+ BouncerSceneLayout.STANDARD ->
StandardLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
modifier = childModifier,
)
- Layout.SIDE_BY_SIDE ->
+ BouncerSceneLayout.SIDE_BY_SIDE ->
SideBySideLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- Layout.STACKED ->
+ BouncerSceneLayout.STACKED ->
StackedLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- Layout.SPLIT ->
+ BouncerSceneLayout.SPLIT ->
SplitLayout(
viewModel = viewModel,
dialogFactory = dialogFactory,
@@ -728,58 +721,10 @@
}
}
-@Composable
-private fun calculateLayout(
- isSideBySideSupported: Boolean,
-): Layout {
- val windowSizeClass = LocalWindowSizeClass.current
- val width = windowSizeClass.widthSizeClass
- val height = windowSizeClass.heightSizeClass
- val isLarge = width > WindowWidthSizeClass.Compact && height > WindowHeightSizeClass.Compact
- val isTall =
- when (height) {
- WindowHeightSizeClass.Expanded -> width < WindowWidthSizeClass.Expanded
- WindowHeightSizeClass.Medium -> width < WindowWidthSizeClass.Medium
- else -> false
- }
- val isSquare =
- when (width) {
- WindowWidthSizeClass.Compact -> height == WindowHeightSizeClass.Compact
- WindowWidthSizeClass.Medium -> height == WindowHeightSizeClass.Medium
- WindowWidthSizeClass.Expanded -> height == WindowHeightSizeClass.Expanded
- else -> false
- }
- val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
-
- return when {
- // Small and tall devices (i.e. phone/folded in portrait) or square device not in landscape
- // mode (unfolded with hinge along horizontal plane).
- (!isLarge && isTall) || (isSquare && !isLandscape) -> Layout.STANDARD
- // Small and wide devices (i.e. phone/folded in landscape).
- !isLarge -> Layout.SPLIT
- // Large and tall devices (i.e. tablet in portrait).
- isTall -> Layout.STACKED
- // Large and wide/square devices (i.e. tablet in landscape, unfolded).
- else -> if (isSideBySideSupported) Layout.SIDE_BY_SIDE else Layout.STANDARD
- }
-}
-
interface BouncerSceneDialogFactory {
operator fun invoke(): AlertDialog
}
-/** Enumerates all known adaptive layout configurations. */
-private enum class Layout {
- /** The default UI with the bouncer laid out normally. */
- STANDARD,
- /** The bouncer is displayed vertically stacked with the user switcher. */
- STACKED,
- /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
- SIDE_BY_SIDE,
- /** The bouncer is split in two with both sides shown side-by-side. */
- SPLIT,
-}
-
/** Enumerates all supported user-input area visibilities. */
private enum class UserInputAreaVisibility {
/**
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
new file mode 100644
index 0000000..08b7559
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.bouncer.ui.composable
+
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
+import androidx.compose.runtime.Composable
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
+import com.android.systemui.bouncer.ui.helper.SizeClass
+import com.android.systemui.bouncer.ui.helper.calculateLayoutInternal
+
+/**
+ * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
+ * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.SIDE_BY_SIDE] is replaced by
+ * [BouncerSceneLayout.STANDARD].
+ */
+@Composable
+fun calculateLayout(
+ isSideBySideSupported: Boolean,
+): BouncerSceneLayout {
+ val windowSizeClass = LocalWindowSizeClass.current
+
+ return calculateLayoutInternal(
+ width = windowSizeClass.widthSizeClass.toEnum(),
+ height = windowSizeClass.heightSizeClass.toEnum(),
+ isSideBySideSupported = isSideBySideSupported,
+ )
+}
+
+private fun WindowWidthSizeClass.toEnum(): SizeClass {
+ return when (this) {
+ WindowWidthSizeClass.Compact -> SizeClass.COMPACT
+ WindowWidthSizeClass.Medium -> SizeClass.MEDIUM
+ WindowWidthSizeClass.Expanded -> SizeClass.EXPANDED
+ else -> error("Unsupported WindowWidthSizeClass \"$this\"")
+ }
+}
+
+private fun WindowHeightSizeClass.toEnum(): SizeClass {
+ return when (this) {
+ WindowHeightSizeClass.Compact -> SizeClass.COMPACT
+ WindowHeightSizeClass.Medium -> SizeClass.MEDIUM
+ WindowHeightSizeClass.Expanded -> SizeClass.EXPANDED
+ else -> error("Unsupported WindowHeightSizeClass \"$this\"")
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
index 1c993cf..e77ade9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -23,19 +23,9 @@
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
-import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
-
-sealed interface FoldPosture {
- /** A foldable device that's fully closed/folded or a device that doesn't support folding. */
- data object Folded : FoldPosture
- /** A foldable that's halfway open with the hinge held vertically. */
- data object Book : FoldPosture
- /** A foldable that's halfway open with the hinge held horizontally. */
- data object Tabletop : FoldPosture
- /** A foldable that's fully unfolded / flat. */
- data object FullyUnfolded : FoldPosture
-}
+import com.android.systemui.fold.ui.helper.FoldPosture
+import com.android.systemui.fold.ui.helper.foldPostureInternal
/** Returns the [FoldPosture] of the device currently. */
@Composable
@@ -48,32 +38,6 @@
initialValue = FoldPosture.Folded,
key1 = layoutInfo,
) {
- value =
- layoutInfo
- ?.displayFeatures
- ?.firstNotNullOfOrNull { it as? FoldingFeature }
- .let { foldingFeature ->
- when (foldingFeature?.state) {
- null -> FoldPosture.Folded
- FoldingFeature.State.HALF_OPENED ->
- foldingFeature.orientation.toHalfwayPosture()
- FoldingFeature.State.FLAT ->
- if (foldingFeature.isSeparating) {
- // Dual screen device.
- foldingFeature.orientation.toHalfwayPosture()
- } else {
- FoldPosture.FullyUnfolded
- }
- else -> error("Unsupported state \"${foldingFeature.state}\"")
- }
- }
- }
-}
-
-private fun FoldingFeature.Orientation.toHalfwayPosture(): FoldPosture {
- return when (this) {
- FoldingFeature.Orientation.HORIZONTAL -> FoldPosture.Tabletop
- FoldingFeature.Orientation.VERTICAL -> FoldPosture.Book
- else -> error("Unsupported orientation \"$this\"")
+ value = foldPostureInternal(layoutInfo)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt
new file mode 100644
index 0000000..61b2057
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/fold/ui/helper/FoldPostureTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.fold.ui.helper
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FoldPostureTest : SysuiTestCase() {
+
+ @Test
+ fun foldPosture_whenNull_returnsFolded() {
+ assertThat(foldPostureInternal(null)).isEqualTo(FoldPosture.Folded)
+ }
+
+ @Test
+ fun foldPosture_whenHalfOpenHorizontally_returnsTabletop() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.HALF_OPENED,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Tabletop)
+ }
+
+ @Test
+ fun foldPosture_whenHalfOpenVertically_returnsBook() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.HALF_OPENED,
+ orientation = FoldingFeature.Orientation.VERTICAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Book)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndNotSeparating_returnsFullyUnfolded() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ isSeparating = false,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.FullyUnfolded)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndSeparatingHorizontally_returnsTabletop() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ isSeparating = true,
+ orientation = FoldingFeature.Orientation.HORIZONTAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Tabletop)
+ }
+
+ @Test
+ fun foldPosture_whenFlatAndSeparatingVertically_returnsBook() {
+ assertThat(
+ foldPostureInternal(
+ createWindowLayoutInfo(
+ state = FoldingFeature.State.FLAT,
+ isSeparating = true,
+ orientation = FoldingFeature.Orientation.VERTICAL,
+ )
+ )
+ )
+ .isEqualTo(FoldPosture.Book)
+ }
+
+ private fun createWindowLayoutInfo(
+ state: FoldingFeature.State,
+ orientation: FoldingFeature.Orientation = FoldingFeature.Orientation.VERTICAL,
+ isSeparating: Boolean = false,
+ occlusionType: FoldingFeature.OcclusionType = FoldingFeature.OcclusionType.NONE,
+ ): WindowLayoutInfo {
+ return WindowLayoutInfo(
+ listOf(
+ object : FoldingFeature {
+ override val bounds: Rect = Rect(0, 0, 100, 100)
+ override val isSeparating: Boolean = isSeparating
+ override val occlusionType: FoldingFeature.OcclusionType = occlusionType
+ override val orientation: FoldingFeature.Orientation = orientation
+ override val state: FoldingFeature.State = state
+ }
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index b8f4c0f..7ab44e7 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -53,6 +53,8 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
+ android:focusable="true"
+ android:importantForAccessibility="no"
android:tint="?attr/shadeActive"
android:visibility="gone" />
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
new file mode 100644
index 0000000..5385442
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.bouncer.ui.helper
+
+import androidx.annotation.VisibleForTesting
+
+/** Enumerates all known adaptive layout configurations. */
+enum class BouncerSceneLayout {
+ /** The default UI with the bouncer laid out normally. */
+ STANDARD,
+ /** The bouncer is displayed vertically stacked with the user switcher. */
+ STACKED,
+ /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
+ SIDE_BY_SIDE,
+ /** The bouncer is split in two with both sides shown side-by-side. */
+ SPLIT,
+}
+
+/** Enumerates the supported window size classes. */
+enum class SizeClass {
+ COMPACT,
+ MEDIUM,
+ EXPANDED,
+}
+
+/**
+ * Internal version of `calculateLayout` in the System UI Compose library, extracted here to allow
+ * for testing that's not dependent on Compose.
+ */
+@VisibleForTesting
+fun calculateLayoutInternal(
+ width: SizeClass,
+ height: SizeClass,
+ isSideBySideSupported: Boolean,
+): BouncerSceneLayout {
+ return when (height) {
+ SizeClass.COMPACT -> BouncerSceneLayout.SPLIT
+ SizeClass.MEDIUM ->
+ when (width) {
+ SizeClass.COMPACT -> BouncerSceneLayout.STANDARD
+ SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD
+ SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE
+ }
+ SizeClass.EXPANDED ->
+ when (width) {
+ SizeClass.COMPACT -> BouncerSceneLayout.STANDARD
+ SizeClass.MEDIUM -> BouncerSceneLayout.STACKED
+ SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE
+ }
+ }.takeIf { it != BouncerSceneLayout.SIDE_BY_SIDE || isSideBySideSupported }
+ ?: BouncerSceneLayout.STANDARD
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt b/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt
new file mode 100644
index 0000000..bc1cc4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fold/ui/helper/FoldPosture.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.fold.ui.helper
+
+import androidx.annotation.VisibleForTesting
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+
+sealed interface FoldPosture {
+ /** A foldable device that's fully closed/folded or a device that doesn't support folding. */
+ data object Folded : FoldPosture
+ /** A foldable that's halfway open with the hinge held vertically. */
+ data object Book : FoldPosture
+ /** A foldable that's halfway open with the hinge held horizontally. */
+ data object Tabletop : FoldPosture
+ /** A foldable that's fully unfolded / flat. */
+ data object FullyUnfolded : FoldPosture
+}
+
+/**
+ * Internal version of `foldPosture` in the System UI Compose library, extracted here to allow for
+ * testing that's not dependent on Compose.
+ */
+@VisibleForTesting
+fun foldPostureInternal(layoutInfo: WindowLayoutInfo?): FoldPosture {
+ return layoutInfo
+ ?.displayFeatures
+ ?.firstNotNullOfOrNull { it as? FoldingFeature }
+ .let { foldingFeature ->
+ when (foldingFeature?.state) {
+ null -> FoldPosture.Folded
+ FoldingFeature.State.HALF_OPENED -> foldingFeature.orientation.toHalfwayPosture()
+ FoldingFeature.State.FLAT ->
+ if (foldingFeature.isSeparating) {
+ // Dual screen device.
+ foldingFeature.orientation.toHalfwayPosture()
+ } else {
+ FoldPosture.FullyUnfolded
+ }
+ else -> error("Unsupported state \"${foldingFeature.state}\"")
+ }
+ }
+}
+
+private fun FoldingFeature.Orientation.toHalfwayPosture(): FoldPosture {
+ return when (this) {
+ FoldingFeature.Orientation.HORIZONTAL -> FoldPosture.Tabletop
+ FoldingFeature.Orientation.VERTICAL -> FoldPosture.Book
+ else -> error("Unsupported orientation \"$this\"")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index fa18b35b..052c0da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -12,6 +12,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -22,6 +23,7 @@
import android.widget.Scroller;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -43,6 +45,7 @@
private static final int NO_PAGE = -1;
private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
+ private static final int SINGLE_PAGE_SCROLL_DURATION_MILLIS = 300;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
private static final long BOUNCE_ANIMATION_DURATION = 450L;
private static final int TILE_ANIMATION_STAGGER_DELAY = 85;
@@ -63,8 +66,9 @@
private PageListener mPageListener;
private boolean mListening;
- private Scroller mScroller;
+ @VisibleForTesting Scroller mScroller;
+ /* set of animations used to indicate which tiles were just revealed */
@Nullable
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
@@ -306,6 +310,38 @@
mPageIndicator = indicator;
mPageIndicator.setNumPages(mPages.size());
mPageIndicator.setLocation(mPageIndicatorPosition);
+ mPageIndicator.setOnKeyListener((view, keyCode, keyEvent) -> {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ // only scroll on ACTION_UP as we don't handle longpressing for now. Still we need
+ // to intercept even ACTION_DOWN otherwise keyboard focus will be moved before we
+ // have a chance to intercept ACTION_UP.
+ if (keyEvent.getAction() == KeyEvent.ACTION_UP && mScroller.isFinished()) {
+ scrollByX(getDeltaXForKeyboardScrolling(keyCode),
+ SINGLE_PAGE_SCROLL_DURATION_MILLIS);
+ }
+ return true;
+ }
+ return false;
+ });
+ }
+
+ private int getDeltaXForKeyboardScrolling(int keyCode) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && getCurrentItem() != 0) {
+ return -getWidth();
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ && getCurrentItem() != mPages.size() - 1) {
+ return getWidth();
+ }
+ return 0;
+ }
+
+ private void scrollByX(int x, int durationMillis) {
+ if (x != 0) {
+ mScroller.startScroll(/* startX= */ getScrollX(), /* startY= */ getScrollY(),
+ /* dx= */ x, /* dy= */ 0, /* duration= */ durationMillis);
+ // scroller just sets its state, we need to invalidate view to actually start scrolling
+ postInvalidateOnAnimation();
+ }
}
@Override
@@ -596,9 +632,7 @@
});
setOffscreenPageLimit(lastPageNumber); // Ensure the page to reveal has been inflated.
int dx = getWidth() * lastPageNumber;
- mScroller.startScroll(getScrollX(), getScrollY(), isLayoutRtl() ? -dx : dx, 0,
- REVEAL_SCROLL_DURATION_MILLIS);
- postInvalidateOnAnimation();
+ scrollByX(isLayoutRtl() ? -dx : dx, REVEAL_SCROLL_DURATION_MILLIS);
}
private boolean shouldNotRunAnimation(Set<String> tilesToReveal) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index b30bc56..bc5090f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,12 +30,11 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -282,7 +281,6 @@
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
- private final ActivityStarter mActivityStarter;
@Inject
public Factory(
@@ -290,14 +288,13 @@
UiEventLogger uiEventLogger,
VibratorHelper vibratorHelper,
SystemClock clock,
- @Main CoroutineDispatcher mainDispatcher,
- ActivityStarter activityStarter) {
+ @Main CoroutineDispatcher mainDispatcher
+ ) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
- mActivityStarter = activityStarter;
}
/**
@@ -313,8 +310,6 @@
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
- root.setActivityStarter(mActivityStarter);
-
BrightnessSliderHapticPlugin plugin;
if (hapticBrightnessSlider()) {
plugin = new BrightnessSliderHapticPluginImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 5ecf07f..c885492 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -33,7 +33,6 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
/**
@@ -42,7 +41,6 @@
*/
public class BrightnessSliderView extends FrameLayout {
- private ActivityStarter mActivityStarter;
@NonNull
private ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
@@ -59,10 +57,6 @@
super(context, attrs);
}
- public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
- mActivityStarter = activityStarter;
- }
-
// Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
@@ -71,7 +65,6 @@
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
- mSlider.setActivityStarter(mActivityStarter);
// Finds the progress drawable. Assumes brightness_progress_drawable.xml
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 6ec10da..a5a0ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -23,9 +23,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
-import androidx.annotation.NonNull;
-
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
public class ToggleSeekBar extends SeekBar {
@@ -33,8 +32,6 @@
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin = null;
- private ActivityStarter mActivityStarter;
-
public ToggleSeekBar(Context context) {
super(context);
}
@@ -52,7 +49,7 @@
if (mEnforcedAdmin != null) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
return true;
}
if (!isEnabled()) {
@@ -77,8 +74,4 @@
public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
}
-
- public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
- mActivityStarter = activityStarter;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index de334bb..2338be2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -85,7 +85,9 @@
public void setFooterVisibility(@Visibility int visibility) {
mFooterVisibility = visibility;
- setSecondaryVisible(visibility == View.VISIBLE, false);
+ setSecondaryVisible(/* visible = */ visibility == View.VISIBLE,
+ /* animate = */false,
+ /* onAnimationEnded = */ null);
}
public void setFooterText(@StringRes int text) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 19bce89..fa2748c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
@@ -41,6 +42,7 @@
private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
private val notificationIconAreaController: NotificationIconAreaController,
private val renderListInteractor: RenderNotificationListInteractor,
+ private val activeNotificationsInteractor: ActiveNotificationsInteractor,
) : Coordinator {
override fun attach(pipeline: NotifPipeline) {
@@ -50,7 +52,13 @@
fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
traceSection("StackCoordinator.onAfterRenderList") {
- controller.setNotifStats(calculateNotifStats(entries))
+ val notifStats = calculateNotifStats(entries)
+ if (FooterViewRefactor.isEnabled) {
+ activeNotificationsInteractor.setNotifStats(notifStats)
+ }
+ // TODO(b/293167744): This shouldn't be done if the footer flag is on, once the footer
+ // visibility is handled in the new stack.
+ controller.setNotifStats(notifStats)
if (NotificationIconContainerRefactor.isEnabled || FooterViewRefactor.isEnabled) {
renderListInteractor.setRenderedList(entries)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 12ee54d..5ed82cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
@@ -37,6 +38,9 @@
/** Are any already-seen notifications currently filtered out of the active list? */
val hasFilteredOutSeenNotifications = MutableStateFlow(false)
+
+ /** Stats about the list of notifications attached to the shade */
+ val notifStats = MutableStateFlow(NotifStats.empty)
}
/** Represents the notification list, comprised of groups and individual notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 542f3c4..31893b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -52,4 +53,14 @@
*/
val areAnyNotificationsPresentValue: Boolean
get() = repository.activeNotifications.value.renderList.isNotEmpty()
+
+ /** Are there are any notifications that can be cleared by the "Clear all" button? */
+ val hasClearableNotifications: Flow<Boolean> =
+ repository.notifStats
+ .map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
+ .distinctUntilChanged()
+
+ fun setNotifStats(notifStats: NotifStats) {
+ repository.notifStats.value = notifStats
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 10a43d5..3184d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -46,6 +46,7 @@
import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;
+import java.util.function.Consumer;
public class FooterView extends StackScrollerDecorView {
private static final String TAG = "FooterView";
@@ -63,9 +64,13 @@
private String mSeenNotifsFilteredText;
private Drawable mSeenNotifsFilteredIcon;
+ private @StringRes int mClearAllButtonTextId;
+ private @StringRes int mClearAllButtonDescriptionId;
private @StringRes int mMessageStringId;
private @DrawableRes int mMessageIconId;
+ private OnClickListener mClearAllButtonClickListener;
+
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -84,12 +89,18 @@
return isSecondaryVisible();
}
+ /** See {@link this#setClearAllButtonVisible(boolean, boolean, Consumer)}. */
+ public void setClearAllButtonVisible(boolean visible, boolean animate) {
+ setClearAllButtonVisible(visible, animate, /* onAnimationEnded = */ null);
+ }
+
/**
* Set the visibility of the "Clear all" button to {@code visible}. Animate the change if
* {@code animate} is true.
*/
- public void setClearAllButtonVisible(boolean visible, boolean animate) {
- setSecondaryVisible(visible, animate);
+ public void setClearAllButtonVisible(boolean visible, boolean animate,
+ Consumer<Boolean> onAnimationEnded) {
+ setSecondaryVisible(visible, animate, onAnimationEnded);
}
@Override
@@ -106,6 +117,42 @@
});
}
+ /** Set the text label for the "Clear all" button. */
+ public void setClearAllButtonText(@StringRes int textId) {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
+ if (mClearAllButtonTextId == textId) {
+ return; // nothing changed
+ }
+ mClearAllButtonTextId = textId;
+ updateClearAllButtonText();
+ }
+
+ private void updateClearAllButtonText() {
+ if (mClearAllButtonTextId == 0) {
+ return; // not initialized yet
+ }
+ mClearAllButton.setText(getContext().getString(mClearAllButtonTextId));
+ }
+
+ /** Set the accessibility content description for the "Clear all" button. */
+ public void setClearAllButtonDescription(@StringRes int contentDescriptionId) {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ return;
+ }
+ if (mClearAllButtonDescriptionId == contentDescriptionId) {
+ return; // nothing changed
+ }
+ mClearAllButtonDescriptionId = contentDescriptionId;
+ updateClearAllButtonDescription();
+ }
+
+ private void updateClearAllButtonDescription() {
+ if (mClearAllButtonDescriptionId == 0) {
+ return; // not initialized yet
+ }
+ mClearAllButton.setContentDescription(getContext().getString(mClearAllButtonDescriptionId));
+ }
+
/** Set the string for a message to be shown instead of the buttons. */
public void setMessageString(@StringRes int messageId) {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
@@ -181,6 +228,10 @@
/** Set onClickListener for the clear all (end) button. */
public void setClearAllButtonClickListener(OnClickListener listener) {
+ if (FooterViewRefactor.isEnabled()) {
+ if (mClearAllButtonClickListener == listener) return;
+ mClearAllButtonClickListener = listener;
+ }
mClearAllButton.setOnClickListener(listener);
}
@@ -214,7 +265,28 @@
mManageButton.setText(mManageNotificationText);
mManageButton.setContentDescription(mManageNotificationText);
}
- if (!FooterViewRefactor.isEnabled()) {
+ if (FooterViewRefactor.isEnabled()) {
+ updateClearAllButtonText();
+ updateClearAllButtonDescription();
+
+ updateMessageString();
+ updateMessageIcon();
+ } else {
+ // NOTE: Prior to the refactor, `updateResources` set the class properties to the right
+ // string values. It was always being called together with `updateContent`, which
+ // deals with actually associating those string values with the correct views
+ // (buttons or text).
+ // In the new code, the resource IDs are being set in the view binder (through
+ // setMessageString and similar setters). The setters themselves now deal with
+ // updating both the resource IDs and the views where appropriate (as in, calling
+ // `updateMessageString` when the resource ID changes). This eliminates the need for
+ // `updateResources`, which will eventually be removed. There are, however, still
+ // situations in which we want to update the views even if the resource IDs didn't
+ // change, such as configuration changes.
+ mClearAllButton.setText(R.string.clear_all_notifications_text);
+ mClearAllButton.setContentDescription(
+ mContext.getString(R.string.accessibility_clear_all));
+
mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
mSeenNotifsFooterTextView
.setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
@@ -230,16 +302,8 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateColors();
- mClearAllButton.setText(R.string.clear_all_notifications_text);
- mClearAllButton.setContentDescription(
- mContext.getString(R.string.accessibility_clear_all));
updateResources();
updateContent();
-
- if (FooterViewRefactor.isEnabled()) {
- updateMessageString();
- updateMessageIcon();
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 6d823437..0299114 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.notification.footer.ui.viewbinder
+import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
@@ -28,9 +32,31 @@
fun bind(
footer: FooterView,
viewModel: FooterViewModel,
+ clearAllNotifications: View.OnClickListener,
): DisposableHandle {
+ // Listen for changes when the view is attached.
return footer.repeatWhenAttached {
- // Listen for changes when the view is attached.
+ lifecycleScope.launch {
+ viewModel.clearAllButton.collect { button ->
+ if (button.isVisible.isAnimating) {
+ footer.setClearAllButtonVisible(
+ button.isVisible.value,
+ /* animate = */ true,
+ ) { _ ->
+ button.isVisible.stopAnimating()
+ }
+ } else {
+ footer.setClearAllButtonVisible(
+ button.isVisible.value,
+ /* animate = */ false,
+ )
+ }
+ footer.setClearAllButtonText(button.labelId)
+ footer.setClearAllButtonDescription(button.accessibilityDescriptionId)
+ footer.setClearAllButtonClickListener(clearAllNotifications)
+ }
+ }
+
lifecycleScope.launch {
viewModel.message.collect { message ->
footer.setFooterLabelVisible(message.visible)
diff --git a/core/java/android/os/WorkDuration.aidl b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
similarity index 65%
rename from core/java/android/os/WorkDuration.aidl
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
index 0f61204..ea5abef 100644
--- a/core/java/android/os/WorkDuration.aidl
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package android.os;
+package com.android.systemui.statusbar.notification.footer.ui.viewmodel
-parcelable WorkDuration cpp_header "android/WorkDuration.h";
\ No newline at end of file
+import android.annotation.StringRes
+import com.android.systemui.util.ui.AnimatedValue
+
+data class FooterButtonViewModel(
+ @StringRes val labelId: Int,
+ @StringRes val accessibilityDescriptionId: Int,
+ val isVisible: AnimatedValue<Boolean>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 3d68a7b..721bea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -18,18 +18,51 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.toAnimatedValueFlow
import dagger.Module
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** ViewModel for [FooterView]. */
-class FooterViewModel(seenNotificationsInteractor: SeenNotificationsInteractor) {
+class FooterViewModel(
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ seenNotificationsInteractor: SeenNotificationsInteractor,
+ shadeInteractor: ShadeInteractor,
+) {
+ val clearAllButton: Flow<FooterButtonViewModel> =
+ activeNotificationsInteractor.hasClearableNotifications
+ .sample(
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
+ val shouldAnimate = isShadeFullyExpanded && animationsEnabled
+ AnimatableEvent(hasClearableNotifications, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ .map { visible ->
+ FooterButtonViewModel(
+ labelId = R.string.clear_all_notifications_text,
+ accessibilityDescriptionId = R.string.accessibility_clear_all,
+ isVisible = visible,
+ )
+ }
+
val message: Flow<FooterMessageViewModel> =
seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs ->
FooterMessageViewModel(
@@ -45,10 +78,18 @@
@Provides
@SysUISingleton
fun provideOptional(
+ activeNotificationsInteractor: Provider<ActiveNotificationsInteractor>,
seenNotificationsInteractor: Provider<SeenNotificationsInteractor>,
+ shadeInteractor: Provider<ShadeInteractor>,
): Optional<FooterViewModel> {
return if (FooterViewRefactor.isEnabled) {
- Optional.of(FooterViewModel(seenNotificationsInteractor.get()))
+ Optional.of(
+ FooterViewModel(
+ activeNotificationsInteractor.get(),
+ seenNotificationsInteractor.get(),
+ shadeInteractor.get()
+ )
+ )
} else {
Optional.empty()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 9f2b0a6..8e82442 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -204,6 +204,11 @@
override fun shouldSuppress(entry: NotificationEntry) = !entry.canBubble()
}
+class BubbleAppSuspendedSuppressor :
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "app is suspended") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.ranking.isSuspended
+}
+
class BubbleNoMetadataSuppressor() :
VisualInterruptionFilter(types = setOf(BUBBLE), reason = "has no or invalid bubble metadata") {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 2fffd37..334e08d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -56,6 +56,14 @@
}
}
+ fun logSuspendedAppBubble(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ }, {
+ "No bubble up: notification: app $str1 is suspended"
+ })
+ }
+
fun logNoBubbleNoMetadata(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 4045380..510086d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -204,6 +204,11 @@
return false;
}
+ if (entry.getRanking().isSuspended()) {
+ mLogger.logSuspendedAppBubble(entry);
+ return false;
+ }
+
if (entry.getBubbleMetadata() == null
|| (entry.getBubbleMetadata().getShortcutId() == null
&& entry.getBubbleMetadata().getIntent() == null)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 39a87de..73dd5f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -159,6 +159,7 @@
addFilter(PulseLockscreenVisibilityPrivateSuppressor())
addFilter(PulseLowImportanceSuppressor())
addFilter(BubbleNotAllowedSuppressor())
+ addFilter(BubbleAppSuspendedSuppressor())
addFilter(BubbleNoMetadataSuppressor())
addFilter(HunGroupAlertBehaviorSuppressor())
addFilter(HunJustLaunchedFsiSuppressor())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index ec90a8d..162e8af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -54,7 +54,7 @@
mContent = findContentView();
mSecondaryView = findSecondaryView();
setVisible(false /* visible */, false /* animate */);
- setSecondaryVisible(false /* visible */, false /* animate */);
+ setSecondaryVisible(false /* visible */, false /* animate */, null /* onAnimationEnd */);
setOutlineProvider(null);
}
@@ -155,15 +155,23 @@
/**
* Set the secondary view of this layout to visible.
*
- * @param visible should the secondary view be visible
- * @param animate should the change be animated
+ * @param visible True if the contents should be visible.
+ * @param animate True if we should fade to new visibility.
+ * @param onAnimationEnded Callback to run after visibility updates, takes a boolean as a
+ * parameter that represents whether the animation was cancelled.
*/
- protected void setSecondaryVisible(boolean visible, boolean animate) {
+ protected void setSecondaryVisible(boolean visible, boolean animate,
+ Consumer<Boolean> onAnimationEnded) {
if (mIsSecondaryVisible != visible) {
mSecondaryAnimating = animate;
mIsSecondaryVisible = visible;
- setViewVisible(mSecondaryView, visible, animate,
- (cancelled) -> onSecondaryVisibilityAnimationEnd());
+ Consumer<Boolean> onAnimationEndedWrapper = (cancelled) -> {
+ onContentVisibilityAnimationEnd();
+ if (onAnimationEnded != null) {
+ onAnimationEnded.accept(cancelled);
+ }
+ };
+ setViewVisible(mSecondaryView, visible, animate, onAnimationEndedWrapper);
}
if (!mSecondaryAnimating) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6a34f98..283a593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4608,13 +4608,15 @@
if (mManageButtonClickListener != null) {
mFooterView.setManageButtonClickListener(mManageButtonClickListener);
}
- mFooterView.setClearAllButtonClickListener(v -> {
- if (mFooterClearAllListener != null) {
- mFooterClearAllListener.onClearAll();
- }
- clearNotifications(ROWS_ALL, true /* closeShade */);
- footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
- });
+ if (!FooterViewRefactor.isEnabled()) {
+ mFooterView.setClearAllButtonClickListener(v -> {
+ if (mFooterClearAllListener != null) {
+ mFooterClearAllListener.onClearAll();
+ }
+ clearNotifications(ROWS_ALL, true /* closeShade */);
+ footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
+ });
+ }
if (FooterViewRefactor.isEnabled()) {
updateFooter();
}
@@ -4687,9 +4689,9 @@
}
boolean animate = mIsExpanded && mAnimationsEnabled;
mFooterView.setVisible(visible, animate);
- mFooterView.setClearAllButtonVisible(showDismissView, animate);
mFooterView.showHistory(showHistory);
if (!FooterViewRefactor.isEnabled()) {
+ mFooterView.setClearAllButtonVisible(showDismissView, animate);
mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
}
}
@@ -5355,11 +5357,15 @@
return viewsToRemove;
}
+ /** Clear all clearable notifications when the user requests it. */
+ public void clearAllNotifications() {
+ clearNotifications(ROWS_ALL, /* closeShade = */ true);
+ }
+
/**
* Collects a list of visible rows, and animates them away in a staggered fashion as if they
* were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
*/
- @VisibleForTesting
void clearNotifications(@SelectedRows int selection, boolean closeShade) {
// Animate-swipe all dismissable notifications, then animate the shade closed
final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
@@ -5659,6 +5665,7 @@
}
void setFooterClearAllListener(FooterClearAllListener listener) {
+ FooterViewRefactor.assertInLegacyMode();
mFooterClearAllListener = listener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d5ec0c5..e6315fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -470,7 +470,7 @@
@Override
public void onSnooze(StatusBarNotification sbn,
- NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
+ NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
@@ -592,7 +592,7 @@
@Override
public boolean updateSwipeProgress(View animView, boolean dismissable,
- float swipeProgress) {
+ float swipeProgress) {
// Returning true prevents alpha fading.
return false;
}
@@ -759,8 +759,10 @@
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
- mView.setFooterClearAllListener(() ->
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ if (!FooterViewRefactor.isEnabled()) {
+ mView.setFooterClearAllListener(() ->
+ mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ }
mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
@Override
@@ -1090,7 +1092,7 @@
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
- boolean cancelAnimators) {
+ boolean cancelAnimators) {
mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
}
@@ -1408,14 +1410,14 @@
* Return whether there are any clearable notifications
*/
public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the clear all
- // button in the refactored code
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
+ // visibility in the refactored code
return hasNotifications(selection, true /* clearable */);
}
public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the clear all
- // button in the refactored code
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
+ // visibility in the refactored code
boolean hasAlertingMatchingClearable = isClearable
? mNotifStats.getHasClearableAlertingNotifs()
: mNotifStats.getHasNonClearableAlertingNotifs();
@@ -1454,7 +1456,7 @@
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
- boolean remoteInputActive) {
+ boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
@@ -1573,7 +1575,7 @@
}
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
- @SelectedRows int selectedRows) {
+ @SelectedRows int selectedRows) {
if (selectedRows == ROWS_ALL) {
mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId());
@@ -1665,7 +1667,7 @@
* Set rounded rect clipping bounds on this view.
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
- int bottomRadius) {
+ int bottomRadius) {
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
}
@@ -2021,7 +2023,7 @@
private class NotifStackControllerImpl implements NotifStackController {
@Override
public void setNotifStats(@NonNull NotifStats notifStats) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once clear all visibility
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once footer visibility
// is handled in the refactored stack.
mNotifStats = notifStats;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 54df4ab..4554085 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -19,6 +19,8 @@
import android.view.LayoutInflater
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.traceSection
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.nano.MetricsProto
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.reinflateAndBindLatest
import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
@@ -46,6 +48,7 @@
@Inject
constructor(
private val viewModel: NotificationListViewModel,
+ private val metricsLogger: MetricsLogger,
private val configuration: ConfigurationState,
private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
@@ -100,7 +103,17 @@
attachToRoot = false,
) { footerView: FooterView ->
traceSection("bind FooterView") {
- val disposableHandle = FooterViewBinder.bind(footerView, footerViewModel)
+ val disposableHandle =
+ FooterViewBinder.bind(
+ footerView,
+ footerViewModel,
+ clearAllNotifications = {
+ metricsLogger.action(
+ MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
+ )
+ parentView.clearAllNotifications()
+ },
+ )
parentView.setFooterView(footerView)
return@reinflateAndBindLatest disposableHandle
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
new file mode 100644
index 0000000..395d712
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
@@ -0,0 +1,253 @@
+/*
+ * 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.bouncer.ui.helper
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SIDE_BY_SIDE
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SPLIT
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STACKED
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BouncerSceneLayoutTest : SysuiTestCase() {
+
+ data object Phone :
+ Device(
+ name = "phone",
+ width = SizeClass.COMPACT,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+ data object Tablet :
+ Device(
+ name = "tablet",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Horizontally,
+ )
+ data object Folded :
+ Device(
+ name = "folded",
+ width = SizeClass.COMPACT,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Vertically,
+ )
+ data object Unfolded :
+ Device(
+ name = "unfolded",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.MEDIUM,
+ naturallyHeld = Vertically,
+ widthWhenUnnaturallyHeld = SizeClass.MEDIUM,
+ heightWhenUnnaturallyHeld = SizeClass.MEDIUM,
+ )
+ data object TallerFolded :
+ Device(
+ name = "taller folded",
+ width = SizeClass.COMPACT,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+ data object TallerUnfolded :
+ Device(
+ name = "taller unfolded",
+ width = SizeClass.EXPANDED,
+ height = SizeClass.EXPANDED,
+ naturallyHeld = Vertically,
+ )
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun testCases() =
+ listOf(
+ Phone to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ Tablet to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = STACKED,
+ ),
+ Folded to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ Unfolded to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = STANDARD,
+ ),
+ TallerFolded to
+ Expected(
+ whenNaturallyHeld = STANDARD,
+ whenUnnaturallyHeld = SPLIT,
+ ),
+ TallerUnfolded to
+ Expected(
+ whenNaturallyHeld = SIDE_BY_SIDE,
+ whenUnnaturallyHeld = SIDE_BY_SIDE,
+ ),
+ )
+ .flatMap { (device, expected) ->
+ buildList {
+ // Holding the device in its natural orientation (vertical or horizontal):
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld,
+ expected = expected.layout(heldNaturally = true),
+ )
+ )
+
+ if (expected.whenNaturallyHeld == SIDE_BY_SIDE) {
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld,
+ isSideBySideSupported = false,
+ expected = STANDARD,
+ )
+ )
+ }
+
+ // Holding the device the other way:
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld.flip(),
+ expected = expected.layout(heldNaturally = false),
+ )
+ )
+
+ if (expected.whenUnnaturallyHeld == SIDE_BY_SIDE) {
+ add(
+ TestCase(
+ device = device,
+ held = device.naturallyHeld.flip(),
+ isSideBySideSupported = false,
+ expected = STANDARD,
+ )
+ )
+ }
+ }
+ }
+ }
+
+ @Parameterized.Parameter @JvmField var testCase: TestCase? = null
+
+ @Test
+ fun calculateLayout() {
+ testCase?.let { nonNullTestCase ->
+ with(nonNullTestCase) {
+ assertThat(
+ calculateLayoutInternal(
+ width = device.width(whenHeld = held),
+ height = device.height(whenHeld = held),
+ isSideBySideSupported = isSideBySideSupported,
+ )
+ )
+ .isEqualTo(expected)
+ }
+ }
+ }
+
+ data class TestCase(
+ val device: Device,
+ val held: Held,
+ val expected: BouncerSceneLayout,
+ val isSideBySideSupported: Boolean = true,
+ ) {
+ override fun toString(): String {
+ return buildString {
+ append(device.name)
+ append(" width: ${device.width(held).name.lowercase(Locale.US)}")
+ append(" height: ${device.height(held).name.lowercase(Locale.US)}")
+ append(" when held $held")
+ if (!isSideBySideSupported) {
+ append(" (side-by-side not supported)")
+ }
+ }
+ }
+ }
+
+ data class Expected(
+ val whenNaturallyHeld: BouncerSceneLayout,
+ val whenUnnaturallyHeld: BouncerSceneLayout,
+ ) {
+ fun layout(heldNaturally: Boolean): BouncerSceneLayout {
+ return if (heldNaturally) {
+ whenNaturallyHeld
+ } else {
+ whenUnnaturallyHeld
+ }
+ }
+ }
+
+ sealed class Device(
+ val name: String,
+ private val width: SizeClass,
+ private val height: SizeClass,
+ val naturallyHeld: Held,
+ private val widthWhenUnnaturallyHeld: SizeClass = height,
+ private val heightWhenUnnaturallyHeld: SizeClass = width,
+ ) {
+ fun width(whenHeld: Held): SizeClass {
+ return if (isHeldNaturally(whenHeld)) {
+ width
+ } else {
+ widthWhenUnnaturallyHeld
+ }
+ }
+
+ fun height(whenHeld: Held): SizeClass {
+ return if (isHeldNaturally(whenHeld)) {
+ height
+ } else {
+ heightWhenUnnaturallyHeld
+ }
+ }
+
+ private fun isHeldNaturally(whenHeld: Held): Boolean {
+ return whenHeld == naturallyHeld
+ }
+ }
+
+ sealed class Held {
+ abstract fun flip(): Held
+ }
+ data object Vertically : Held() {
+ override fun flip(): Held {
+ return Horizontally
+ }
+ }
+ data object Horizontally : Held() {
+ override fun flip(): Held {
+ return Vertically
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
new file mode 100644
index 0000000..db9e548
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
@@ -0,0 +1,86 @@
+package com.android.systemui.qs
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.KeyEvent
+import android.view.View
+import android.widget.Scroller
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PagedTileLayoutTest : SysuiTestCase() {
+
+ @Mock private lateinit var pageIndicator: PageIndicator
+ @Captor private lateinit var captor: ArgumentCaptor<View.OnKeyListener>
+
+ private lateinit var pageTileLayout: TestPagedTileLayout
+ private lateinit var scroller: Scroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ pageTileLayout = TestPagedTileLayout(mContext)
+ pageTileLayout.setPageIndicator(pageIndicator)
+ verify(pageIndicator).setOnKeyListener(captor.capture())
+ setViewWidth(pageTileLayout, width = PAGE_WIDTH)
+ scroller = pageTileLayout.mScroller
+ }
+
+ private fun setViewWidth(view: View, width: Int) {
+ view.left = 0
+ view.right = width
+ }
+
+ @Test
+ fun scrollsRight_afterRightArrowPressed_whenFocusOnPagerIndicator() {
+ pageTileLayout.currentPageIndex = 0
+
+ sendUpEvent(KeyEvent.KEYCODE_DPAD_RIGHT)
+
+ assertThat(scroller.isFinished).isFalse() // aka we're scrolling
+ assertThat(scroller.finalX).isEqualTo(scroller.currX + PAGE_WIDTH)
+ }
+
+ @Test
+ fun scrollsLeft_afterLeftArrowPressed_whenFocusOnPagerIndicator() {
+ pageTileLayout.currentPageIndex = 1 // we won't scroll left if we're on the first page
+
+ sendUpEvent(KeyEvent.KEYCODE_DPAD_LEFT)
+
+ assertThat(scroller.isFinished).isFalse() // aka we're scrolling
+ assertThat(scroller.finalX).isEqualTo(scroller.currX - PAGE_WIDTH)
+ }
+
+ private fun sendUpEvent(keyCode: Int) {
+ val event = KeyEvent(KeyEvent.ACTION_UP, keyCode)
+ captor.value.onKey(pageIndicator, keyCode, event)
+ }
+
+ /**
+ * Custom PagedTileLayout to easy mock "currentItem" i.e. currently visible page. Setting this
+ * up otherwise would require setting adapter etc
+ */
+ class TestPagedTileLayout(context: Context) : PagedTileLayout(context, null) {
+
+ var currentPageIndex: Int = 0
+
+ override fun getCurrentItem(): Int {
+ return currentPageIndex
+ }
+ }
+
+ companion object {
+ const val PAGE_WIDTH = 200
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index 428574b..fa5fad0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
@@ -57,6 +58,7 @@
@Mock private lateinit var groupExpansionManagerImpl: GroupExpansionManagerImpl
@Mock private lateinit var notificationIconAreaController: NotificationIconAreaController
@Mock private lateinit var renderListInteractor: RenderNotificationListInteractor
+ @Mock private lateinit var activeNotificationsInteractor: ActiveNotificationsInteractor
@Mock private lateinit var stackController: NotifStackController
@Mock private lateinit var section: NotifSection
@@ -75,6 +77,7 @@
groupExpansionManagerImpl,
notificationIconAreaController,
renderListInteractor,
+ activeNotificationsInteractor,
)
coordinator.attach(pipeline)
afterRenderListListener = withArgCaptor {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index b24cafd..4ab3cd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -72,4 +72,58 @@
assertThat(areAnyNotificationsPresent).isFalse()
assertThat(underTest.areAnyNotificationsPresentValue).isFalse()
}
+
+ @Test
+ fun testHasClearableNotifications_whenHasClearableAlertingNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isTrue()
+ }
+
+ @Test
+ fun testHasClearableNotifications_whenHasClearableSilentNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isTrue()
+ }
+
+ @Test
+ fun testHasClearableNotifications_whenHasNoClearableNotifs() =
+ testComponent.runTest {
+ val hasClearable by collectLastValue(underTest.hasClearableNotifications)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(hasClearable).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index a64ac67..22c5bae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -114,9 +114,46 @@
}
@Test
+ public void testSetClearAllButtonText_resourceOnlyFetchedOnce() {
+ int resId = R.string.clear_all_notifications_text;
+ mView.setClearAllButtonText(resId);
+ verify(mSpyContext).getString(eq(resId));
+
+ clearInvocations(mSpyContext);
+
+ assertThat(((TextView) mView.findViewById(R.id.dismiss_text))
+ .getText().toString()).contains("Clear all");
+
+ // Set it a few more times, it shouldn't lead to the resource being fetched again
+ mView.setClearAllButtonText(resId);
+ mView.setClearAllButtonText(resId);
+
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
+ public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() {
+ int resId = R.string.accessibility_clear_all;
+ mView.setClearAllButtonDescription(resId);
+ verify(mSpyContext).getString(eq(resId));
+
+ clearInvocations(mSpyContext);
+
+ assertThat(((TextView) mView.findViewById(R.id.dismiss_text))
+ .getContentDescription().toString()).contains("Clear all notifications");
+
+ // Set it a few more times, it shouldn't lead to the resource being fetched again
+ mView.setClearAllButtonDescription(resId);
+ mView.setClearAllButtonDescription(resId);
+
+ verify(mSpyContext, never()).getString(anyInt());
+ }
+
+ @Test
public void testSetMessageString_resourceOnlyFetchedOnce() {
- mView.setMessageString(R.string.unlock_to_see_notif_text);
- verify(mSpyContext).getString(eq(R.string.unlock_to_see_notif_text));
+ int resId = R.string.unlock_to_see_notif_text;
+ mView.setMessageString(resId);
+ verify(mSpyContext).getString(eq(resId));
clearInvocations(mSpyContext);
@@ -124,22 +161,23 @@
.getText().toString()).contains("Unlock");
// Set it a few more times, it shouldn't lead to the resource being fetched again
- mView.setMessageString(R.string.unlock_to_see_notif_text);
- mView.setMessageString(R.string.unlock_to_see_notif_text);
+ mView.setMessageString(resId);
+ mView.setMessageString(resId);
verify(mSpyContext, never()).getString(anyInt());
}
@Test
public void testSetMessageIcon_resourceOnlyFetchedOnce() {
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
- verify(mSpyContext).getDrawable(eq(R.drawable.ic_friction_lock_closed));
+ int resId = R.drawable.ic_friction_lock_closed;
+ mView.setMessageIcon(resId);
+ verify(mSpyContext).getDrawable(eq(resId));
clearInvocations(mSpyContext);
// Set it a few more times, it shouldn't lead to the resource being fetched again
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
- mView.setMessageIcon(R.drawable.ic_friction_lock_closed);
+ mView.setMessageIcon(resId);
+ mView.setMessageIcon(resId);
verify(mSpyContext, never()).getDrawable(anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 57a7c3c..94dcf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -18,37 +18,222 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Optional
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class FooterViewModelTest : SysuiTestCase() {
- private val repository = ActiveNotificationListRepository()
- private val interactor = SeenNotificationsInteractor(repository)
- private val underTest = FooterViewModel(interactor)
+ private lateinit var footerViewModel: FooterViewModel
- @Test
- fun testMessageVisible_whenFilteredNotifications() = runTest {
- val message by collectLastValue(underTest.message)
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ActivatableNotificationViewModelModule::class,
+ FooterViewModelModule::class,
+ HeadlessSystemUserModeModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<Optional<FooterViewModel>> {
+ val activeNotificationListRepository: ActiveNotificationListRepository
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val shadeRepository: FakeShadeRepository
+ val powerRepository: FakePowerRepository
- repository.hasFilteredOutSeenNotifications.value = true
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
- assertThat(message?.visible).isTrue()
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerFooterViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ )
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
+
+ // The underTest in the component is Optional, because that matches the provider we
+ // currently have for the footer view model.
+ footerViewModel = testComponent.underTest.get()
}
@Test
- fun testMessageVisible_whenNoFilteredNotifications() = runTest {
- val message by collectLastValue(underTest.message)
+ fun testMessageVisible_whenFilteredNotifications() =
+ testComponent.runTest {
+ val message by collectLastValue(footerViewModel.message)
- repository.hasFilteredOutSeenNotifications.value = false
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
- assertThat(message?.visible).isFalse()
- }
+ assertThat(message?.visible).isTrue()
+ }
+
+ @Test
+ fun testMessageVisible_whenNoFilteredNotifications() =
+ testComponent.runTest {
+ val message by collectLastValue(footerViewModel.message)
+
+ activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+
+ assertThat(message?.visible).isFalse()
+ }
+
+ @Test
+ fun testClearAllButtonVisible_whenHasClearableNotifs() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ assertThat(button?.isVisible?.value).isTrue()
+ }
+
+ @Test
+ fun testClearAllButtonVisible_whenHasNoClearableNotifs() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = false,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = false,
+ )
+ runCurrent()
+
+ assertThat(button?.isVisible?.value).isFalse()
+ }
+
+ @Test
+ fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+ runCurrent()
+
+ // WHEN shade is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setLegacyShadeExpansion(1f)
+ // AND QS not expanded
+ shadeRepository.setQsExpansion(0f)
+ // AND device is awake
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // AND there are clearable notifications
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ // THEN button visibility should animate
+ assertThat(button?.isVisible?.isAnimating).isTrue()
+ }
+
+ @Test
+ fun testClearAllButtonAnimating_whenShadeNotExpanded() =
+ testComponent.runTest {
+ val button by collectLastValue(footerViewModel.clearAllButton)
+ runCurrent()
+
+ // WHEN shade is collapsed
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setLegacyShadeExpansion(0f)
+ // AND QS not expanded
+ shadeRepository.setQsExpansion(0f)
+ // AND device is awake
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // AND there are clearable notifications
+ activeNotificationListRepository.notifStats.value =
+ NotifStats(
+ numActiveNotifs = 2,
+ hasNonClearableAlertingNotifs = false,
+ hasClearableAlertingNotifs = true,
+ hasNonClearableSilentNotifs = false,
+ hasClearableSilentNotifs = true,
+ )
+ runCurrent()
+
+ // THEN button visibility should not animate
+ assertThat(button?.isVisible?.isAnimating).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 3e331a6..0a9bac9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -999,11 +999,25 @@
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
}
+ @Test
+ public void shouldNotBubbleUp_suspended() {
+ assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createSuspendedBubble()))
+ .isFalse();
+ }
+
+ private NotificationEntry createSuspendedBubble() {
+ return createBubble(null, null, true);
+ }
+
private NotificationEntry createBubble() {
- return createBubble(null, null);
+ return createBubble(null, null, false);
}
private NotificationEntry createBubble(String groupKey, Integer groupAlert) {
+ return createBubble(groupKey, groupAlert, false);
+ }
+
+ private NotificationEntry createBubble(String groupKey, Integer groupAlert, Boolean suspended) {
Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0,
new Intent().setPackage(mContext.getPackageName()),
@@ -1031,6 +1045,7 @@
.setNotification(n)
.setImportance(IMPORTANCE_HIGH)
.setCanBubble(true)
+ .setSuspended(suspended)
.build();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index a3b7e8c..7babff5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -601,6 +601,13 @@
}
@Test
+ fun testShouldNotBubble_bubbleAppSuspended() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { packageSuspended = true })
+ assertNoEventsLogged()
+ }
+
+ @Test
fun testShouldNotFsi_noFullScreenIntent() {
forEachFsiState {
assertShouldNotFsi(buildFsiEntry { hasFsi = false })
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9d8f979..f3b2ef3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -552,7 +552,8 @@
mAsync = true;
} else if (opt.equals("--splashscreen-show-icon")) {
mShowSplashScreen = true;
- } else if (opt.equals("--dismiss-keyguard-if-insecure")) {
+ } else if (opt.equals("--dismiss-keyguard-if-insecure")
+ || opt.equals("--dismiss-keyguard")) {
mDismissKeyguardIfInsecure = true;
} else {
return false;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a158b18..c24d6a0 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -527,12 +527,27 @@
@Override
public boolean canHandleVolumeKey() {
if (isPlaybackTypeLocal()) {
+ if (DEBUG) {
+ Log.d(TAG, "Local MediaSessionRecord can handle volume key");
+ }
return true;
}
if (mVolumeControlType == VOLUME_CONTROL_FIXED) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Local MediaSessionRecord with FIXED volume control can't handle volume"
+ + " key");
+ }
return false;
}
if (mVolumeAdjustmentForRemoteGroupSessions) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Volume adjustment for remote group sessions allowed so MediaSessionRecord"
+ + " can handle volume key");
+ }
return true;
}
// See b/228021646 for details.
@@ -540,7 +555,18 @@
List<RoutingSessionInfo> sessions = mRouter2Manager.getRoutingSessions(mPackageName);
boolean foundNonSystemSession = false;
boolean remoteSessionAllowVolumeAdjustment = true;
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Found "
+ + sessions.size()
+ + " routing sessions for package name "
+ + mPackageName);
+ }
for (RoutingSessionInfo session : sessions) {
+ if (DEBUG) {
+ Log.d(TAG, "Found routingSessionInfo: " + session);
+ }
if (!session.isSystemSession()) {
foundNonSystemSession = true;
if (session.getVolumeHandling() == PLAYBACK_VOLUME_FIXED) {
@@ -549,9 +575,15 @@
}
}
if (!foundNonSystemSession) {
- Log.d(TAG, "Package " + mPackageName
- + " has a remote media session but no associated routing session");
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Package "
+ + mPackageName
+ + " has a remote media session but no associated routing session");
+ }
}
+
return foundNonSystemSession && remoteSessionAllowVolumeAdjustment;
}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index ee3b746..dd39fb0 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -32,8 +32,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.WorkDuration;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
@@ -197,9 +195,6 @@
private static native void nativeSetMode(long halPtr, int mode, boolean enabled);
- private static native void nativeReportActualWorkDuration(long halPtr,
- WorkDuration[] workDurations);
-
/** Wrapper for HintManager.nativeInit */
public void halInit() {
nativeInit();
@@ -257,10 +252,6 @@
nativeSetMode(halPtr, mode, enabled);
}
- /** Wrapper for HintManager.nativeReportActualWorkDuration */
- public void halReportActualWorkDuration(long halPtr, WorkDuration[] workDurations) {
- nativeReportActualWorkDuration(halPtr, workDurations);
- }
}
@VisibleForTesting
@@ -633,52 +624,6 @@
}
}
- @Override
- public void reportActualWorkDuration2(WorkDuration[] workDurations) {
- synchronized (this) {
- if (mHalSessionPtr == 0 || !mUpdateAllowed) {
- return;
- }
- Preconditions.checkArgument(workDurations.length != 0, "the count"
- + " of work durations shouldn't be 0.");
- for (WorkDuration workDuration : workDurations) {
- validateWorkDuration(workDuration);
- }
- mNativeWrapper.halReportActualWorkDuration(mHalSessionPtr, workDurations);
- }
- }
-
- void validateWorkDuration(WorkDuration workDuration) {
- if (DEBUG) {
- Slogf.d(TAG, "WorkDuration(" + workDuration.getTimestampNanos() + ", "
- + workDuration.getWorkPeriodStartTimestampNanos() + ", "
- + workDuration.getActualTotalDurationNanos() + ", "
- + workDuration.getActualCpuDurationNanos() + ", "
- + workDuration.getActualGpuDurationNanos() + ")");
- }
- if (workDuration.getWorkPeriodStartTimestampNanos() <= 0) {
- throw new IllegalArgumentException(
- TextUtils.formatSimple(
- "Work period start timestamp (%d) should be greater than 0",
- workDuration.getWorkPeriodStartTimestampNanos()));
- }
- if (workDuration.getActualTotalDurationNanos() <= 0) {
- throw new IllegalArgumentException(
- TextUtils.formatSimple("Actual total duration (%d) should be greater than 0",
- workDuration.getActualTotalDurationNanos()));
- }
- if (workDuration.getActualCpuDurationNanos() <= 0) {
- throw new IllegalArgumentException(
- TextUtils.formatSimple("Actual CPU duration (%d) should be greater than 0",
- workDuration.getActualCpuDurationNanos()));
- }
- if (workDuration.getActualGpuDurationNanos() < 0) {
- throw new IllegalArgumentException(
- TextUtils.formatSimple("Actual GPU duration (%d) should be non negative",
- workDuration.getActualGpuDurationNanos()));
- }
- }
-
private void onProcStateChanged(boolean updateAllowed) {
updateHintAllowed(updateAllowed);
}
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
index 5c37680..17d327e 100644
--- a/services/core/java/com/android/server/timedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -7,10 +7,7 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
- }
- ],
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
+ },
{
"name": "FrameworksTimeServicesTests"
}
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 63dd7b4..358618a 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -7,15 +7,15 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "FrameworksTimeServicesTests"
}
],
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
"name": "CtsLocationTimeZoneManagerHostTest"
- },
- {
- "name": "FrameworksTimeServicesTests"
}
]
}
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index ccd9bd0..7edf445 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -20,7 +20,6 @@
#include <aidl/android/hardware/power/IPower.h>
#include <android-base/stringprintf.h>
-#include <inttypes.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <powermanager/PowerHalController.h>
@@ -39,15 +38,6 @@
namespace android {
-static struct {
- jclass clazz{};
- jfieldID workPeriodStartTimestampNanos{};
- jfieldID actualTotalDurationNanos{};
- jfieldID actualCpuDurationNanos{};
- jfieldID actualGpuDurationNanos{};
- jfieldID timestampNanos{};
-} gWorkDurationInfo;
-
static power::PowerHalController gPowerHalController;
static std::unordered_map<jlong, std::shared_ptr<IPowerHintSession>> gSessionMap;
static std::mutex gSessionMapLock;
@@ -190,26 +180,6 @@
setMode(session_ptr, static_cast<SessionMode>(mode), enabled);
}
-static void nativeReportActualWorkDuration2(JNIEnv* env, jclass /* clazz */, jlong session_ptr,
- jobjectArray jWorkDurations) {
- int size = env->GetArrayLength(jWorkDurations);
- std::vector<WorkDuration> workDurations(size);
- for (int i = 0; i < size; i++) {
- jobject workDuration = env->GetObjectArrayElement(jWorkDurations, i);
- workDurations[i].workPeriodStartTimestampNanos =
- env->GetLongField(workDuration, gWorkDurationInfo.workPeriodStartTimestampNanos);
- workDurations[i].durationNanos =
- env->GetLongField(workDuration, gWorkDurationInfo.actualTotalDurationNanos);
- workDurations[i].cpuDurationNanos =
- env->GetLongField(workDuration, gWorkDurationInfo.actualCpuDurationNanos);
- workDurations[i].gpuDurationNanos =
- env->GetLongField(workDuration, gWorkDurationInfo.actualGpuDurationNanos);
- workDurations[i].timeStampNanos =
- env->GetLongField(workDuration, gWorkDurationInfo.timestampNanos);
- }
- reportActualWorkDuration(session_ptr, workDurations);
-}
-
// ----------------------------------------------------------------------------
static const JNINativeMethod sHintManagerServiceMethods[] = {
/* name, signature, funcPtr */
@@ -224,23 +194,9 @@
{"nativeSendHint", "(JI)V", (void*)nativeSendHint},
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
{"nativeSetMode", "(JIZ)V", (void*)nativeSetMode},
- {"nativeReportActualWorkDuration", "(J[Landroid/os/WorkDuration;)V",
- (void*)nativeReportActualWorkDuration2},
};
int register_android_server_HintManagerService(JNIEnv* env) {
- gWorkDurationInfo.clazz = env->FindClass("android/os/WorkDuration");
- gWorkDurationInfo.workPeriodStartTimestampNanos =
- env->GetFieldID(gWorkDurationInfo.clazz, "mWorkPeriodStartTimestampNanos", "J");
- gWorkDurationInfo.actualTotalDurationNanos =
- env->GetFieldID(gWorkDurationInfo.clazz, "mActualTotalDurationNanos", "J");
- gWorkDurationInfo.actualCpuDurationNanos =
- env->GetFieldID(gWorkDurationInfo.clazz, "mActualCpuDurationNanos", "J");
- gWorkDurationInfo.actualGpuDurationNanos =
- env->GetFieldID(gWorkDurationInfo.clazz, "mActualGpuDurationNanos", "J");
- gWorkDurationInfo.timestampNanos =
- env->GetFieldID(gWorkDurationInfo.clazz, "mTimestampNanos", "J");
-
return jniRegisterNativeMethods(env,
"com/android/server/power/hint/"
"HintManagerService$NativeWrapper",
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 3748527..d09aa89 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -44,7 +44,6 @@
import android.os.IHintSession;
import android.os.PerformanceHintManager;
import android.os.Process;
-import android.os.WorkDuration;
import android.util.Log;
import com.android.server.FgThread;
@@ -90,11 +89,6 @@
private static final long[] DURATIONS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
- private static final WorkDuration[] WORK_DURATIONS_THREE = new WorkDuration[] {
- new WorkDuration(1L, 11L, 8L, 4L, 1L),
- new WorkDuration(2L, 13L, 8L, 6L, 2L),
- new WorkDuration(3L, 333333333L, 8L, 333333333L, 3L),
- };
@Mock private Context mContext;
@Mock private HintManagerService.NativeWrapper mNativeWrapperMock;
@@ -599,55 +593,4 @@
}
a.close();
}
-
- @Test
- public void testReportActualWorkDuration2() throws Exception {
- HintManagerService service = createService();
- IBinder token = new Binder();
-
- AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
-
- a.updateTargetWorkDuration(100L);
- a.reportActualWorkDuration2(WORK_DURATIONS_THREE);
- verify(mNativeWrapperMock, times(1)).halReportActualWorkDuration(anyLong(),
- eq(WORK_DURATIONS_THREE));
-
- assertThrows(IllegalArgumentException.class, () -> {
- a.reportActualWorkDuration2(new WorkDuration[] {});
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(0L, 11L, 8L, 4L, 1L)});
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(1L, 0L, 8L, 4L, 1L)});
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- a.reportActualWorkDuration2(new WorkDuration[] {new WorkDuration(1L, 11L, 0L, 4L, 1L)});
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- a.reportActualWorkDuration2(
- new WorkDuration[] {new WorkDuration(1L, 11L, 8L, -1L, 1L)});
- });
-
- reset(mNativeWrapperMock);
- // Set session to background, then the duration would not be updated.
- service.mUidObserver.onUidStateChanged(
- a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
-
- // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
- final CountDownLatch latch = new CountDownLatch(1);
- FgThread.getHandler().post(() -> {
- latch.countDown();
- });
- latch.await();
-
- assertFalse(service.mUidObserver.isUidForeground(a.mUid));
- a.reportActualWorkDuration2(WORK_DURATIONS_THREE);
- verify(mNativeWrapperMock, never()).halReportActualWorkDuration(anyLong(), any(), any());
- }
}