Merge "Update javadoc of isApkVeritySupported" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 13bc512..7913ad1 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -12,37 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+aconfig_srcjars = [
+ ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+ ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.nfc.flags-aconfig-java{.generated_srcjars}",
+ ":android.os.flags-aconfig-java{.generated_srcjars}",
+ ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
+ ":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.view.flags-aconfig-java{.generated_srcjars}",
+ ":camera_platform_flags_core_java_lib{.generated_srcjars}",
+ ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+ ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
+ ":com.android.text.flags-aconfig-java{.generated_srcjars}",
+ ":telecom_flags_core_java_lib{.generated_srcjars}",
+ ":telephony_flags_core_java_lib{.generated_srcjars}",
+ ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
+ ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
+ ":android.widget.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
+ ":sdk_sandbox_flags_lib{.generated_srcjars}",
+ ":android.permission.flags-aconfig-java{.generated_srcjars}",
+ ":hwui_flags_java_lib{.generated_srcjars}",
+ ":display_flags_lib{.generated_srcjars}",
+]
+
filegroup {
name: "framework-minus-apex-aconfig-srcjars",
- srcs: [
- ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
- ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
- ":android.os.flags-aconfig-java{.generated_srcjars}",
- ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
- ":android.security.flags-aconfig-java{.generated_srcjars}",
- ":android.view.flags-aconfig-java{.generated_srcjars}",
- ":camera_platform_flags_core_java_lib{.generated_srcjars}",
- ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
- ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
- ":com.android.text.flags-aconfig-java{.generated_srcjars}",
- ":telecom_flags_core_java_lib{.generated_srcjars}",
- ":telephony_flags_core_java_lib{.generated_srcjars}",
- ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
- ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
- ":android.widget.flags-aconfig-java{.generated_srcjars}",
- ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
- ":sdk_sandbox_flags_lib{.generated_srcjars}",
- ],
+ srcs: aconfig_srcjars,
}
// Aconfig declarations and libraries for the core framework
java_defaults {
name: "framework-minus-apex-aconfig-libraries",
-
// Add java_aconfig_libraries to here to add them to the core framework
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
+ srcs: aconfig_srcjars,
// Add aconfig-annotations-lib as a dependency for the optimization
libs: ["aconfig-annotations-lib"],
}
@@ -120,6 +124,19 @@
aconfig_declarations: "com.android.text.flags-aconfig",
}
+// NFC
+aconfig_declarations {
+ name: "android.nfc.flags-aconfig",
+ package: "android.nfc",
+ srcs: ["core/java/android/nfc/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.nfc.flags-aconfig-java",
+ aconfig_declarations: "android.nfc.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Security
aconfig_declarations {
name: "android.security.flags-aconfig",
@@ -137,7 +154,6 @@
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
- test: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -257,3 +273,43 @@
aconfig_declarations: "com.android.media.flags.bettertogether-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Permissions
+aconfig_declarations {
+ name: "android.permission.flags-aconfig",
+ package: "android.permission.flags",
+ srcs: ["core/java/android/permission/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.permission.flags-aconfig-java",
+ aconfig_declarations: "android.permission.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Biometrics
+aconfig_declarations {
+ name: "android.hardware.biometrics.flags-aconfig",
+ package: "android.hardware.biometrics",
+ srcs: ["core/java/android/hardware/biometrics/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.biometrics.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.biometrics.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Graphics
+java_aconfig_library {
+ name: "hwui_flags_java_lib",
+ aconfig_declarations: "hwui_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Display
+java_aconfig_library {
+ name: "display_flags_lib",
+ aconfig_declarations: "display_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 57a5a3c..f1a3af2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -621,6 +621,7 @@
"--api-lint-ignore-prefix org. " +
"--error NoSettingsProvider " +
"--error UnhiddenSystemApi " +
+ "--error UnflaggedApi " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--hide BroadcastBehavior " +
"--hide CallbackInterface " +
diff --git a/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java b/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java
index 7927aa9..885000f 100644
--- a/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java
+++ b/apct-tests/perftests/core/src/android/accessibility/AccessibilityPerfTest.java
@@ -22,7 +22,6 @@
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.perftests.utils.PerfTestActivity;
-import android.platform.test.annotations.LargeTest;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -32,6 +31,7 @@
import androidx.benchmark.BenchmarkState;
import androidx.benchmark.junit4.BenchmarkRule;
+import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
diff --git a/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java
index 898111f..436ee16 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewCursorAnchorInfoPerfTest.java
@@ -22,13 +22,13 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
-import android.platform.test.annotations.LargeTest;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.CursorAnchorInfo;
import android.widget.TextView;
+import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/view/ViewConfigurationPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewConfigurationPerfTest.java
new file mode 100644
index 0000000..7a7250b
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/ViewConfigurationPerfTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@SmallTest
+public class ViewConfigurationPerfTest {
+ @Rule
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
+ private final Context mContext = getInstrumentation().getTargetContext();
+
+ @Test
+ public void testGet_newViewConfiguration() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ // Reset cache so that `ViewConfiguration#get` creates a new instance.
+ ViewConfiguration.resetCacheForTesting();
+ state.resumeTiming();
+
+ ViewConfiguration.get(mContext);
+ }
+ }
+
+ @Test
+ public void testGet_cachedViewConfiguration() {
+ final BenchmarkState state = mBenchmarkRule.getState();
+ // Do `get` once to make sure there's something cached.
+ ViewConfiguration.get(mContext);
+
+ while (state.keepRunning()) {
+ ViewConfiguration.get(mContext);
+ }
+ }
+}
diff --git a/apct-tests/perftests/surfaceflinger/Android.bp b/apct-tests/perftests/surfaceflinger/Android.bp
index 0c28bce..21d0d44 100644
--- a/apct-tests/perftests/surfaceflinger/Android.bp
+++ b/apct-tests/perftests/surfaceflinger/Android.bp
@@ -32,6 +32,7 @@
"apct-perftests-utils",
"collector-device-lib",
"platform-test-annotations",
+ "cts-wm-util",
],
test_suites: ["device-tests"],
data: [":perfetto_artifacts"],
diff --git a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
index 26f2586..aa55b01 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
@@ -16,9 +16,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.perftests.surfaceflinger">
- <!-- permission needed to write perfetto trace and read/write simpleperf report -->
+ <!-- permission needed to read/write simpleperf report -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- permission needed to disable tracing -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST" />
<application android:label="SurfaceFlingerPerfTests">
<uses-library android:name="android.test.runner" />
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index 58cf58b..6dcd86e 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
@@ -44,17 +44,12 @@
<option name="hidden-api-checks" value="false"/>
<!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
- <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener,android.device.collectors.SimpleperfListener" />
-
- <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
- <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.SimpleperfListener" />
<option name="instrumentation-arg" key="profiling-iterations" value="525" />
- <!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
- <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
<!-- SimpleperfListener related arguments -->
+ <option name="instrumentation-arg" key="record" value="false"/>
<option name="instrumentation-arg" key="report" value="true" />
<option name="instrumentation-arg" key="arguments" value="-g" />
<option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
@@ -70,13 +65,11 @@
<option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
<option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
<option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
-
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/data/local/tmp/SurfaceFlingerPerfTests" />
<!-- Needed for pulling the collected trace config on to the host -->
- <option name="pull-pattern-keys" value="perfetto_file_path" />
<option name="pull-pattern-keys" value="simpleperf_file_path" />
</metrics_collector>
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java
index 8a447bb..8d6dd12 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java
@@ -16,6 +16,10 @@
package android.surfaceflinger;
+import static android.view.SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
+import static android.view.SurfaceControl.BUFFER_TRANSFORM_ROTATE_270;
+import static android.view.SurfaceControl.BUFFER_TRANSFORM_ROTATE_90;
+
import android.annotation.ColorInt;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
@@ -33,9 +37,11 @@
* @hide
*/
public class BufferFlinger {
+ private final int mTransformHint;
ArrayBlockingQueue<GraphicBuffer> mBufferQ;
- public BufferFlinger(int numOfBuffers, @ColorInt int color) {
+ public BufferFlinger(int numOfBuffers, @ColorInt int color, int bufferTransformHint) {
+ mTransformHint = bufferTransformHint;
mBufferQ = new ArrayBlockingQueue<>(numOfBuffers);
while (numOfBuffers > 0) {
@@ -56,12 +62,18 @@
public void addBuffer(SurfaceControl.Transaction t, SurfaceControl surfaceControl) {
try {
final GraphicBuffer buffer = mBufferQ.take();
- t.setBuffer(surfaceControl,
+ int transform = BUFFER_TRANSFORM_IDENTITY;
+ if (mTransformHint == BUFFER_TRANSFORM_ROTATE_90) {
+ transform = BUFFER_TRANSFORM_ROTATE_270;
+ } else if (mTransformHint == BUFFER_TRANSFORM_ROTATE_270) {
+ transform = BUFFER_TRANSFORM_ROTATE_90;
+ }
+ t.setBufferTransform(surfaceControl, transform);
+ t.setBuffer(
+ surfaceControl,
HardwareBuffer.createFromGraphicBuffer(buffer),
null,
- (SyncFence fence) -> {
- releaseCallback(fence, buffer);
- });
+ (SyncFence fence) -> releaseCallback(fence, buffer));
} catch (InterruptedException ignore) {
}
}
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index dca818e..fd9d991 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -16,6 +16,9 @@
package android.surfaceflinger;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop;
+
+import android.app.Instrumentation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -25,12 +28,13 @@
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.helpers.SimpleperfHelper;
+
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -39,6 +43,8 @@
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Random;
@@ -63,12 +69,33 @@
public final RuleChain mAllRules = RuleChain
.outerRule(mActivityRule);
+ private int mTransformHint;
+ private SimpleperfHelper mSimpleperfHelper = new SimpleperfHelper();
+
+ /** Start simpleperf sampling. */
+ public void startSimpleperf(String subcommand, String arguments) {
+ if (!mSimpleperfHelper.startCollecting(subcommand, arguments)) {
+ Log.e(TAG, "Simpleperf did not start successfully.");
+ }
+ }
+
+ /** Stop simpleperf sampling and dump the collected file into the given path. */
+ private void stopSimpleperf(Path path) {
+ if (!mSimpleperfHelper.stopCollecting(path.toString())) {
+ Log.e(TAG, "Failed to collect the simpleperf output.");
+ }
+ }
+
@BeforeClass
public static void suiteSetup() {
final Bundle arguments = InstrumentationRegistry.getArguments();
sProfilingIterations = Integer.parseInt(
arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS));
Log.d(TAG, "suiteSetup: mProfilingIterations = " + sProfilingIterations);
+ // disable transaction tracing
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .executeShellCommand("service call SurfaceFlinger 1041 i32 -1");
}
@Before
@@ -77,17 +104,45 @@
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = 0; i < MAX_BUFFERS; i++) {
SurfaceControl sc = createSurfaceControl();
- BufferFlinger bufferTracker = createBufferTracker(Color.argb(getRandomColorComponent(),
- getRandomColorComponent(), getRandomColorComponent(),
- getRandomColorComponent()));
+ BufferFlinger bufferTracker =
+ createBufferTracker(
+ Color.argb(
+ getRandomColorComponent(),
+ getRandomColorComponent(),
+ getRandomColorComponent(),
+ getRandomColorComponent()),
+ mActivity.getBufferTransformHint());
bufferTracker.addBuffer(t, sc);
t.setPosition(sc, i * 10, i * 10);
}
t.apply(true);
+ mBufferTrackers.get(0).addBuffer(mTransaction, mSurfaceControls.get(0));
+ mTransaction.show(mSurfaceControls.get(0)).apply(true);
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ instrumentation.waitForIdleSync();
+ // Wait for device animation that shows above the activity to leave.
+ try {
+ waitForWindowOnTop(mActivity.getWindow());
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait for window", e);
+ }
+ String args =
+ "-o /data/local/tmp/perf.data -g -e"
+ + " instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking -p "
+ + mSimpleperfHelper.getPID("surfaceflinger")
+ + ","
+ + mSimpleperfHelper.getPID("android.perftests.surfaceflinger");
+ startSimpleperf("record", args);
}
@After
public void teardown() {
+ try {
+ mSimpleperfHelper.stopSimpleperf();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to stop simpleperf", e);
+ }
mSurfaceControls.forEach(SurfaceControl::release);
mBufferTrackers.forEach(BufferFlinger::freeBuffers);
}
@@ -97,8 +152,9 @@
}
private final ArrayList<BufferFlinger> mBufferTrackers = new ArrayList<>();
- private BufferFlinger createBufferTracker(int color) {
- BufferFlinger bufferTracker = new BufferFlinger(BUFFER_COUNT, color);
+
+ private BufferFlinger createBufferTracker(int color, int bufferTransformHint) {
+ BufferFlinger bufferTracker = new BufferFlinger(BUFFER_COUNT, color, bufferTransformHint);
mBufferTrackers.add(bufferTracker);
return bufferTracker;
}
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
index bb95659..5f1f44d 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
@@ -16,12 +16,15 @@
package android.surfaceflinger;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_DEFAULT;
+
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
@@ -42,6 +45,11 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ View decorView = getWindow().getDecorView();
+ // Hide both the navigation bar and the status bar.
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ decorView.setSystemUiVisibility(uiOptions);
+
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mTestSurfaceView = new TestSurfaceView(this);
setContentView(mTestSurfaceView);
@@ -51,6 +59,10 @@
return mTestSurfaceView.getChildSurfaceControlHelper();
}
+ public int getBufferTransformHint() {
+ return mTestSurfaceView.getRootSurfaceControl().getBufferTransformHint();
+ }
+
public class TestSurfaceView extends SurfaceView {
public TestSurfaceView(Context context) {
super(context);
@@ -79,6 +91,9 @@
// check to see if surface is valid
if (holder.getSurface().isValid()) {
mSurfaceControl = getSurfaceControl();
+ new SurfaceControl.Transaction()
+ .setFrameRate(mSurfaceControl, 1000, FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .apply(true);
}
return new SurfaceControl.Builder()
.setName("ChildSurfaceControl")
diff --git a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
index 9699757..75838c2 100644
--- a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
+++ b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
@@ -49,13 +49,25 @@
String QUICK_DOZE_REQUEST_IDENTIFIER = "quick_doze_request";
/**
+ * Mode manager off body state identifier.
+ *
+ * <p>Unique identifier that can be used as identifier parameter in
+ * registerInternalStateObserver
+ * to listen to changes in quick doze request state from mode manager.
+ *
+ * TODO(b/288276510): convert to int constant
+ */
+ String OFFBODY_STATE_ID = "off_body";
+
+ /**
* StringDef for Mode manager identifiers.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@StringDef({
- QUICK_DOZE_REQUEST_IDENTIFIER
+ QUICK_DOZE_REQUEST_IDENTIFIER,
+ OFFBODY_STATE_ID
})
@Target(ElementType.TYPE_USE)
@interface Identifier {
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index b8596d5..f252a0b 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -377,7 +377,11 @@
@GuardedBy("this")
private boolean mModeManagerRequestedQuickDoze;
@GuardedBy("this")
+ private boolean mIsOffBody;
+ @GuardedBy("this")
private boolean mForceModeManagerQuickDozeRequest;
+ @GuardedBy("this")
+ private boolean mForceModeManagerOffBodyState;
/** Time in the elapsed realtime timebase when this listener last received a motion event. */
@GuardedBy("this")
@@ -437,6 +441,7 @@
private static final int ACTIVE_REASON_ALARM = 7;
private static final int ACTIVE_REASON_EMERGENCY_CALL = 8;
private static final int ACTIVE_REASON_MODE_MANAGER = 9;
+ private static final int ACTIVE_REASON_ONBODY = 10;
@VisibleForTesting
static String stateToString(int state) {
@@ -837,7 +842,7 @@
class ModeManagerQuickDozeRequestConsumer implements Consumer<Boolean> {
@Override
public void accept(Boolean enabled) {
- Slog.d(TAG, "Mode manager quick doze request: " + enabled);
+ Slog.i(TAG, "Mode manager quick doze request: " + enabled);
synchronized (DeviceIdleController.this) {
if (!mForceModeManagerQuickDozeRequest
&& mModeManagerRequestedQuickDoze != enabled) {
@@ -848,13 +853,46 @@
}
@GuardedBy("DeviceIdleController.this")
- public void onModeManagerRequestChangedLocked() {
+ private void onModeManagerRequestChangedLocked() {
// Get into quick doze faster when mode manager requests instead of taking
// traditional multi-stage approach.
+ maybeBecomeActiveOnModeManagerEventsLocked();
updateQuickDozeFlagLocked();
- if (!mModeManagerRequestedQuickDoze && !mBatterySaverEnabled) {
- mActiveReason = ACTIVE_REASON_MODE_MANAGER;
- becomeActiveLocked("mode_manager", Process.myUid());
+ }
+ }
+
+ @VisibleForTesting
+ class ModeManagerOffBodyStateConsumer implements Consumer<Boolean> {
+ @Override
+ public void accept(Boolean isOffBody) {
+ Slog.i(TAG, "Offbody event from mode manager: " + isOffBody);
+ synchronized (DeviceIdleController.this) {
+ if (!mForceModeManagerOffBodyState && mIsOffBody != isOffBody) {
+ mIsOffBody = isOffBody;
+ onModeManagerOffBodyChangedLocked();
+ }
+ }
+ }
+
+ @GuardedBy("DeviceIdleController.this")
+ private void onModeManagerOffBodyChangedLocked() {
+ maybeBecomeActiveOnModeManagerEventsLocked();
+ }
+ }
+
+ @GuardedBy("DeviceIdleController.this")
+ private void maybeBecomeActiveOnModeManagerEventsLocked() {
+ synchronized (DeviceIdleController.this) {
+ if (mQuickDozeActivated) {
+ // Quick doze is enabled so don't turn the device active.
+ return;
+ }
+ // Fall through when quick doze is not requested.
+
+ if (!mIsOffBody) {
+ // Quick doze was not requested and device is on body so turn the device active.
+ mActiveReason = ACTIVE_REASON_ONBODY;
+ becomeActiveLocked("on_body", Process.myUid());
}
}
}
@@ -864,6 +902,10 @@
new ModeManagerQuickDozeRequestConsumer();
@VisibleForTesting
+ final ModeManagerOffBodyStateConsumer mModeManagerOffBodyStateConsumer =
+ new ModeManagerOffBodyStateConsumer();
+
+ @VisibleForTesting
final class MotionListener extends TriggerEventListener
implements SensorEventListener {
@@ -2648,6 +2690,12 @@
WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER,
AppSchedulingModuleThread.getExecutor(),
mModeManagerQuickDozeRequestConsumer);
+
+ modeManagerInternal.addActiveStateChangeListener(
+ WearModeManagerInternal.OFFBODY_STATE_ID,
+ AppSchedulingModuleThread.getExecutor(),
+ mModeManagerOffBodyStateConsumer
+ );
}
}
mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
@@ -4463,7 +4511,7 @@
pw.println(
" Resume normal functioning after force-idle or force-inactive or "
+ "force-modemanager-quickdoze.");
- pw.println(" get [light|deep|force|screen|charging|network|offbody|forcebodystate]");
+ pw.println(" get [light|deep|force|screen|charging|network|offbody|forceoffbody]");
pw.println(" Retrieve the current given state.");
pw.println(" disable [light|deep|all]");
pw.println(" Completely disable device idle mode.");
@@ -4500,6 +4548,10 @@
pw.println(" force-modemanager-quickdoze [true|false]");
pw.println(" Simulate mode manager request to enable (true) or disable (false) "
+ "quick doze. Mode manager changes will be ignored until unforce is called.");
+ pw.println(" force-modemanager-offbody [true|false]");
+ pw.println(" Force mode manager offbody state, this can be used to simulate "
+ + "device being off-body (true) or on-body (false). Mode manager changes "
+ + "will be ignored until unforce is called.");
}
class Shell extends ShellCommand {
@@ -4634,6 +4686,9 @@
mForceModeManagerQuickDozeRequest = false;
pw.println("mForceModeManagerQuickDozeRequest: "
+ mForceModeManagerQuickDozeRequest);
+ mForceModeManagerOffBodyState = false;
+ pw.println("mForceModeManagerOffBodyState: "
+ + mForceModeManagerOffBodyState);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -4660,6 +4715,8 @@
case "forcemodemanagerquick":
pw.println(mForceModeManagerQuickDozeRequest);
break;
+ case "offbody": pw.println(mIsOffBody); break;
+ case "forceoffbody": pw.println(mForceModeManagerOffBodyState); break;
default: pw.println("Unknown get option: " + arg); break;
}
} finally {
@@ -4982,6 +5039,31 @@
pw.println("Provide true or false argument after force-modemanager-quickdoze");
return -1;
}
+ } else if ("force-modemanager-offbody".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ String arg = shell.getNextArg();
+
+ if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) {
+ boolean isOffBody = Boolean.parseBoolean(arg);
+
+ synchronized (DeviceIdleController.this) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mForceModeManagerOffBodyState = true;
+ pw.println("mForceModeManagerOffBodyState: "
+ + mForceModeManagerOffBodyState);
+ mIsOffBody = isOffBody;
+ pw.println("mIsOffBody: " + mIsOffBody);
+ mModeManagerOffBodyStateConsumer.onModeManagerOffBodyChangedLocked();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } else {
+ pw.println("Provide true or false argument after force-modemanager-offbody");
+ return -1;
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
@@ -5233,6 +5315,12 @@
if (mAlarmsActive) {
pw.print(" mAlarmsActive="); pw.println(mAlarmsActive);
}
+ if (mConstants.USE_MODE_MANAGER) {
+ pw.print(" mModeManagerRequestedQuickDoze=");
+ pw.println(mModeManagerRequestedQuickDoze);
+ pw.print(" mIsOffBody=");
+ pw.println(mIsOffBody);
+ }
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index e7ea7c2..a143d6f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1762,8 +1762,16 @@
sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount());
}
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uId, null, jobStatus.getBatteryName(),
+ final int sourceUid = uId;
+ FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ jobStatus.isProxyJob()
+ ? new int[]{sourceUid, jobStatus.getUid()} : new int[]{sourceUid},
+ // Given that the source tag is set by the calling app, it should be connected
+ // to the calling app in the attribution for a proxied job.
+ jobStatus.isProxyJob()
+ ? new String[]{null, jobStatus.getSourceTag()}
+ : new String[]{jobStatus.getSourceTag()},
+ jobStatus.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
jobStatus.getLoggingJobId(),
@@ -2191,8 +2199,16 @@
cancelled, reason, internalReasonCode, debugReason);
// If the job was running, the JobServiceContext should log with state FINISHED.
if (!wasRunning) {
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- cancelled.getSourceUid(), null, cancelled.getBatteryName(),
+ final int sourceUid = cancelled.getSourceUid();
+ FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ cancelled.isProxyJob()
+ ? new int[]{sourceUid, cancelled.getUid()} : new int[]{sourceUid},
+ // Given that the source tag is set by the calling app, it should be connected
+ // to the calling app in the attribution for a proxied job.
+ cancelled.isProxyJob()
+ ? new String[]{null, cancelled.getSourceTag()}
+ : new String[]{cancelled.getSourceTag()},
+ cancelled.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED,
internalReasonCode, cancelled.getStandbyBucket(),
cancelled.getLoggingJobId(),
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index b737041..43d2ae9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -470,8 +470,15 @@
return false;
}
mJobPackageTracker.noteActive(job);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- job.getSourceUid(), null, job.getBatteryName(),
+ final int sourceUid = job.getSourceUid();
+ FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
+ // Given that the source tag is set by the calling app, it should be connected
+ // to the calling app in the attribution for a proxied job.
+ job.isProxyJob()
+ ? new String[]{null, job.getSourceTag()}
+ : new String[]{job.getSourceTag()},
+ job.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN,
job.getStandbyBucket(),
@@ -1531,8 +1538,16 @@
}
mJobPackageTracker.noteInactive(completedJob,
loggingInternalStopReason, loggingDebugReason);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- completedJob.getSourceUid(), null, completedJob.getBatteryName(),
+ final int sourceUid = completedJob.getSourceUid();
+ FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ completedJob.isProxyJob()
+ ? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid},
+ // Given that the source tag is set by the calling app, it should be connected
+ // to the calling app in the attribution for a proxied job.
+ completedJob.isProxyJob()
+ ? new String[]{null, completedJob.getSourceTag()}
+ : new String[]{completedJob.getSourceTag()},
+ completedJob.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
loggingInternalStopReason, completedJob.getStandbyBucket(),
completedJob.getLoggingJobId(),
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index d9c4632..8504b1f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsJobSchedulerTestCases",
"options": [
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -14,7 +14,7 @@
"options": [
{"include-filter": "com.android.server.job"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -23,7 +23,7 @@
"options": [
{"include-filter": "com.android.server.job"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index e0c766f..458ff35 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -255,6 +255,9 @@
final String tag;
+ /** Whether this job was scheduled by one app on behalf of another. */
+ final boolean mIsProxyJob;
+
private GrantedUriPermissions uriPerms;
private boolean prepared;
@@ -626,6 +629,9 @@
: bnNamespace + job.getService().flattenToShortString();
this.tag = "*job*/" + this.batteryName + "#" + job.getId();
+ final String componentPackage = job.getService().getPackageName();
+ mIsProxyJob = !this.sourcePackageName.equals(componentPackage);
+
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
@@ -1048,6 +1054,11 @@
return mLoggingJobId;
}
+ /** Returns whether this job was scheduled by one app on behalf of another. */
+ public boolean isProxyJob() {
+ return mIsProxyJob;
+ }
+
public void printUniqueId(PrintWriter pw) {
if (mNamespace != null) {
pw.print(mNamespace);
@@ -1292,6 +1303,12 @@
return mNamespaceHash;
}
+ /**
+ * Returns the tag passed by the calling app to describe the source app work. This is primarily
+ * only valid if {@link #isProxyJob()} returns true, but may be non-null if an app uses
+ * {@link JobScheduler#scheduleAsPackage(JobInfo, String, int, String)} for itself.
+ */
+ @Nullable
public String getSourceTag() {
return sourceTag;
}
@@ -1871,9 +1888,13 @@
mReadyDynamicSatisfied = mDynamicConstraints != 0
&& mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
- FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.write(
FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
- sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
+ isProxyJob() ? new int[]{sourceUid, getUid()} : new int[]{sourceUid},
+ // Given that the source tag is set by the calling app, it should be connected
+ // to the calling app in the attribution for a proxied job.
+ isProxyJob() ? new String[]{null, sourceTag} : new String[]{sourceTag},
+ getBatteryName(), getProtoConstraint(constraint),
state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
: FrameworkStatsLog
.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 3fb6961..8989b10 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -29,14 +29,14 @@
droidstubs {
name: "api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
- args: metalava_framework_docs_args + "--error UnflaggedApi ",
+ args: metalava_framework_docs_args,
check_api: {
current: {
api_file: ":non-updatable-current.txt",
@@ -77,8 +77,7 @@
"client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\)"
-test = " --show-annotation android.annotation.TestApi" +
- " --hide UnflaggedApi" // TODO(b/297362755): TestApi lint doesn't ignore existing APIs.
+test = " --show-annotation android.annotation.TestApi"
module_libs = " --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
@@ -137,6 +136,7 @@
},
api_lint: {
enabled: true,
+ new_since: ":android.api.test.latest",
baseline_file: ":non-updatable-test-lint-baseline.txt",
},
},
diff --git a/api/api.go b/api/api.go
index 6095a9a..83804c6 100644
--- a/api/api.go
+++ b/api/api.go
@@ -433,7 +433,7 @@
}
// combined_apis bp2build converter
-func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *CombinedApis) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
basePrefix := "non-updatable"
scopeToSuffix := map[string]string{
"public": "-current.txt",
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 9225fe8..71f366a 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
# Convert each removed.txt to the "dex format" equivalent, and print all output.
for f in "$@"; do
- "$metalava_path" --no-banner "$f" --dex-api "${tmp_dir}/tmp"
+ "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
cat "${tmp_dir}/tmp"
done
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 9dedf70..0d3dc49 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -1246,6 +1246,40 @@
return "TRANSPORT_IS_NULL";
case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS:
return "AGENT_LOGGING_RESULTS";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE:
+ return "START_SYSTEM_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL:
+ return "START_RESTORE_AT_INSTALL";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE:
+ return "TRANSPORT_ERROR_DURING_START_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME:
+ return "CANNOT_GET_NEXT_PKG_NAME";
+ case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE:
+ return "UNKNOWN_RESTORE_TYPE";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE:
+ return "KV_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE:
+ return "FULL_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET:
+ return "NO_NEXT_RESTORE_TARGET";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR:
+ return "KV_AGENT_ERROR";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED:
+ return "PACKAGE_RESTORE_FINISHED";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE:
+ return "TRANSPORT_ERROR_KV_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD:
+ return "NO_FEEDER_THREAD";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR:
+ return "FULL_AGENT_ERROR";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE:
+ return "TRANSPORT_ERROR_FULL_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE:
+ return "RESTORE_COMPLETE";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE:
+ return "START_PACKAGE_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE:
+ return "AGENT_FAILURE";
default:
return "UNKNOWN_ID";
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 6c6784e..1eb9e97 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32285,6 +32285,7 @@
method public static long getNativeHeapFreeSize();
method public static long getNativeHeapSize();
method public static long getPss();
+ method @FlaggedApi("android.os.remove_app_profiler_pss_collection") public static long getRss();
method public static String getRuntimeStat(String);
method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
method @Deprecated public static int getThreadAllocCount();
@@ -44887,6 +44888,7 @@
method public int getSubscriptionType();
method public int getUsageSetting();
method public boolean isEmbedded();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isNtn();
method public boolean isOpportunistic();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR;
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 6b7910a..afb10f5 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -265,8 +265,6 @@
New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.reset()
UnflaggedApi: android.database.sqlite.SQLiteRawStatement#step():
New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.step()
-UnflaggedApi: android.database.sqlite.SQLiteRawStatement#toString():
- New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.toString()
UnflaggedApi: android.graphics.Gainmap#Gainmap(android.graphics.Gainmap, android.graphics.Bitmap):
New API must be flagged with @FlaggedApi: constructor android.graphics.Gainmap(android.graphics.Gainmap,android.graphics.Bitmap)
UnflaggedApi: android.graphics.Path#computeBounds(android.graphics.RectF):
@@ -321,8 +319,6 @@
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onBind(android.content.Intent)
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onClose():
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onClose()
-UnflaggedApi: android.media.midi.MidiUmpDeviceService#onCreate():
- New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onCreate()
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onDeviceStatusChanged(android.media.midi.MidiDeviceStatus):
New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onDeviceStatusChanged(android.media.midi.MidiDeviceStatus)
UnflaggedApi: android.media.midi.MidiUmpDeviceService#onGetInputPortReceivers():
@@ -335,8 +331,6 @@
New API must be flagged with @FlaggedApi: field android.os.UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO
UnflaggedApi: android.provider.Settings#ACTION_CREDENTIAL_PROVIDER:
New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_CREDENTIAL_PROVIDER
-UnflaggedApi: android.telecom.Call.Details#PROPERTY_IS_TRANSACTIONAL:
- New API must be flagged with @FlaggedApi: field android.telecom.Call.Details.PROPERTY_IS_TRANSACTIONAL
UnflaggedApi: android.telecom.Call.Details#getId():
New API must be flagged with @FlaggedApi: method android.telecom.Call.Details.getId()
UnflaggedApi: android.telephony.CarrierConfigManager#KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE:
@@ -355,46 +349,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE
UnflaggedApi: android.telephony.TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED:
New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
-UnflaggedApi: android.text.BoringLayout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.BoringLayout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.BoringLayout.Metrics#getDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.BoringLayout.Metrics.getDrawingBoundingBox()
-UnflaggedApi: android.text.DynamicLayout#getLineBreakConfig():
- New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.getLineBreakConfig()
UnflaggedApi: android.text.DynamicLayout.Builder#setLineBreakConfig(android.graphics.text.LineBreakConfig):
New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setLineBreakConfig(android.graphics.text.LineBreakConfig)
UnflaggedApi: android.text.DynamicLayout.Builder#setUseBoundsForWidth(boolean):
New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.Layout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.Layout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.Layout#getBreakStrategy():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getBreakStrategy()
-UnflaggedApi: android.text.Layout#getEllipsize():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getEllipsize()
-UnflaggedApi: android.text.Layout#getHyphenationFrequency():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getHyphenationFrequency()
-UnflaggedApi: android.text.Layout#getJustificationMode():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getJustificationMode()
-UnflaggedApi: android.text.Layout#getLeftIndents():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLeftIndents()
-UnflaggedApi: android.text.Layout#getLineBreakConfig():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineBreakConfig()
-UnflaggedApi: android.text.Layout#getLineSpacingAmount():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingAmount()
-UnflaggedApi: android.text.Layout#getLineSpacingMultiplier():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingMultiplier()
-UnflaggedApi: android.text.Layout#getMaxLines():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getMaxLines()
-UnflaggedApi: android.text.Layout#getRightIndents():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getRightIndents()
-UnflaggedApi: android.text.Layout#getTextDirectionHeuristic():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getTextDirectionHeuristic()
-UnflaggedApi: android.text.Layout#getUseBoundsForWidth():
- New API must be flagged with @FlaggedApi: method android.text.Layout.getUseBoundsForWidth()
-UnflaggedApi: android.text.Layout#isFontPaddingIncluded():
- New API must be flagged with @FlaggedApi: method android.text.Layout.isFontPaddingIncluded()
-UnflaggedApi: android.text.Layout.Builder:
- New API must be flagged with @FlaggedApi: class android.text.Layout.Builder
UnflaggedApi: android.text.Layout.Builder#Builder(CharSequence, int, int, android.text.TextPaint, int):
New API must be flagged with @FlaggedApi: constructor android.text.Layout.Builder(CharSequence,int,int,android.text.TextPaint,int)
UnflaggedApi: android.text.Layout.Builder#build():
@@ -429,24 +387,12 @@
New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setRightIndents(int[])
UnflaggedApi: android.text.Layout.Builder#setTextDirectionHeuristic(android.text.TextDirectionHeuristic):
New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setTextDirectionHeuristic(android.text.TextDirectionHeuristic)
-UnflaggedApi: android.text.Layout.Builder#setUseBoundsForWidth(boolean):
- New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.StaticLayout#computeDrawingBoundingBox():
- New API must be flagged with @FlaggedApi: method android.text.StaticLayout.computeDrawingBoundingBox()
UnflaggedApi: android.text.StaticLayout.Builder#setUseBoundsForWidth(boolean):
New API must be flagged with @FlaggedApi: method android.text.StaticLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.style.LineBreakConfigSpan:
- New API must be flagged with @FlaggedApi: class android.text.style.LineBreakConfigSpan
UnflaggedApi: android.text.style.LineBreakConfigSpan#LineBreakConfigSpan(android.graphics.text.LineBreakConfig):
New API must be flagged with @FlaggedApi: constructor android.text.style.LineBreakConfigSpan(android.graphics.text.LineBreakConfig)
-UnflaggedApi: android.text.style.LineBreakConfigSpan#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.equals(Object)
UnflaggedApi: android.text.style.LineBreakConfigSpan#getLineBreakConfig():
New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.getLineBreakConfig()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.hashCode()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#toString():
- New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.toString()
UnflaggedApi: android.view.HapticScrollFeedbackProvider#HapticScrollFeedbackProvider(android.view.View):
New API must be flagged with @FlaggedApi: constructor android.view.HapticScrollFeedbackProvider(android.view.View)
UnflaggedApi: android.view.HapticScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
@@ -455,16 +401,10 @@
New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onScrollProgress(int,int,int,int)
UnflaggedApi: android.view.HapticScrollFeedbackProvider#onSnapToItem(int, int, int):
New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onSnapToItem(int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(android.view.MotionEvent, int, boolean):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(android.view.MotionEvent,int,boolean)
UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(int,int,int,boolean)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(android.view.MotionEvent, int, int):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(android.view.MotionEvent,int,int)
UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(int, int, int, int):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(int,int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(android.view.MotionEvent, int):
- New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(android.view.MotionEvent,int)
UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(int, int, int):
New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(int,int,int)
UnflaggedApi: android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT:
@@ -515,7 +455,3 @@
New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setClientSupported(boolean)
UnflaggedApi: android.view.inputmethod.InlineSuggestionsRequest.Builder#setServiceSupported(boolean):
New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setServiceSupported(boolean)
-UnflaggedApi: android.widget.TextView#getUseBoundsForWidth():
- New API must be flagged with @FlaggedApi: method android.widget.TextView.getUseBoundsForWidth()
-UnflaggedApi: android.widget.TextView#setUseBoundsForWidth(boolean):
- New API must be flagged with @FlaggedApi: method android.widget.TextView.setUseBoundsForWidth(boolean)
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3de7748..9b8d2b4 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -554,6 +554,30 @@
}
+package android.se.omapi {
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeFrameworkInitializer {
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
+ }
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeServiceManager {
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
+ }
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
+ ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public SeServiceManager.ServiceNotFoundException(@NonNull String);
+ }
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final class SeServiceManager.ServiceRegisterer {
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder get();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void register(@NonNull android.os.IBinder);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder tryGet();
+ }
+
+}
+
package android.telecom {
public abstract class ConnectionService extends android.app.Service {
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index a0d3858..1633835 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -35,26 +35,8 @@
SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnMediaKeyListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.session.MediaSessionManager#setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler):
SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnVolumeKeyLongPressListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
- SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
- SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_STACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_STACK
-UnflaggedApi: android.Manifest.permission#CONTROL_AUTOMOTIVE_GNSS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS
-UnflaggedApi: android.Manifest.permission#GET_INTENT_SENDER_INTENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_INTENT_SENDER_INTENT
-UnflaggedApi: android.Manifest.permission#MAKE_UID_VISIBLE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MAKE_UID_VISIBLE
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
UnflaggedApi: android.Manifest.permission#USE_COMPANION_TRANSPORTS:
@@ -89,13 +71,7 @@
New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.OnTransportsChangedListener.onTransportsChanged(java.util.List<android.companion.AssociationInfo>)
UnflaggedApi: android.content.Context#REMOTE_AUTH_SERVICE:
New API must be flagged with @FlaggedApi: field android.content.Context.REMOTE_AUTH_SERVICE
-UnflaggedApi: android.content.ContextWrapper#createContextForSdkInSandbox(android.content.pm.ApplicationInfo, int):
- New API must be flagged with @FlaggedApi: method android.content.ContextWrapper.createContextForSdkInSandbox(android.content.pm.ApplicationInfo,int)
-UnflaggedApi: android.media.session.MediaController.PlaybackInfo#PlaybackInfo(int, int, int, int, android.media.AudioAttributes, String):
- New API must be flagged with @FlaggedApi: constructor android.media.session.MediaController.PlaybackInfo(int,int,int,int,android.media.AudioAttributes,String)
UnflaggedApi: android.os.IpcDataCache#MODULE_TELEPHONY:
New API must be flagged with @FlaggedApi: field android.os.IpcDataCache.MODULE_TELEPHONY
-UnflaggedApi: android.provider.ContactsContract.RawContactsEntity#queryRawContactEntity(android.content.ContentResolver, long):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.RawContactsEntity.queryRawContactEntity(android.content.ContentResolver,long)
UnflaggedApi: android.provider.Settings.Config#getAllStrings():
New API must be flagged with @FlaggedApi: method android.provider.Settings.Config.getAllStrings()
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c9db763..ad65806 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,6 +95,7 @@
field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
field public static final String CALL_AUDIO_INTERCEPTION = "android.permission.CALL_AUDIO_INTERCEPTION";
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
+ field public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
@@ -3231,6 +3232,7 @@
method public int getDefaultActivityPolicy();
method public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
+ method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent();
method public int getLockState();
method @Nullable public String getName();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -3262,6 +3264,7 @@
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
+ method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
@@ -3483,6 +3486,7 @@
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
+ field public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -3798,10 +3802,6 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
- public class PackageInfo implements android.os.Parcelable {
- field public boolean isArchived;
- }
-
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
@@ -3879,6 +3879,7 @@
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
+ field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
}
public abstract class PackageManager {
@@ -9628,6 +9629,73 @@
}
+package android.nfc.cardemulation {
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable {
+ ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
+ }
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
+ }
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
+ }
+
+}
+
package android.os {
public class BatteryManager {
@@ -10636,12 +10704,14 @@
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
- method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+ method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
+ method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
- method @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+ method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
+ method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -16707,6 +16777,7 @@
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index d62bea8..b11a686 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -21,28 +21,10 @@
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
-MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
-
-
MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0:
- Missing nullability on parameter `inputId` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
- Missing nullability on parameter `eventType` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
- Missing nullability on parameter `eventArgs` in method `onEvent`
MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
Missing nullability on parameter `base` in method `attachBaseContext`
-MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
- Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
- Missing nullability on field `METADATA_AUTHORITY_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
- Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSyncState`
MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0:
Missing nullability on parameter `context` in method `attachInfo`
MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1:
@@ -61,10 +43,6 @@
Missing nullability on parameter `intent` in method `onUnbind`
MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- Missing nullability on method `setServiceId` return
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
- Missing nullability on parameter `serviceId` in method `setServiceId`
ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
@@ -173,12 +151,6 @@
SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler):
SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setBeamPushUrisCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setNdefPushMessageCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...):
- SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setOnNdefPushCompleteCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
@@ -225,1350 +197,32 @@
SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_CONTEXT_EVENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_LIGHT_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RADIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RADIO
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RESPONSE_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_CACHE_FILESYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CACHE_FILESYSTEM
-UnflaggedApi: android.Manifest.permission#ACCESS_CONTEXT_HUB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CONTEXT_HUB
-UnflaggedApi: android.Manifest.permission#ACCESS_DRM_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#ACCESS_FPS_COUNTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_FPS_COUNTER
-UnflaggedApi: android.Manifest.permission#ACCESS_INSTANT_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_INSTANT_APPS
-UnflaggedApi: android.Manifest.permission#ACCESS_LOCUS_ID_USAGE_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_LOCUS_ID_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_MOCK_LOCATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MOCK_LOCATION
-UnflaggedApi: android.Manifest.permission#ACCESS_MTP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MTP
-UnflaggedApi: android.Manifest.permission#ACCESS_NETWORK_CONDITIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NETWORK_CONDITIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_PDB_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_PDB_STATE
-UnflaggedApi: android.Manifest.permission#ACCESS_RCS_USER_CAPABILITY_EXCHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE
-UnflaggedApi: android.Manifest.permission#ACCESS_SHARED_LIBRARIES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHARED_LIBRARIES
-UnflaggedApi: android.Manifest.permission#ACCESS_SHORTCUTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHORTCUTS
UnflaggedApi: android.Manifest.permission#ACCESS_SMARTSPACE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#ACCESS_SURFACE_FLINGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#ACCESS_TUNED_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TUNED_INFO
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_DESCRAMBLER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_DESCRAMBLER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_SHARED_FILTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_SHARED_FILTER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_TUNER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_TUNER
-UnflaggedApi: android.Manifest.permission#ACCESS_ULTRASOUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_ULTRASOUND
-UnflaggedApi: android.Manifest.permission#ACCESS_VIBRATOR_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_VIBRATOR_STATE
-UnflaggedApi: android.Manifest.permission#ACTIVITY_EMBEDDING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACTIVITY_EMBEDDING
-UnflaggedApi: android.Manifest.permission#ADD_ALWAYS_UNLOCKED_DISPLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADD_TRUSTED_DISPLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_TRUSTED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADJUST_RUNTIME_PERMISSIONS_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
-UnflaggedApi: android.Manifest.permission#ALLOCATE_AGGRESSIVE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOCATE_AGGRESSIVE
-UnflaggedApi: android.Manifest.permission#ALLOW_ANY_CODEC_FOR_PLAYBACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK
-UnflaggedApi: android.Manifest.permission#ALLOW_PLACE_IN_MULTI_PANE_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS
-UnflaggedApi: android.Manifest.permission#ALLOW_SLIPPERY_TOUCHES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES
UnflaggedApi: android.Manifest.permission#ALWAYS_UPDATE_WALLPAPER:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER
-UnflaggedApi: android.Manifest.permission#AMBIENT_WALLPAPER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.AMBIENT_WALLPAPER
-UnflaggedApi: android.Manifest.permission#APPROVE_INCIDENT_REPORTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.APPROVE_INCIDENT_REPORTS
-UnflaggedApi: android.Manifest.permission#ASSOCIATE_COMPANION_DEVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES
-UnflaggedApi: android.Manifest.permission#BACKGROUND_CAMERA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKGROUND_CAMERA
-UnflaggedApi: android.Manifest.permission#BACKUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKUP
-UnflaggedApi: android.Manifest.permission#BATTERY_PREDICTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BATTERY_PREDICTION
-UnflaggedApi: android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ATTENTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ATTENTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_AUGMENTED_AUTOFILL_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AUGMENTED_AUTOFILL_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_DIAGNOSTIC_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_STREAMING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_STREAMING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CELL_BROADCAST_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_SUGGESTIONS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DIRECTORY_SEARCH:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DIRECTORY_SEARCH
-UnflaggedApi: android.Manifest.permission#BIND_DISPLAY_HASHING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DISPLAY_HASHING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DOMAIN_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_EUICC_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EUICC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_FIELD_CLASSIFICATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_FIELD_CLASSIFICATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_GBA_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_GBA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_HOTWORD_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_IMS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_IMS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_KEYGUARD_APPWIDGET:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_KEYGUARD_APPWIDGET
-UnflaggedApi: android.Manifest.permission#BIND_MUSIC_RECOGNITION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NETWORK_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PRINT_RECOMMENDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PRINT_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ROTATION_RESOLVER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SATELLITE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SATELLITE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SETTINGS_SUGGESTIONS_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SOUND_TRIGGER_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_DATA_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_DATA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_NETWORK_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_NETWORK_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRACE_REPORT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRACE_REPORT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRANSLATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRANSLATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRUST_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_TV_REMOTE_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TV_REMOTE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_VISUAL_QUERY_DETECTION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_MAP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#BRICK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRICK
-UnflaggedApi: android.Manifest.permission#BRIGHTNESS_SLIDER_USAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE
-UnflaggedApi: android.Manifest.permission#BROADCAST_CLOSE_SYSTEM_DIALOGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
-UnflaggedApi: android.Manifest.permission#BYPASS_ROLE_QUALIFICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.BYPASS_ROLE_QUALIFICATION
-UnflaggedApi: android.Manifest.permission#CALL_AUDIO_INTERCEPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CALL_AUDIO_INTERCEPTION
-UnflaggedApi: android.Manifest.permission#CAMERA_DISABLE_TRANSMIT_LED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_DISABLE_TRANSMIT_LED
-UnflaggedApi: android.Manifest.permission#CAMERA_OPEN_CLOSE_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER
-UnflaggedApi: android.Manifest.permission#CAPTURE_AUDIO_HOTWORD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
-UnflaggedApi: android.Manifest.permission#CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD
-UnflaggedApi: android.Manifest.permission#CAPTURE_MEDIA_OUTPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_MEDIA_OUTPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TUNER_AUDIO_INPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TV_INPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TV_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_VOICE_COMMUNICATION_OUTPUT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_IDLE_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_IDLE_STATE
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_LAUNCH_TIME_ESTIMATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE
-UnflaggedApi: android.Manifest.permission#CHANGE_DEVICE_IDLE_TEMP_WHITELIST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST
-UnflaggedApi: android.Manifest.permission#CHECK_REMOTE_LOCKSCREEN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN
-UnflaggedApi: android.Manifest.permission#CLEAR_APP_USER_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CLEAR_APP_USER_DATA
-UnflaggedApi: android.Manifest.permission#COMPANION_APPROVE_WIFI_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_DISPLAY_BRIGHTNESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_INTERACT_ACROSS_PROFILES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES
-UnflaggedApi: android.Manifest.permission#CONNECTIVITY_USE_RESTRICTED_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
-UnflaggedApi: android.Manifest.permission#CONTROL_DEVICE_LIGHTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DEVICE_LIGHTS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_COLOR_TRANSFORMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_SATURATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_SATURATION
-UnflaggedApi: android.Manifest.permission#CONTROL_INCALL_EXPERIENCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_INCALL_EXPERIENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_KEYGUARD_SECURE_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#CONTROL_OEM_PAID_NETWORK_PREFERENCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_VPN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_VPN
-UnflaggedApi: android.Manifest.permission#CREATE_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_USERS
-UnflaggedApi: android.Manifest.permission#CREATE_VIRTUAL_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_VIRTUAL_DEVICE
-UnflaggedApi: android.Manifest.permission#CRYPT_KEEPER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.CRYPT_KEEPER
-UnflaggedApi: android.Manifest.permission#DEVICE_POWER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DEVICE_POWER
-UnflaggedApi: android.Manifest.permission#DISABLE_SYSTEM_SOUND_EFFECTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS
-UnflaggedApi: android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE
-UnflaggedApi: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#ENTER_CAR_MODE_PRIORITIZED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED
-UnflaggedApi: android.Manifest.permission#EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#FORCE_BACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_BACK
-UnflaggedApi: android.Manifest.permission#FORCE_STOP_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_STOP_PACKAGES
-UnflaggedApi: android.Manifest.permission#GET_APP_METADATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_METADATA
-UnflaggedApi: android.Manifest.permission#GET_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_HISTORICAL_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_PROCESS_STATE_AND_OOM_SCORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_PROCESS_STATE_AND_OOM_SCORE
-UnflaggedApi: android.Manifest.permission#GET_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GET_TOP_ACTIVITY_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_TOP_ACTIVITY_INFO
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS
-UnflaggedApi: android.Manifest.permission#HANDLE_CAR_MODE_CHANGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HANDLE_CAR_MODE_CHANGES
-UnflaggedApi: android.Manifest.permission#HARDWARE_TEST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HARDWARE_TEST
-UnflaggedApi: android.Manifest.permission#HDMI_CEC:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.HDMI_CEC
-UnflaggedApi: android.Manifest.permission#INJECT_EVENTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INJECT_EVENTS
-UnflaggedApi: android.Manifest.permission#INSTALL_DPC_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DPC_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_DYNAMIC_SYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM
-UnflaggedApi: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_EXISTING_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#INSTALL_PACKAGE_UPDATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_PACKAGE_UPDATES
-UnflaggedApi: android.Manifest.permission#INSTALL_SELF_UPDATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_SELF_UPDATES
-UnflaggedApi: android.Manifest.permission#INTENT_FILTER_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS_FULL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
-UnflaggedApi: android.Manifest.permission#INTERNAL_SYSTEM_WINDOW:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERNAL_SYSTEM_WINDOW
-UnflaggedApi: android.Manifest.permission#INVOKE_CARRIER_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.INVOKE_CARRIER_SETUP
-UnflaggedApi: android.Manifest.permission#KILL_ALL_BACKGROUND_PROCESSES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_ALL_BACKGROUND_PROCESSES
-UnflaggedApi: android.Manifest.permission#KILL_UID:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_UID
-UnflaggedApi: android.Manifest.permission#LAUNCH_DEVICE_MANAGER_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP
+UnflaggedApi: android.Manifest.permission#CAMERA_HEADLESS_SYSTEM_USER:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_HEADLESS_SYSTEM_USER
UnflaggedApi: android.Manifest.permission#LAUNCH_PERMISSION_SETTINGS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_PERMISSION_SETTINGS
-UnflaggedApi: android.Manifest.permission#LOCAL_MAC_ADDRESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCAL_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#LOCATION_BYPASS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCATION_BYPASS
-UnflaggedApi: android.Manifest.permission#LOCK_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCK_DEVICE
-UnflaggedApi: android.Manifest.permission#LOG_FOREGROUND_RESOURCE_USE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOG_FOREGROUND_RESOURCE_USE
-UnflaggedApi: android.Manifest.permission#LOOP_RADIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOOP_RADIO
-UnflaggedApi: android.Manifest.permission#MANAGE_ACCESSIBILITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACCESSIBILITY
-UnflaggedApi: android.Manifest.permission#MANAGE_ACTIVITY_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACTIVITY_TASKS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_HIBERNATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_HIBERNATION
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_OPS_RESTRICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_PREDICTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_PREDICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_TOKENS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_TOKENS
-UnflaggedApi: android.Manifest.permission#MANAGE_AUTO_FILL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_AUTO_FILL
-UnflaggedApi: android.Manifest.permission#MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED
-UnflaggedApi: android.Manifest.permission#MANAGE_CARRIER_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_CA_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CA_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#MANAGE_CLIPBOARD_ACCESS_NOTIFICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION
-UnflaggedApi: android.Manifest.permission#MANAGE_CLOUDSEARCH:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLOUDSEARCH
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_CAPTURE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_CAPTURE
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_SUGGESTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEBUGGING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEBUGGING
-UnflaggedApi: android.Manifest.permission#MANAGE_DEFAULT_APPLICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_ADMINS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_ADMINS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_EXEMPTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_ETHERNET_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ETHERNET_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_FACTORY_RESET_PROTECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_ACTIVITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_ACTIVITY
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_MODE
-UnflaggedApi: android.Manifest.permission#MANAGE_HOTWORD_DETECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_HOTWORD_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_IPSEC_TUNNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_IPSEC_TUNNELS
-UnflaggedApi: android.Manifest.permission#MANAGE_LOW_POWER_STANDBY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_LOW_POWER_STANDBY
-UnflaggedApi: android.Manifest.permission#MANAGE_MUSIC_RECOGNITION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_MUSIC_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_NOTIFICATION_LISTENERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ONE_TIME_PERMISSION_SESSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLE_HOLDERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLLBACKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLLBACKS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROTATION_RESOLVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROTATION_RESOLVER
-UnflaggedApi: android.Manifest.permission#MANAGE_SAFETY_CENTER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SAFETY_CENTER
-UnflaggedApi: android.Manifest.permission#MANAGE_SEARCH_UI:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SEARCH_UI
-UnflaggedApi: android.Manifest.permission#MANAGE_SENSOR_PRIVACY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#MANAGE_SMARTSPACE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#MANAGE_SOUND_TRIGGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SOUND_TRIGGER
-UnflaggedApi: android.Manifest.permission#MANAGE_SPEECH_RECOGNITION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SPEECH_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_PLANS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_USER_ASSOCIATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION
-UnflaggedApi: android.Manifest.permission#MANAGE_TEST_NETWORKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TEST_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_TIME_AND_ZONE_DETECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_UI_TRANSLATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_UI_TRANSLATION
-UnflaggedApi: android.Manifest.permission#MANAGE_USB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USB
-UnflaggedApi: android.Manifest.permission#MANAGE_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USERS
-UnflaggedApi: android.Manifest.permission#MANAGE_USER_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION
-UnflaggedApi: android.Manifest.permission#MANAGE_WEAK_ESCROW_TOKEN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN
-UnflaggedApi: android.Manifest.permission#MANAGE_WEARABLE_SENSING_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#MANAGE_WIFI_COUNTRY_CODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE
-UnflaggedApi: android.Manifest.permission#MARK_DEVICE_ORGANIZATION_OWNED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED
-UnflaggedApi: android.Manifest.permission#MEDIA_RESOURCE_OVERRIDE_PID:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID
-UnflaggedApi: android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA
-UnflaggedApi: android.Manifest.permission#MODIFY_APPWIDGET_BIND_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_ROUTING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_ROUTING
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_SETTINGS_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#MODIFY_CELL_BROADCASTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#MODIFY_DAY_NIGHT_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_DAY_NIGHT_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_PARENTAL_CONTROLS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_PARENTAL_CONTROLS
-UnflaggedApi: android.Manifest.permission#MODIFY_QUIET_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_QUIET_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE
-UnflaggedApi: android.Manifest.permission#MONITOR_DEVICE_CONFIG_ACCESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS
-UnflaggedApi: android.Manifest.permission#MOVE_PACKAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.MOVE_PACKAGE
-UnflaggedApi: android.Manifest.permission#NETWORK_AIRPLANE_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_AIRPLANE_MODE
-UnflaggedApi: android.Manifest.permission#NETWORK_CARRIER_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_CARRIER_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_FACTORY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_FACTORY
-UnflaggedApi: android.Manifest.permission#NETWORK_MANAGED_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_MANAGED_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_SCAN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SCAN
-UnflaggedApi: android.Manifest.permission#NETWORK_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETTINGS
-UnflaggedApi: android.Manifest.permission#NETWORK_SETUP_WIZARD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETUP_WIZARD
-UnflaggedApi: android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP
-UnflaggedApi: android.Manifest.permission#NETWORK_STACK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STACK
-UnflaggedApi: android.Manifest.permission#NETWORK_STATS_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STATS_PROVIDER
-UnflaggedApi: android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON
-UnflaggedApi: android.Manifest.permission#NOTIFICATION_DURING_SETUP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFICATION_DURING_SETUP
-UnflaggedApi: android.Manifest.permission#NOTIFY_TV_INPUTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFY_TV_INPUTS
-UnflaggedApi: android.Manifest.permission#OBSERVE_APP_USAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_APP_USAGE
-UnflaggedApi: android.Manifest.permission#OBSERVE_NETWORK_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_NETWORK_POLICY
-UnflaggedApi: android.Manifest.permission#OBSERVE_ROLE_HOLDERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#OBSERVE_SENSOR_PRIVACY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#OPEN_ACCESSIBILITY_DETAILS_SETTINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS
-UnflaggedApi: android.Manifest.permission#OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD
-UnflaggedApi: android.Manifest.permission#PACKAGE_VERIFICATION_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKAGE_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#PACKET_KEEPALIVE_OFFLOAD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
-UnflaggedApi: android.Manifest.permission#PEERS_MAC_ADDRESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PEERS_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#PERFORM_CDMA_PROVISIONING:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_CDMA_PROVISIONING
-UnflaggedApi: android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION
-UnflaggedApi: android.Manifest.permission#PERFORM_SIM_ACTIVATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_SIM_ACTIVATION
-UnflaggedApi: android.Manifest.permission#POWER_SAVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.POWER_SAVER
-UnflaggedApi: android.Manifest.permission#PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_RESOLVER_RANKER_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_TRUST_AGENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#PROVISION_DEMO_DEVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVISION_DEMO_DEVICE
-UnflaggedApi: android.Manifest.permission#QUERY_ADMIN_POLICY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_ADMIN_POLICY
-UnflaggedApi: android.Manifest.permission#QUERY_CLONED_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_CLONED_APPS
-UnflaggedApi: android.Manifest.permission#QUERY_USERS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_USERS
-UnflaggedApi: android.Manifest.permission#RADIO_SCAN_WITHOUT_LOCATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION
-UnflaggedApi: android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION
-UnflaggedApi: android.Manifest.permission#READ_APP_SPECIFIC_LOCALES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_APP_SPECIFIC_LOCALES
-UnflaggedApi: android.Manifest.permission#READ_CARRIER_APP_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CARRIER_APP_INFO
-UnflaggedApi: android.Manifest.permission#READ_CELL_BROADCASTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#READ_CLIPBOARD_IN_BACKGROUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND
-UnflaggedApi: android.Manifest.permission#READ_CONTENT_RATING_SYSTEMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS
-UnflaggedApi: android.Manifest.permission#READ_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#READ_DREAM_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#READ_GLOBAL_APP_SEARCH_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA
UnflaggedApi: android.Manifest.permission#READ_INSTALLED_SESSION_PATHS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALLED_SESSION_PATHS
-UnflaggedApi: android.Manifest.permission#READ_INSTALL_SESSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALL_SESSIONS
-UnflaggedApi: android.Manifest.permission#READ_NETWORK_USAGE_HISTORY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_NETWORK_USAGE_HISTORY
-UnflaggedApi: android.Manifest.permission#READ_OEM_UNLOCK_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#READ_PEOPLE_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PEOPLE_DATA
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICES
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICE_RECOMMENDATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS
-UnflaggedApi: android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
-UnflaggedApi: android.Manifest.permission#READ_PROJECTION_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PROJECTION_STATE
-UnflaggedApi: android.Manifest.permission#READ_RESTRICTED_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RESTRICTED_STATS
-UnflaggedApi: android.Manifest.permission#READ_RUNTIME_PROFILES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RUNTIME_PROFILES
-UnflaggedApi: android.Manifest.permission#READ_SAFETY_CENTER_STATUS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SAFETY_CENTER_STATUS
-UnflaggedApi: android.Manifest.permission#READ_SEARCH_INDEXABLES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SEARCH_INDEXABLES
-UnflaggedApi: android.Manifest.permission#READ_SYSTEM_UPDATE_INFO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SYSTEM_UPDATE_INFO
-UnflaggedApi: android.Manifest.permission#READ_WALLPAPER_INTERNAL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WALLPAPER_INTERNAL
-UnflaggedApi: android.Manifest.permission#READ_WIFI_CREDENTIAL:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WIFI_CREDENTIAL
-UnflaggedApi: android.Manifest.permission#READ_WRITE_SYNC_DISABLED_MODE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG
-UnflaggedApi: android.Manifest.permission#REAL_GET_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REAL_GET_TASKS
-UnflaggedApi: android.Manifest.permission#RECEIVE_BLUETOOTH_MAP:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#RECEIVE_DATA_ACTIVITY_CHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE
-UnflaggedApi: android.Manifest.permission#RECEIVE_DEVICE_CUSTOMIZATION_READY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
-UnflaggedApi: android.Manifest.permission#RECEIVE_WIFI_CREDENTIAL_CHANGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE
-UnflaggedApi: android.Manifest.permission#RECORD_BACKGROUND_AUDIO:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECORD_BACKGROUND_AUDIO
-UnflaggedApi: android.Manifest.permission#RECOVERY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVERY
-UnflaggedApi: android.Manifest.permission#RECOVER_KEYSTORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVER_KEYSTORE
-UnflaggedApi: android.Manifest.permission#REGISTER_CALL_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CALL_PROVIDER
-UnflaggedApi: android.Manifest.permission#REGISTER_CONNECTION_MANAGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CONNECTION_MANAGER
UnflaggedApi: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_NSD_OFFLOAD_ENGINE
-UnflaggedApi: android.Manifest.permission#REGISTER_SIM_SUBSCRIPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION
-UnflaggedApi: android.Manifest.permission#REGISTER_STATS_PULL_ATOM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_STATS_PULL_ATOM
-UnflaggedApi: android.Manifest.permission#REMOTE_DISPLAY_PROVIDER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOTE_DISPLAY_PROVIDER
-UnflaggedApi: android.Manifest.permission#REMOVE_DRM_CERTIFICATES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#REMOVE_TASKS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_TASKS
-UnflaggedApi: android.Manifest.permission#RENOUNCE_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RENOUNCE_PERMISSIONS
UnflaggedApi: android.Manifest.permission#REPORT_USAGE_STATS:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.REPORT_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#REQUEST_NOTIFICATION_ASSISTANT_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#RESET_PASSWORD:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESET_PASSWORD
-UnflaggedApi: android.Manifest.permission#RESTART_WIFI_SUBSYSTEM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTART_WIFI_SUBSYSTEM
-UnflaggedApi: android.Manifest.permission#RESTORE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#RESTRICTED_VR_ACCESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTRICTED_VR_ACCESS
-UnflaggedApi: android.Manifest.permission#RETRIEVE_WINDOW_CONTENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.RETRIEVE_WINDOW_CONTENT
-UnflaggedApi: android.Manifest.permission#REVIEW_ACCESSIBILITY_SERVICES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES
-UnflaggedApi: android.Manifest.permission#REVOKE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#ROTATE_SURFACE_FLINGER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.ROTATE_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#SATELLITE_COMMUNICATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SATELLITE_COMMUNICATION
-UnflaggedApi: android.Manifest.permission#SCHEDULE_PRIORITIZED_ALARM:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM
-UnflaggedApi: android.Manifest.permission#SECURE_ELEMENT_PRIVILEGED_OPERATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION
-UnflaggedApi: android.Manifest.permission#SEND_CATEGORY_CAR_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_CATEGORY_CAR_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#SEND_DEVICE_CUSTOMIZATION_READY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#SEND_SAFETY_CENTER_UPDATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
-UnflaggedApi: android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS
-UnflaggedApi: android.Manifest.permission#SEND_SMS_NO_CONFIRMATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SMS_NO_CONFIRMATION
-UnflaggedApi: android.Manifest.permission#SERIAL_PORT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SERIAL_PORT
-UnflaggedApi: android.Manifest.permission#SET_ACTIVITY_WATCHER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ACTIVITY_WATCHER
-UnflaggedApi: android.Manifest.permission#SET_CLIP_SOURCE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_CLIP_SOURCE
-UnflaggedApi: android.Manifest.permission#SET_DEFAULT_ACCOUNT_FOR_CONTACTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS
-UnflaggedApi: android.Manifest.permission#SET_HARMFUL_APP_WARNINGS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_HARMFUL_APP_WARNINGS
-UnflaggedApi: android.Manifest.permission#SET_LOW_POWER_STANDBY_PORTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS
-UnflaggedApi: android.Manifest.permission#SET_MEDIA_KEY_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_MEDIA_KEY_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_ORIENTATION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ORIENTATION
-UnflaggedApi: android.Manifest.permission#SET_POINTER_SPEED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_POINTER_SPEED
-UnflaggedApi: android.Manifest.permission#SET_SCREEN_COMPATIBILITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SCREEN_COMPATIBILITY
-UnflaggedApi: android.Manifest.permission#SET_SYSTEM_AUDIO_CAPTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
-UnflaggedApi: android.Manifest.permission#SET_UNRESTRICTED_KEEP_CLEAR_AREAS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS
-UnflaggedApi: android.Manifest.permission#SET_VOLUME_KEY_LONG_PRESS_LISTENER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_COMPONENT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_COMPONENT
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_DIM_AMOUNT:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT
-UnflaggedApi: android.Manifest.permission#SHOW_KEYGUARD_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHOW_KEYGUARD_MESSAGE
-UnflaggedApi: android.Manifest.permission#SHUTDOWN:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHUTDOWN
-UnflaggedApi: android.Manifest.permission#SIGNAL_REBOOT_READINESS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SIGNAL_REBOOT_READINESS
-UnflaggedApi: android.Manifest.permission#SOUND_TRIGGER_RUN_IN_BATTERY_SAVER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER
-UnflaggedApi: android.Manifest.permission#STAGE_HEALTH_CONNECT_REMOTE_DATA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA
-UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND
-UnflaggedApi: android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES
-UnflaggedApi: android.Manifest.permission#START_REVIEW_PERMISSION_DECISIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS
-UnflaggedApi: android.Manifest.permission#START_TASKS_FROM_RECENTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_TASKS_FROM_RECENTS
-UnflaggedApi: android.Manifest.permission#STATUS_BAR_SERVICE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STATUS_BAR_SERVICE
-UnflaggedApi: android.Manifest.permission#STOP_APP_SWITCHES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.STOP_APP_SWITCHES
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_NOTIFICATION_APP_NAME:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON
-UnflaggedApi: android.Manifest.permission#SUGGEST_EXTERNAL_TIME:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUGGEST_EXTERNAL_TIME
-UnflaggedApi: android.Manifest.permission#SUSPEND_APPS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUSPEND_APPS
-UnflaggedApi: android.Manifest.permission#SYSTEM_APPLICATION_OVERLAY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY
-UnflaggedApi: android.Manifest.permission#SYSTEM_CAMERA:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_CAMERA
-UnflaggedApi: android.Manifest.permission#TETHER_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TETHER_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#TIS_EXTENSION_INTERFACE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TIS_EXTENSION_INTERFACE
-UnflaggedApi: android.Manifest.permission#TOGGLE_AUTOMOTIVE_PROJECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION
-UnflaggedApi: android.Manifest.permission#TRIGGER_LOST_MODE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TRIGGER_LOST_MODE
-UnflaggedApi: android.Manifest.permission#TV_INPUT_HARDWARE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_INPUT_HARDWARE
-UnflaggedApi: android.Manifest.permission#TV_VIRTUAL_REMOTE_CONTROLLER:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER
-UnflaggedApi: android.Manifest.permission#UNLIMITED_SHORTCUTS_API_CALLS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UNLIMITED_SHORTCUTS_API_CALLS
-UnflaggedApi: android.Manifest.permission#UPDATE_APP_OPS_STATS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#UPDATE_DEVICE_MANAGEMENT_RESOURCES:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES
-UnflaggedApi: android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
-UnflaggedApi: android.Manifest.permission#UPDATE_FONTS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_FONTS
-UnflaggedApi: android.Manifest.permission#UPDATE_LOCK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_LOCK
-UnflaggedApi: android.Manifest.permission#UPGRADE_RUNTIME_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#USER_ACTIVITY:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USER_ACTIVITY
-UnflaggedApi: android.Manifest.permission#USE_COLORIZED_NOTIFICATIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#USE_RESERVED_DISK:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_RESERVED_DISK
-UnflaggedApi: android.Manifest.permission#UWB_PRIVILEGED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.UWB_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#WHITELIST_AUTO_REVOKE_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WHITELIST_RESTRICTED_PERMISSIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WIFI_ACCESS_COEX_UNSAFE_CHANNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_SET_DEVICE_MOBILITY_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_COEX_UNSAFE_CHANNELS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_USABILITY_STATS_SCORE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE
-UnflaggedApi: android.Manifest.permission#WRITE_ALLOWLISTED_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DEVICE_CONFIG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DREAM_STATE:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
-UnflaggedApi: android.Manifest.permission#WRITE_OBB:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_OBB
-UnflaggedApi: android.Manifest.permission#WRITE_SECURITY_LOG:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SECURITY_LOG
-UnflaggedApi: android.Manifest.permission#WRITE_SMS:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SMS
-UnflaggedApi: android.Manifest.permission_group#UNDEFINED:
- New API must be flagged with @FlaggedApi: field android.Manifest.permission_group.UNDEFINED
-UnflaggedApi: android.R.array#config_keySystemUuidMapping:
- New API must be flagged with @FlaggedApi: field android.R.array.config_keySystemUuidMapping
-UnflaggedApi: android.R.array#config_optionalIpSecAlgorithms:
- New API must be flagged with @FlaggedApi: field android.R.array.config_optionalIpSecAlgorithms
-UnflaggedApi: android.R.attr#allowClearUserDataOnFailedRestore:
- New API must be flagged with @FlaggedApi: field android.R.attr.allowClearUserDataOnFailedRestore
-UnflaggedApi: android.R.attr#gameSessionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.gameSessionService
-UnflaggedApi: android.R.attr#hotwordDetectionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.hotwordDetectionService
-UnflaggedApi: android.R.attr#isVrOnly:
- New API must be flagged with @FlaggedApi: field android.R.attr.isVrOnly
-UnflaggedApi: android.R.attr#minExtensionVersion:
- New API must be flagged with @FlaggedApi: field android.R.attr.minExtensionVersion
-UnflaggedApi: android.R.attr#playHomeTransitionSound:
- New API must be flagged with @FlaggedApi: field android.R.attr.playHomeTransitionSound
-UnflaggedApi: android.R.attr#requiredSystemPropertyName:
- New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyName
-UnflaggedApi: android.R.attr#requiredSystemPropertyValue:
- New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyValue
-UnflaggedApi: android.R.attr#sdkVersion:
- New API must be flagged with @FlaggedApi: field android.R.attr.sdkVersion
-UnflaggedApi: android.R.attr#supportsAmbientMode:
- New API must be flagged with @FlaggedApi: field android.R.attr.supportsAmbientMode
-UnflaggedApi: android.R.attr#userRestriction:
- New API must be flagged with @FlaggedApi: field android.R.attr.userRestriction
-UnflaggedApi: android.R.attr#visualQueryDetectionService:
- New API must be flagged with @FlaggedApi: field android.R.attr.visualQueryDetectionService
-UnflaggedApi: android.R.bool#config_enableDefaultNotes:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotes
-UnflaggedApi: android.R.bool#config_enableDefaultNotesForWorkProfile:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotesForWorkProfile
-UnflaggedApi: android.R.bool#config_enableQrCodeScannerOnLockScreen:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_enableQrCodeScannerOnLockScreen
-UnflaggedApi: android.R.bool#config_safetyProtectionEnabled:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_safetyProtectionEnabled
-UnflaggedApi: android.R.bool#config_sendPackageName:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_sendPackageName
-UnflaggedApi: android.R.bool#config_showDefaultAssistant:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultAssistant
-UnflaggedApi: android.R.bool#config_showDefaultEmergency:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultEmergency
-UnflaggedApi: android.R.bool#config_showDefaultHome:
- New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultHome
-UnflaggedApi: android.R.color#system_notification_accent_color:
- New API must be flagged with @FlaggedApi: field android.R.color.system_notification_accent_color
-UnflaggedApi: android.R.dimen#config_restrictedIconSize:
- New API must be flagged with @FlaggedApi: field android.R.dimen.config_restrictedIconSize
-UnflaggedApi: android.R.dimen#config_viewConfigurationHandwritingGestureLineMargin:
- New API must be flagged with @FlaggedApi: field android.R.dimen.config_viewConfigurationHandwritingGestureLineMargin
-UnflaggedApi: android.R.drawable#ic_info:
- New API must be flagged with @FlaggedApi: field android.R.drawable.ic_info
-UnflaggedApi: android.R.drawable#ic_safety_protection:
- New API must be flagged with @FlaggedApi: field android.R.drawable.ic_safety_protection
-UnflaggedApi: android.R.raw#loaderror:
- New API must be flagged with @FlaggedApi: field android.R.raw.loaderror
-UnflaggedApi: android.R.raw#nodomain:
- New API must be flagged with @FlaggedApi: field android.R.raw.nodomain
-UnflaggedApi: android.R.string#config_customMediaKeyDispatcher:
- New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaKeyDispatcher
-UnflaggedApi: android.R.string#config_customMediaSessionPolicyProvider:
- New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaSessionPolicyProvider
-UnflaggedApi: android.R.string#config_defaultAssistant:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAssistant
-UnflaggedApi: android.R.string#config_defaultAutomotiveNavigation:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAutomotiveNavigation
-UnflaggedApi: android.R.string#config_defaultBrowser:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultBrowser
-UnflaggedApi: android.R.string#config_defaultCallRedirection:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallRedirection
-UnflaggedApi: android.R.string#config_defaultCallScreening:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallScreening
-UnflaggedApi: android.R.string#config_defaultDialer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultDialer
-UnflaggedApi: android.R.string#config_defaultNotes:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultNotes
UnflaggedApi: android.R.string#config_defaultRetailDemo:
New API must be flagged with @FlaggedApi: field android.R.string.config_defaultRetailDemo
-UnflaggedApi: android.R.string#config_defaultSms:
- New API must be flagged with @FlaggedApi: field android.R.string.config_defaultSms
-UnflaggedApi: android.R.string#config_devicePolicyManagement:
- New API must be flagged with @FlaggedApi: field android.R.string.config_devicePolicyManagement
-UnflaggedApi: android.R.string#config_feedbackIntentExtraKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentExtraKey
-UnflaggedApi: android.R.string#config_feedbackIntentNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentNameKey
-UnflaggedApi: android.R.string#config_helpIntentExtraKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentExtraKey
-UnflaggedApi: android.R.string#config_helpIntentNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameKey:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameValue:
- New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameValue
-UnflaggedApi: android.R.string#config_systemActivityRecognizer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemActivityRecognizer
-UnflaggedApi: android.R.string#config_systemAmbientAudioIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAmbientAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAppProtectionService:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAppProtectionService
-UnflaggedApi: android.R.string#config_systemAudioIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAutomotiveCalendarSyncManager:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCalendarSyncManager
-UnflaggedApi: android.R.string#config_systemAutomotiveCluster:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCluster
-UnflaggedApi: android.R.string#config_systemAutomotiveProjection:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveProjection
-UnflaggedApi: android.R.string#config_systemCallStreaming:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemCallStreaming
-UnflaggedApi: android.R.string#config_systemCompanionDeviceProvider:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemCompanionDeviceProvider
-UnflaggedApi: android.R.string#config_systemContacts:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemContacts
-UnflaggedApi: android.R.string#config_systemFinancedDeviceController:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemFinancedDeviceController
-UnflaggedApi: android.R.string#config_systemGallery:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemGallery
-UnflaggedApi: android.R.string#config_systemNotificationIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemNotificationIntelligence
-UnflaggedApi: android.R.string#config_systemSettingsIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSettingsIntelligence
-UnflaggedApi: android.R.string#config_systemShell:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemShell
-UnflaggedApi: android.R.string#config_systemSpeechRecognizer:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSpeechRecognizer
-UnflaggedApi: android.R.string#config_systemSupervision:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemSupervision
-UnflaggedApi: android.R.string#config_systemTelevisionNotificationHandler:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemTelevisionNotificationHandler
-UnflaggedApi: android.R.string#config_systemTextIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemTextIntelligence
-UnflaggedApi: android.R.string#config_systemUi:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemUi
-UnflaggedApi: android.R.string#config_systemUiIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemUiIntelligence
-UnflaggedApi: android.R.string#config_systemVisualIntelligence:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemVisualIntelligence
-UnflaggedApi: android.R.string#config_systemWearHealthService:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWearHealthService
-UnflaggedApi: android.R.string#config_systemWellbeing:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWellbeing
-UnflaggedApi: android.R.string#config_systemWifiCoexManager:
- New API must be flagged with @FlaggedApi: field android.R.string.config_systemWifiCoexManager
-UnflaggedApi: android.R.string#safety_protection_display_text:
- New API must be flagged with @FlaggedApi: field android.R.string.safety_protection_display_text
-UnflaggedApi: android.R.style#Theme_DeviceDefault_DocumentsUI:
- New API must be flagged with @FlaggedApi: field android.R.style.Theme_DeviceDefault_DocumentsUI
-UnflaggedApi: android.R.style#Theme_Leanback_FormWizard:
- New API must be flagged with @FlaggedApi: field android.R.style.Theme_Leanback_FormWizard
UnflaggedApi: android.app.ActivityManager#getExternalHistoricalProcessStartReasons(String, int):
New API must be flagged with @FlaggedApi: method android.app.ActivityManager.getExternalHistoricalProcessStartReasons(String,int)
UnflaggedApi: android.app.AppOpsManager#OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO:
New API must be flagged with @FlaggedApi: field android.app.AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#toString():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.toString()
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.hashCode()
-UnflaggedApi: android.app.GameModeConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.equals(Object)
-UnflaggedApi: android.app.GameModeConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.hashCode()
-UnflaggedApi: android.app.StatusBarManager.DisableInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.StatusBarManager.DisableInfo.toString()
-UnflaggedApi: android.app.Vr2dDisplayProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.equals(Object)
-UnflaggedApi: android.app.Vr2dDisplayProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.hashCode()
-UnflaggedApi: android.app.Vr2dDisplayProperties#toString():
- New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.toString()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.toString()
-UnflaggedApi: android.app.admin.Authority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.Authority.equals(Object)
-UnflaggedApi: android.app.admin.Authority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.Authority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.toString()
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.hashCode()
-UnflaggedApi: android.app.admin.DevicePolicyKeyguardService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyKeyguardService.onDestroy()
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#WORK_PROFILE_DEFAULT_APPS_TITLE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings:
- New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.PermissionSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#LOCATION_AUTO_GRANTED_MESSAGE:
- New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.LOCATION_AUTO_GRANTED_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyState#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyState.toString()
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DpcAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.toString()
-UnflaggedApi: android.app.admin.EnforcingAdmin#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.equals(Object)
-UnflaggedApi: android.app.admin.EnforcingAdmin#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.hashCode()
-UnflaggedApi: android.app.admin.EnforcingAdmin#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.toString()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.toString()
-UnflaggedApi: android.app.admin.LockTaskPolicy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.equals(Object)
-UnflaggedApi: android.app.admin.LockTaskPolicy#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.hashCode()
-UnflaggedApi: android.app.admin.LockTaskPolicy#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.toString()
-UnflaggedApi: android.app.admin.NoArgsPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.NoArgsPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.toString()
-UnflaggedApi: android.app.admin.PolicyKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PolicyKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PolicyState#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.PolicyState.toString()
-UnflaggedApi: android.app.admin.RoleAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.equals(Object)
-UnflaggedApi: android.app.admin.RoleAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.hashCode()
-UnflaggedApi: android.app.admin.RoleAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.toString()
-UnflaggedApi: android.app.admin.UnknownAuthority#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.equals(Object)
-UnflaggedApi: android.app.admin.UnknownAuthority#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.hashCode()
-UnflaggedApi: android.app.admin.UnknownAuthority#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.toString()
-UnflaggedApi: android.app.admin.UserRestrictionPolicyKey#toString():
- New API must be flagged with @FlaggedApi: method android.app.admin.UserRestrictionPolicyKey.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEvent#toString():
- New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEvent.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEventRequest#toString():
- New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEventRequest.toString()
-UnflaggedApi: android.app.assist.ActivityId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.equals(Object)
-UnflaggedApi: android.app.assist.ActivityId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.hashCode()
-UnflaggedApi: android.app.assist.ActivityId#toString():
- New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.toString()
-UnflaggedApi: android.app.assist.AssistStructure.ViewNode#ViewNode():
- New API must be flagged with @FlaggedApi: constructor android.app.assist.AssistStructure.ViewNode()
-UnflaggedApi: android.app.backup.RestoreDescription#toString():
- New API must be flagged with @FlaggedApi: method android.app.backup.RestoreDescription.toString()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#toString():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.toString()
-UnflaggedApi: android.app.cloudsearch.SearchResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionContext#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionContext.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.toString()
-UnflaggedApi: android.app.prediction.AppPredictor#finalize():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictor.finalize()
-UnflaggedApi: android.app.prediction.AppTarget#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTarget.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetEvent.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.hashCode()
-UnflaggedApi: android.app.search.SearchAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.equals(Object)
-UnflaggedApi: android.app.search.SearchAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.hashCode()
-UnflaggedApi: android.app.search.SearchAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.toString()
-UnflaggedApi: android.app.search.SearchSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.equals(Object)
-UnflaggedApi: android.app.search.SearchSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.hashCode()
-UnflaggedApi: android.app.search.SearchSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.toString()
-UnflaggedApi: android.app.search.SearchTargetEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.equals(Object)
-UnflaggedApi: android.app.search.SearchTargetEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTargetEvent#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTargetEvent.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#toString():
- New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.toString()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.equals(Object)
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.hashCode()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.toString()
-UnflaggedApi: android.app.time.TimeCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.toString()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.toString()
-UnflaggedApi: android.app.time.TimeState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.equals(Object)
-UnflaggedApi: android.app.time.TimeState#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.hashCode()
-UnflaggedApi: android.app.time.TimeState#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeState.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.toString()
-UnflaggedApi: android.app.time.TimeZoneState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneState#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.hashCode()
-UnflaggedApi: android.app.time.TimeZoneState#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.toString()
-UnflaggedApi: android.app.time.UnixEpochTime#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.equals(Object)
-UnflaggedApi: android.app.time.UnixEpochTime#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.hashCode()
-UnflaggedApi: android.app.time.UnixEpochTime#toString():
- New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.toString()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.equals(Object)
-UnflaggedApi: android.app.usage.BroadcastResponseStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.hashCode()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#toString():
- New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.toString()
-UnflaggedApi: android.app.usage.CacheQuotaHint#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.equals(Object)
-UnflaggedApi: android.app.usage.CacheQuotaHint#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.hashCode()
-UnflaggedApi: android.app.usage.CacheQuotaService#onCreate():
- New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaService.onCreate()
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_INTERRUPTION:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_SEEN:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED_PRIV:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV
-UnflaggedApi: android.app.usage.UsageEvents.Event#SYSTEM_INTERACTION:
- New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#getInstanceId():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getInstanceId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getNotificationChannelId():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getNotificationChannelId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootClassName():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootClassName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootPackageName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#isInstantApp():
- New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.isInstantApp()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.hashCode()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.hashCode()
UnflaggedApi: android.companion.virtual.VirtualDeviceManager.VirtualDevice#getPersistentDeviceId():
New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceManager.VirtualDevice.getPersistentDeviceId()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#equals(Object):
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.equals(Object)
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#hashCode():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.hashCode()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#toString():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.toString()
-UnflaggedApi: android.companion.virtual.sensor.VirtualSensorConfig#toString():
- New API must be flagged with @FlaggedApi: method android.companion.virtual.sensor.VirtualSensorConfig.toString()
+UnflaggedApi: android.content.Context#THREAD_NETWORK_SERVICE:
+ New API must be flagged with @FlaggedApi: field android.content.Context.THREAD_NETWORK_SERVICE
UnflaggedApi: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
New API must be flagged with @FlaggedApi: field android.content.Intent.ACTION_UNARCHIVE_PACKAGE
-UnflaggedApi: android.content.integrity.Rule#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.equals(Object)
-UnflaggedApi: android.content.integrity.Rule#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.hashCode()
-UnflaggedApi: android.content.integrity.Rule#toString():
- New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.toString()
-UnflaggedApi: android.content.pm.PackageArchiver:
- New API must be flagged with @FlaggedApi: class android.content.pm.PackageArchiver
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_ALL_USERS:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_PACKAGE_NAME:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME
-UnflaggedApi: android.content.pm.PackageArchiver#requestArchive(String, android.content.IntentSender):
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestArchive(String,android.content.IntentSender)
-UnflaggedApi: android.content.pm.PackageArchiver#requestUnarchive(String):
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestUnarchive(String)
-UnflaggedApi: android.content.pm.PackageInfo#isArchived:
- New API must be flagged with @FlaggedApi: field android.content.pm.PackageInfo.isArchived
UnflaggedApi: android.content.pm.PackageInstaller#readInstallInfo(android.os.ParcelFileDescriptor, String, int):
New API must be flagged with @FlaggedApi: method android.content.pm.PackageInstaller.readInstallInfo(android.os.ParcelFileDescriptor,String,int)
UnflaggedApi: android.content.pm.PackageInstaller.InstallInfo#calculateInstalledSize(android.content.pm.PackageInstaller.SessionParams, android.os.ParcelFileDescriptor):
@@ -1579,320 +233,6 @@
New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID
UnflaggedApi: android.content.pm.PackageManager#MATCH_ARCHIVED_PACKAGES:
New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES
-UnflaggedApi: android.content.pm.PackageManager#getPackageArchiver():
- New API must be flagged with @FlaggedApi: method android.content.pm.PackageManager.getPackageArchiver()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.equals(Object)
-UnflaggedApi: android.content.pm.SuspendDialogInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.hashCode()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.toString()
-UnflaggedApi: android.content.pm.UserProperties#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.UserProperties.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#toString():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.hashCode()
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_CONVENIENCE:
- New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#EMPTY_SET:
- New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.EMPTY_SET
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.equals(Object)
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.hashCode()
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.toString()
-UnflaggedApi: android.hardware.display.BrightnessChangeEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessChangeEvent.toString()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.toString()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessCorrection#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubClient#finalize():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubClient.finalize()
-UnflaggedApi: android.hardware.location.ContextHubInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.toString()
-UnflaggedApi: android.hardware.location.ContextHubMessage#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubMessage.toString()
-UnflaggedApi: android.hardware.location.GeofenceHardwareMonitorEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.GeofenceHardwareMonitorEvent.toString()
-UnflaggedApi: android.hardware.location.MemoryRegion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.equals(Object)
-UnflaggedApi: android.hardware.location.MemoryRegion#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.toString()
-UnflaggedApi: android.hardware.location.NanoApp#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoApp.toString()
-UnflaggedApi: android.hardware.location.NanoAppFilter#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppFilter.toString()
-UnflaggedApi: android.hardware.location.NanoAppInstanceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppInstanceInfo.toString()
-UnflaggedApi: android.hardware.location.NanoAppMessage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppMessage#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.toString()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppRpcService#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.hashCode()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.toString()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.toString()
-UnflaggedApi: android.hardware.radio.RadioMetadata#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioMetadata#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.hashCode()
-UnflaggedApi: android.hardware.radio.RadioMetadata#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.equals(Object)
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.toString()
-UnflaggedApi: android.hardware.usb.UsbPort#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPort.toString()
-UnflaggedApi: android.hardware.usb.UsbPortStatus#toString():
- New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPortStatus.toString()
-UnflaggedApi: android.location.CorrelationVector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.equals(Object)
-UnflaggedApi: android.location.CorrelationVector#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.hashCode()
-UnflaggedApi: android.location.CorrelationVector#toString():
- New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.toString()
-UnflaggedApi: android.location.Country#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.Country.equals(Object)
-UnflaggedApi: android.location.Country#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.Country.hashCode()
-UnflaggedApi: android.location.Country#toString():
- New API must be flagged with @FlaggedApi: method android.location.Country.toString()
-UnflaggedApi: android.location.GnssExcessPathInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.equals(Object)
-UnflaggedApi: android.location.GnssExcessPathInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.hashCode()
-UnflaggedApi: android.location.GnssExcessPathInfo#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.toString()
-UnflaggedApi: android.location.GnssMeasurementCorrections#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementCorrections.toString()
-UnflaggedApi: android.location.GnssMeasurementRequest#getWorkSource():
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.getWorkSource()
-UnflaggedApi: android.location.GnssMeasurementRequest.Builder#setWorkSource(android.os.WorkSource):
- New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.Builder.setWorkSource(android.os.WorkSource)
-UnflaggedApi: android.location.GnssReflectingPlane#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.equals(Object)
-UnflaggedApi: android.location.GnssReflectingPlane#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.hashCode()
-UnflaggedApi: android.location.GnssReflectingPlane#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.toString()
-UnflaggedApi: android.location.GnssRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.equals(Object)
-UnflaggedApi: android.location.GnssRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.hashCode()
-UnflaggedApi: android.location.GnssRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssRequest.toString()
-UnflaggedApi: android.location.GnssSingleSatCorrection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.equals(Object)
-UnflaggedApi: android.location.GnssSingleSatCorrection#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.hashCode()
-UnflaggedApi: android.location.GnssSingleSatCorrection#toString():
- New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.toString()
-UnflaggedApi: android.location.GpsClock#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsClock.toString()
-UnflaggedApi: android.location.GpsMeasurement#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsMeasurement.toString()
-UnflaggedApi: android.location.GpsMeasurementsEvent#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsMeasurementsEvent.toString()
-UnflaggedApi: android.location.GpsNavigationMessage#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessage.toString()
-UnflaggedApi: android.location.GpsNavigationMessageEvent#toString():
- New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessageEvent.toString()
-UnflaggedApi: android.location.LastLocationRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.equals(Object)
-UnflaggedApi: android.location.LastLocationRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.hashCode()
-UnflaggedApi: android.location.LastLocationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.toString()
-UnflaggedApi: android.location.SatellitePvt#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.toString()
-UnflaggedApi: android.location.SatellitePvt.ClockInfo#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.ClockInfo.toString()
-UnflaggedApi: android.location.SatellitePvt.PositionEcef#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.PositionEcef.toString()
-UnflaggedApi: android.location.SatellitePvt.VelocityEcef#toString():
- New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.VelocityEcef.toString()
-UnflaggedApi: android.location.provider.ProviderRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.equals(Object)
-UnflaggedApi: android.location.provider.ProviderRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.hashCode()
-UnflaggedApi: android.location.provider.ProviderRequest#toString():
- New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.toString()
-UnflaggedApi: android.media.AudioDeviceAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.equals(Object)
-UnflaggedApi: android.media.AudioDeviceAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.hashCode()
-UnflaggedApi: android.media.AudioDeviceAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.toString()
-UnflaggedApi: android.media.AudioFocusInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.equals(Object)
-UnflaggedApi: android.media.AudioFocusInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.hashCode()
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ECHO_REFERENCE:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ECHO_REFERENCE
-UnflaggedApi: android.media.MediaRecorder.AudioSource#HOTWORD:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.HOTWORD
-UnflaggedApi: android.media.MediaRecorder.AudioSource#RADIO_TUNER:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.RADIO_TUNER
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ULTRASOUND:
- New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ULTRASOUND
-UnflaggedApi: android.media.NearbyDevice#toString():
- New API must be flagged with @FlaggedApi: method android.media.NearbyDevice.toString()
-UnflaggedApi: android.media.VolumeInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.equals(Object)
-UnflaggedApi: android.media.VolumeInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.hashCode()
-UnflaggedApi: android.media.VolumeInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.toString()
UnflaggedApi: android.media.audiopolicy.AudioMix#CREATOR:
New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMix.CREATOR
UnflaggedApi: android.media.audiopolicy.AudioMix#describeContents():
@@ -1903,578 +243,34 @@
New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMixingRule.CREATOR
UnflaggedApi: android.media.audiopolicy.AudioMixingRule#describeContents():
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.describeContents()
-UnflaggedApi: android.media.audiopolicy.AudioMixingRule#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.hashCode()
UnflaggedApi: android.media.audiopolicy.AudioMixingRule#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.media.audiopolicy.AudioPolicy#updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>):
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioPolicy.updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.hashCode()
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#toString():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.toString()
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#toString():
- New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.toString()
-UnflaggedApi: android.media.musicrecognition.MusicRecognitionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.media.musicrecognition.MusicRecognitionService.onCreate()
-UnflaggedApi: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerDetectionService.onUnbind(android.content.Intent)
-UnflaggedApi: android.media.tv.TunedInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.equals(Object)
-UnflaggedApi: android.media.tv.TunedInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.hashCode()
-UnflaggedApi: android.media.tv.TunedInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.toString()
-UnflaggedApi: android.media.tv.TvInputHardwareInfo#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TvInputHardwareInfo.toString()
-UnflaggedApi: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle):
- New API must be flagged with @FlaggedApi: method android.media.tv.TvRecordingClient.RecordingCallback.onEvent(String,String,android.os.Bundle)
-UnflaggedApi: android.media.tv.TvStreamConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.equals(Object)
-UnflaggedApi: android.media.tv.TvStreamConfig#toString():
- New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.toString()
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.equals(Object)
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.hashCode()
-UnflaggedApi: android.net.NetworkKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.equals(Object)
-UnflaggedApi: android.net.NetworkKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.hashCode()
-UnflaggedApi: android.net.NetworkKey#toString():
- New API must be flagged with @FlaggedApi: method android.net.NetworkKey.toString()
-UnflaggedApi: android.net.RssiCurve#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.equals(Object)
-UnflaggedApi: android.net.RssiCurve#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.hashCode()
-UnflaggedApi: android.net.RssiCurve#toString():
- New API must be flagged with @FlaggedApi: method android.net.RssiCurve.toString()
-UnflaggedApi: android.net.ScoredNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.equals(Object)
-UnflaggedApi: android.net.ScoredNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.hashCode()
-UnflaggedApi: android.net.ScoredNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.toString()
-UnflaggedApi: android.net.WebAddress#toString():
- New API must be flagged with @FlaggedApi: method android.net.WebAddress.toString()
-UnflaggedApi: android.net.WifiKey#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.equals(Object)
-UnflaggedApi: android.net.WifiKey#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.hashCode()
-UnflaggedApi: android.net.WifiKey#toString():
- New API must be flagged with @FlaggedApi: method android.net.WifiKey.toString()
-UnflaggedApi: android.net.metrics.ApfProgramEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ApfProgramEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.toString()
-UnflaggedApi: android.net.metrics.ApfStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.equals(Object)
-UnflaggedApi: android.net.metrics.ApfStats#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.toString()
-UnflaggedApi: android.net.metrics.DhcpClientEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.equals(Object)
-UnflaggedApi: android.net.metrics.DhcpClientEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.toString()
-UnflaggedApi: android.net.metrics.DhcpErrorEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpErrorEvent.toString()
-UnflaggedApi: android.net.metrics.IpManagerEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpManagerEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.toString()
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.toString()
-UnflaggedApi: android.net.metrics.NetworkEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.equals(Object)
-UnflaggedApi: android.net.metrics.NetworkEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.toString()
-UnflaggedApi: android.net.metrics.RaEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.equals(Object)
-UnflaggedApi: android.net.metrics.RaEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.toString()
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#toString():
- New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.toString()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.equals(Object)
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.hashCode()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#toString():
- New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.toString()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.toString()
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.hashCode()
UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#isBatteryCharging():
New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.isBatteryCharging()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.toString()
UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder#setBatteryCharging(boolean):
New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder.setBatteryCharging(boolean)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#hashCode():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#toString():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.toString()
-UnflaggedApi: android.os.BatterySaverPolicyConfig#toString():
- New API must be flagged with @FlaggedApi: method android.os.BatterySaverPolicyConfig.toString()
+UnflaggedApi: android.nfc.cardemulation.AidGroup#CONTENTS_FILE_DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.nfc.cardemulation.AidGroup#PARCELABLE_WRITE_RETURN_VALUE:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#CONTENTS_FILE_DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#PARCELABLE_WRITE_RETURN_VALUE:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#CONTENTS_FILE_DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#PARCELABLE_WRITE_RETURN_VALUE:
+ New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_ONBOARDING:
New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_ONBOARDING
-UnflaggedApi: android.os.Build.VERSION#KNOWN_CODENAMES:
- New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.KNOWN_CODENAMES
-UnflaggedApi: android.os.Build.VERSION#PREVIEW_SDK_FINGERPRINT:
- New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.PREVIEW_SDK_FINGERPRINT
-UnflaggedApi: android.os.IncidentManager.PendingReport#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.equals(Object)
-UnflaggedApi: android.os.IncidentManager.PendingReport#toString():
- New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.toString()
-UnflaggedApi: android.os.IncidentReportArgs#toString():
- New API must be flagged with @FlaggedApi: method android.os.IncidentReportArgs.toString()
-UnflaggedApi: android.os.NewUserRequest#toString():
- New API must be flagged with @FlaggedApi: method android.os.NewUserRequest.toString()
-UnflaggedApi: android.os.NewUserResponse#toString():
- New API must be flagged with @FlaggedApi: method android.os.NewUserResponse.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#toString():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#toString():
- New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.toString()
-UnflaggedApi: android.os.ServiceSpecificException#toString():
- New API must be flagged with @FlaggedApi: method android.os.ServiceSpecificException.toString()
-UnflaggedApi: android.os.WorkSource.WorkChain#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.equals(Object)
-UnflaggedApi: android.os.WorkSource.WorkChain#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.hashCode()
-UnflaggedApi: android.os.WorkSource.WorkChain#toString():
- New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.toString()
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.hashCode()
-UnflaggedApi: android.os.connectivity.WifiActivityEnergyInfo#toString():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiActivityEnergyInfo.toString()
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.hashCode()
-UnflaggedApi: android.permission.AdminPermissionControlParams#toString():
- New API must be flagged with @FlaggedApi: method android.permission.AdminPermissionControlParams.toString()
-UnflaggedApi: android.permission.PermissionGroupUsage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.equals(Object)
-UnflaggedApi: android.permission.PermissionGroupUsage#hashCode():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.hashCode()
-UnflaggedApi: android.permission.PermissionGroupUsage#toString():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.toString()
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.equals(Object)
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.equals(Object)
-UnflaggedApi: android.printservice.PrintServiceInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#toString():
- New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.toString()
-UnflaggedApi: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
- New API must be flagged with @FlaggedApi: method android.printservice.recommendation.RecommendationService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.provider.CallLog.CallComposerLoggingException#toString():
- New API must be flagged with @FlaggedApi: method android.provider.CallLog.CallComposerLoggingException.toString()
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_ITEM_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_NAME:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA_SET:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DELETED:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DELETED
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#RAW_CONTACT_BACKUP_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.RAW_CONTACT_BACKUP_ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_ITEM_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_NAME:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#DATA_SET:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#STATE:
- New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.STATE
-UnflaggedApi: android.provider.ContactsContract.Settings#setDefaultAccount(android.content.ContentResolver, android.accounts.Account):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.Settings.setDefaultAccount(android.content.ContentResolver,android.accounts.Account)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#addSimAccount(android.content.ContentResolver, String, String, int, int):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.addSimAccount(android.content.ContentResolver,String,String,int,int)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#removeSimAccounts(android.content.ContentResolver, int):
- New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.removeSimAccounts(android.content.ContentResolver,int)
-UnflaggedApi: android.provider.SearchIndexableData#toString():
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableData.toString()
-UnflaggedApi: android.provider.SearchIndexableResource#toString():
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableResource.toString()
-UnflaggedApi: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo):
- New API must be flagged with @FlaggedApi: method android.provider.SearchIndexablesProvider.attachInfo(android.content.Context,android.content.pm.ProviderInfo)
UnflaggedApi: android.provider.Settings#ACTION_APP_PERMISSIONS_SETTINGS:
New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_APP_PERMISSIONS_SETTINGS
UnflaggedApi: android.provider.Settings.System#putString(android.content.ContentResolver, String, String, boolean, boolean):
New API must be flagged with @FlaggedApi: method android.provider.Settings.System.putString(android.content.ContentResolver,String,String,boolean,boolean)
UnflaggedApi: android.provider.Settings.System#resetToDefaults(android.content.ContentResolver, String):
New API must be flagged with @FlaggedApi: method android.provider.Settings.System.resetToDefaults(android.content.ContentResolver,String)
-UnflaggedApi: android.provider.SimPhonebookContract.SimRecords#QUERY_ARG_PIN2:
- New API must be flagged with @FlaggedApi: field android.provider.SimPhonebookContract.SimRecords.QUERY_ARG_PIN2
-UnflaggedApi: android.provider.Telephony.Carriers#APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#CARRIER_EDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.CARRIER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#EDITED_STATUS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.EDITED_STATUS
-UnflaggedApi: android.provider.Telephony.Carriers#MATCH_ALL_APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MATCH_ALL_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#MAX_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#MODEM_PERSIST:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MODEM_PERSIST
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V4:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V4
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V6:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V6
-UnflaggedApi: android.provider.Telephony.Carriers#NO_APN_SET_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.NO_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#TIME_LIMIT_FOR_MAX_CONNECTIONS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#UNEDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.UNEDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_DELETED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_DELETED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITABLE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITABLE
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_VISIBLE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_VISIBLE
-UnflaggedApi: android.provider.Telephony.Carriers#WAIT_TIME_RETRY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.WAIT_TIME_RETRY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts:
- New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CALL_METHOD_GET_PREFERENCE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CALL_METHOD_GET_PREFERENCE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CATEGORY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CERTAINTY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CERTAINTY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_MESSAGE_CLASS:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_RESPONSE_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_SEVERITY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_SEVERITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_URGENCY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_URGENCY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CONTENT_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CONTENT_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DATA_CODING_SCHEME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DATA_CODING_SCHEME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DEFAULT_SORT_ORDER:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DEFAULT_SORT_ORDER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DELIVERY_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DELIVERY_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_IS_PRIMARY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_IS_PRIMARY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_WARNING_TYPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_WARNING_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOGRAPHICAL_SCOPE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOMETRIES:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOMETRIES
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LAC:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LAC
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LANGUAGE_CODE:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LANGUAGE_CODE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LOCATION_CHECK_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LOCATION_CHECK_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MAXIMUM_WAIT_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MAXIMUM_WAIT_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BODY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BODY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BROADCASTED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BROADCASTED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_DISPLAYED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_DISPLAYED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_FORMAT:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_FORMAT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_HISTORY_URI:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_HISTORY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_PRIORITY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_PRIORITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_READ:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_READ
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#PLMN:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.PLMN
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#RECEIVED_TIME:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.RECEIVED_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERIAL_NUMBER:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERIAL_NUMBER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERVICE_CATEGORY:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERVICE_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SLOT_INDEX:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SLOT_INDEX
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SUBSCRIPTION_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SUBSCRIPTION_ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_COUNT:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._COUNT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_ID:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference:
- New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts.Preference
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_ALERT_VIBRATION_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_ALERT_VIBRATION_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_AREA_UPDATE_INFO_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_AREA_UPDATE_INFO_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_AMBER_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_AMBER_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_EXTREME_THREAT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_EXTREME_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_PRESIDENTIAL_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_PRESIDENTIAL_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_SEVERE_THREAT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_SEVERE_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_EMERGENCY_PERF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_EMERGENCY_PERF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_PUBLIC_SAFETY_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_PUBLIC_SAFETY_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_STATE_LOCAL_TEST_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_STATE_LOCAL_TEST_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_TEST_ALERT_PREF:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_TEST_ALERT_PREF
-UnflaggedApi: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
- New API must be flagged with @FlaggedApi: field android.provider.Telephony.Sms.Intents.ACTION_SMS_EMERGENCY_CB_RECEIVED
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionResult.toString()
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionServiceStatus#toString():
- New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionServiceStatus.toString()
-UnflaggedApi: android.service.appprediction.AppPredictionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.appprediction.AppPredictionService.onCreate()
-UnflaggedApi: android.service.assist.classification.FieldClassificationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationRequest.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationResponse#toString():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationResponse.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.AutofillFieldClassificationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.autofill.AutofillFieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.Dataset.Builder#setContent(android.view.autofill.AutofillId, android.content.ClipData):
- New API must be flagged with @FlaggedApi: method android.service.autofill.Dataset.Builder.setContent(android.view.autofill.AutofillId,android.content.ClipData)
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onCreate()
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.autofill.augmented.FillRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillRequest.toString()
-UnflaggedApi: android.service.autofill.augmented.FillWindow#finalize():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillWindow.finalize()
-UnflaggedApi: android.service.autofill.augmented.PresentationParams.Area#toString():
- New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.PresentationParams.Area.toString()
-UnflaggedApi: android.service.cloudsearch.CloudSearchService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.cloudsearch.CloudSearchService.onCreate()
-UnflaggedApi: android.service.contentcapture.ActivityEvent#toString():
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ActivityEvent.toString()
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.dump(java.io.FileDescriptor,java.io.PrintWriter,String[])
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.onCreate()
-UnflaggedApi: android.service.contentsuggestions.ContentSuggestionsService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.contentsuggestions.ContentSuggestionsService.onCreate()
-UnflaggedApi: android.service.displayhash.DisplayHashParams#toString():
- New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashParams.toString()
-UnflaggedApi: android.service.displayhash.DisplayHashingService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashingService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.equals(Object)
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.hashCode()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#toString():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.toString()
-UnflaggedApi: android.service.euicc.EuiccService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onDestroy()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.equals(Object)
-UnflaggedApi: android.service.games.CreateGameSessionRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.hashCode()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.toString()
-UnflaggedApi: android.service.games.GameSessionService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.games.GameSessionService.onCreate()
-UnflaggedApi: android.service.games.GameStartedEvent#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.equals(Object)
-UnflaggedApi: android.service.games.GameStartedEvent#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.hashCode()
-UnflaggedApi: android.service.games.GameStartedEvent#toString():
- New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.toString()
-UnflaggedApi: android.service.notification.Adjustment#toString():
- New API must be flagged with @FlaggedApi: method android.service.notification.Adjustment.toString()
-UnflaggedApi: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationAssistantService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.service.notification.NotificationStats#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.equals(Object)
-UnflaggedApi: android.service.notification.NotificationStats#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.hashCode()
-UnflaggedApi: android.service.notification.NotificationStats#toString():
- New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.toString()
-UnflaggedApi: android.service.notification.SnoozeCriterion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.equals(Object)
-UnflaggedApi: android.service.notification.SnoozeCriterion#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.hashCode()
-UnflaggedApi: android.service.resolver.ResolverRankerService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverRankerService.onDestroy()
-UnflaggedApi: android.service.resolver.ResolverTarget#toString():
- New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverTarget.toString()
-UnflaggedApi: android.service.rotationresolver.RotationResolutionRequest#toString():
- New API must be flagged with @FlaggedApi: method android.service.rotationresolver.RotationResolutionRequest.toString()
-UnflaggedApi: android.service.search.SearchUiService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.search.SearchUiService.onCreate()
-UnflaggedApi: android.service.smartspace.SmartspaceService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.smartspace.SmartspaceService.onCreate()
-UnflaggedApi: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.service.textclassifier.TextClassifierService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#toString():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.toString()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#toString():
- New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.toString()
-UnflaggedApi: android.service.translation.TranslationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.translation.TranslationService.onCreate()
-UnflaggedApi: android.service.trust.TrustAgentService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.trust.TrustAgentService.onCreate()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.toString()
-UnflaggedApi: android.service.voice.HotwordAudioStream#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.equals(Object)
-UnflaggedApi: android.service.voice.HotwordAudioStream#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.hashCode()
-UnflaggedApi: android.service.voice.HotwordAudioStream#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.toString()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordDetectedResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.toString()
-UnflaggedApi: android.service.voice.HotwordDetectionService#getSystemService(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.HotwordDetectionServiceFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionServiceFailure.toString()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordRejectedResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.toString()
UnflaggedApi: android.service.voice.HotwordTrainingAudio:
New API must be flagged with @FlaggedApi: class android.service.voice.HotwordTrainingAudio
UnflaggedApi: android.service.voice.HotwordTrainingAudio#CONTENTS_FILE_DESCRIPTOR:
@@ -2487,8 +283,6 @@
New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingAudio.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.service.voice.HotwordTrainingAudio#describeContents():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.equals(Object)
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioFormat():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getAudioFormat()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioType():
@@ -2497,10 +291,6 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordAudio()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#getHotwordOffsetMillis():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordOffsetMillis()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.toString()
UnflaggedApi: android.service.voice.HotwordTrainingAudio#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder:
@@ -2513,8 +303,6 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioFormat(android.media.AudioFormat)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setAudioType(int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioType(int)
-UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordAudio(byte...):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordAudio(byte...)
UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordOffsetMillis(int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordOffsetMillis(int)
UnflaggedApi: android.service.voice.HotwordTrainingData:
@@ -2537,18 +325,12 @@
New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingData.TIMEOUT_STAGE_VERY_EARLY
UnflaggedApi: android.service.voice.HotwordTrainingData#describeContents():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingData#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.equals(Object)
UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTimeoutStage():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTimeoutStage()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudios():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTrainingAudios()
-UnflaggedApi: android.service.voice.HotwordTrainingData#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingData#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.toString()
UnflaggedApi: android.service.voice.HotwordTrainingData#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.service.voice.HotwordTrainingData.Builder:
@@ -2563,330 +345,10 @@
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTimeoutStage(int)
UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>):
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>)
-UnflaggedApi: android.service.voice.SoundTriggerFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.SoundTriggerFailure.toString()
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#getSystemService(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#openFileInput(String):
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.openFileInput(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionServiceFailure#toString():
- New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionServiceFailure.toString()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#isInAmbientMode():
- New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.isInAmbientMode()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#onAmbientModeChanged(boolean, long):
- New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.onAmbientModeChanged(boolean,long)
-UnflaggedApi: android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService#onCreate():
- New API must be flagged with @FlaggedApi: method android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService.onCreate()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.equals(Object)
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.hashCode()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#toString():
- New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.toString()
-UnflaggedApi: android.telecom.AudioState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telecom.AudioState.equals(Object)
-UnflaggedApi: android.telecom.AudioState#toString():
- New API must be flagged with @FlaggedApi: method android.telecom.AudioState.toString()
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.equals(Object)
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#hashCode():
- New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.hashCode()
-UnflaggedApi: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- New API must be flagged with @FlaggedApi: method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-UnflaggedApi: android.telecom.Connection.CallFilteringCompletionInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telecom.Connection.CallFilteringCompletionInfo.toString()
UnflaggedApi: android.telecom.StreamingCall#EXTRA_CALL_ID:
New API must be flagged with @FlaggedApi: field android.telecom.StreamingCall.EXTRA_CALL_ID
-UnflaggedApi: android.telephony.CallAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.equals(Object)
-UnflaggedApi: android.telephony.CallAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.hashCode()
-UnflaggedApi: android.telephony.CallAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.toString()
-UnflaggedApi: android.telephony.CallQuality#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.equals(Object)
-UnflaggedApi: android.telephony.CallQuality#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.hashCode()
-UnflaggedApi: android.telephony.CallQuality#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.toString()
-UnflaggedApi: android.telephony.CallState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.equals(Object)
-UnflaggedApi: android.telephony.CallState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.hashCode()
-UnflaggedApi: android.telephony.CallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CallState.toString()
-UnflaggedApi: android.telephony.CarrierRestrictionRules#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CarrierRestrictionRules.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Circle#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Circle.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.LatLng#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.LatLng.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Polygon#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Polygon.toString()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.equals(Object)
-UnflaggedApi: android.telephony.CellBroadcastIdRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.hashCode()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.toString()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.equals(Object)
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.hashCode()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.toString()
-UnflaggedApi: android.telephony.DataThrottlingRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.DataThrottlingRequest.toString()
-UnflaggedApi: android.telephony.ImsiEncryptionInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ImsiEncryptionInfo.toString()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.equals(Object)
-UnflaggedApi: android.telephony.LinkCapacityEstimate#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.hashCode()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.toString()
-UnflaggedApi: android.telephony.LteVopsSupportInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.LteVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.ModemActivityInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.equals(Object)
-UnflaggedApi: android.telephony.ModemActivityInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.hashCode()
-UnflaggedApi: android.telephony.ModemActivityInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.toString()
UnflaggedApi: android.telephony.NetworkRegistrationInfo.Builder#setIsNonTerrestrialNetwork(boolean):
New API must be flagged with @FlaggedApi: method android.telephony.NetworkRegistrationInfo.Builder.setIsNonTerrestrialNetwork(boolean)
-UnflaggedApi: android.telephony.NetworkService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.telephony.NetworkService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.NrVopsSupportInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.NrVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.PhoneCapability#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.equals(Object)
-UnflaggedApi: android.telephony.PhoneCapability#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.hashCode()
-UnflaggedApi: android.telephony.PhoneCapability#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.toString()
-UnflaggedApi: android.telephony.PhoneNumberRange#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.equals(Object)
-UnflaggedApi: android.telephony.PhoneNumberRange#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.hashCode()
-UnflaggedApi: android.telephony.PhoneNumberRange#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.toString()
-UnflaggedApi: android.telephony.PinResult#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.equals(Object)
-UnflaggedApi: android.telephony.PinResult#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.hashCode()
-UnflaggedApi: android.telephony.PinResult#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PinResult.toString()
-UnflaggedApi: android.telephony.PreciseCallState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.equals(Object)
-UnflaggedApi: android.telephony.PreciseCallState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.hashCode()
-UnflaggedApi: android.telephony.PreciseCallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.toString()
-UnflaggedApi: android.telephony.SmsCbCmasInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbCmasInfo.toString()
-UnflaggedApi: android.telephony.SmsCbEtwsInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbEtwsInfo.toString()
-UnflaggedApi: android.telephony.SmsCbLocation#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.equals(Object)
-UnflaggedApi: android.telephony.SmsCbLocation#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.hashCode()
-UnflaggedApi: android.telephony.SmsCbLocation#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.toString()
-UnflaggedApi: android.telephony.SmsCbMessage#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.SmsCbMessage.toString()
-UnflaggedApi: android.telephony.TelephonyHistogram#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.TelephonyHistogram.toString()
-UnflaggedApi: android.telephony.TelephonyManager.ModemActivityInfoException#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.TelephonyManager.ModemActivityInfoException.toString()
-UnflaggedApi: android.telephony.ThermalMitigationRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ThermalMitigationRequest.toString()
-UnflaggedApi: android.telephony.UiccAccessRule#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.equals(Object)
-UnflaggedApi: android.telephony.UiccAccessRule#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.hashCode()
-UnflaggedApi: android.telephony.UiccAccessRule#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.toString()
-UnflaggedApi: android.telephony.UiccSlotInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.hashCode()
-UnflaggedApi: android.telephony.UiccSlotInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.toString()
-UnflaggedApi: android.telephony.UiccSlotMapping#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotMapping#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.hashCode()
-UnflaggedApi: android.telephony.UiccSlotMapping#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.toString()
-UnflaggedApi: android.telephony.VopsSupportInfo#writeToParcel(android.os.Parcel, int):
- New API must be flagged with @FlaggedApi: method android.telephony.VopsSupportInfo.writeToParcel(android.os.Parcel,int)
-UnflaggedApi: android.telephony.cdma.CdmaSmsCbProgramData#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.cdma.CdmaSmsCbProgramData.toString()
-UnflaggedApi: android.telephony.data.DataCallResponse#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.equals(Object)
-UnflaggedApi: android.telephony.data.DataCallResponse#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.hashCode()
-UnflaggedApi: android.telephony.data.DataCallResponse#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.toString()
-UnflaggedApi: android.telephony.data.DataProfile#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.equals(Object)
-UnflaggedApi: android.telephony.data.DataProfile#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.hashCode()
-UnflaggedApi: android.telephony.data.DataProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.toString()
-UnflaggedApi: android.telephony.data.DataService#onDestroy():
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onDestroy()
-UnflaggedApi: android.telephony.data.DataService#onUnbind(android.content.Intent):
- New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.equals(Object)
-UnflaggedApi: android.telephony.data.ThrottleStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.toString()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.equals(Object)
-UnflaggedApi: android.telephony.euicc.EuiccNotification#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.hashCode()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.toString()
-UnflaggedApi: android.telephony.euicc.EuiccRulesAuthTable#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccRulesAuthTable.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.hashCode()
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.toString()
-UnflaggedApi: android.telephony.ims.AudioCodecAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.AudioCodecAttributes.toString()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.toString()
-UnflaggedApi: android.telephony.ims.DelegateRequest#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRequest#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.toString()
-UnflaggedApi: android.telephony.ims.FeatureTagState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.equals(Object)
-UnflaggedApi: android.telephony.ims.FeatureTagState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.hashCode()
-UnflaggedApi: android.telephony.ims.FeatureTagState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.toString()
-UnflaggedApi: android.telephony.ims.ImsCallForwardInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallForwardInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsCallProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsConferenceState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsConferenceState.toString()
-UnflaggedApi: android.telephony.ims.ImsExternalCallState#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsExternalCallState.toString()
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onTechnologyChangeFailed(int,android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onUnregistered(android.telephony.ims.ImsReasonInfo):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onUnregistered(android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsSsData#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsData.toString()
-UnflaggedApi: android.telephony.ims.ImsSsInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsStreamMediaProfile#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsStreamMediaProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsSuppServiceNotification#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSuppServiceNotification.toString()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.hashCode()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.toString()
-UnflaggedApi: android.telephony.ims.MediaThreshold#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaThreshold#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.hashCode()
-UnflaggedApi: android.telephony.ims.MediaThreshold#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.toString()
-UnflaggedApi: android.telephony.ims.PublishAttributes#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.equals(Object)
-UnflaggedApi: android.telephony.ims.PublishAttributes#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.hashCode()
-UnflaggedApi: android.telephony.ims.PublishAttributes#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.toString()
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.toString()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.toString()
-UnflaggedApi: android.telephony.ims.RcsContactUceCapability#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactUceCapability.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDialogState#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDialogState#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.equals(Object)
-UnflaggedApi: android.telephony.ims.SipMessage#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.toString()
-UnflaggedApi: android.telephony.ims.SrvccCall#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.equals(Object)
-UnflaggedApi: android.telephony.ims.SrvccCall#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.hashCode()
-UnflaggedApi: android.telephony.ims.SrvccCall#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair.toString()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.equals(Object)
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.hashCode()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.toString()
-UnflaggedApi: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- New API must be flagged with @FlaggedApi: method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
UnflaggedApi: android.telephony.mbms.vendor.MbmsDownloadServiceBase#DESCRIPTOR:
New API must be flagged with @FlaggedApi: field android.telephony.mbms.vendor.MbmsDownloadServiceBase.DESCRIPTOR
UnflaggedApi: android.telephony.mbms.vendor.MbmsStreamingServiceBase#DESCRIPTOR:
@@ -2901,18 +363,12 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaDirection.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.AntennaDirection#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.equals(Object)
UnflaggedApi: android.telephony.satellite.AntennaDirection#getX():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getX()
UnflaggedApi: android.telephony.satellite.AntennaDirection#getY():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getY()
UnflaggedApi: android.telephony.satellite.AntennaDirection#getZ():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getZ()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.toString()
UnflaggedApi: android.telephony.satellite.AntennaDirection#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.AntennaPosition:
@@ -2925,16 +381,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaPosition.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.AntennaPosition#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.equals(Object)
UnflaggedApi: android.telephony.satellite.AntennaPosition#getAntennaDirection():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getAntennaDirection()
UnflaggedApi: android.telephony.satellite.AntennaPosition#getSuggestedHoldPosition():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getSuggestedHoldPosition()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.toString()
UnflaggedApi: android.telephony.satellite.AntennaPosition#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.PointingInfo:
@@ -2947,16 +397,10 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.PointingInfo.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.PointingInfo#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.describeContents()
-UnflaggedApi: android.telephony.satellite.PointingInfo#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.equals(Object)
UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteAzimuthDegrees():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteAzimuthDegrees()
UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteElevationDegrees():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteElevationDegrees()
-UnflaggedApi: android.telephony.satellite.PointingInfo#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.hashCode()
-UnflaggedApi: android.telephony.satellite.PointingInfo#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.toString()
UnflaggedApi: android.telephony.satellite.PointingInfo#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities:
@@ -2969,20 +413,14 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteCapabilities.PARCELABLE_WRITE_RETURN_VALUE
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#describeContents():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.describeContents()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#equals(Object):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.equals(Object)
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getAntennaPositionMap():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getAntennaPositionMap()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getMaxBytesPerOutgoingDatagram():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getMaxBytesPerOutgoingDatagram()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getSupportedRadioTechnologies():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getSupportedRadioTechnologies()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#hashCode():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.hashCode()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#isPointingRequired():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.isPointingRequired()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#toString():
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.toString()
UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#writeToParcel(android.os.Parcel, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.telephony.satellite.SatelliteDatagram:
@@ -3037,8 +475,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY
UnflaggedApi: android.telephony.satellite.SatelliteManager#NT_RADIO_TECHNOLOGY_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ACCESS_BARRED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ACCESS_BARRED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED:
@@ -3057,20 +493,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR_NONE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_ARGUMENTS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_ARGUMENTS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_MODEM_STATE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_MODEM_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_TELEPHONY_STATE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_BUSY:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_BUSY
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_ERROR
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
@@ -3085,28 +507,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_UNKNOWN:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_TIMEOUT:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_TIMEOUT
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_AUTHORIZED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_AUTHORIZED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_REACHABLE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_REACHABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_SUPPORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_SUPPORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NO_RESOURCES:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NO_RESOURCES
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RADIO_NOT_AVAILABLE:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_ABORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_ABORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_FAILED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_FAILED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_IN_PROGRESS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_NOT_SUPPORTED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ACCESS_BARRED:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ERROR:
@@ -3153,14 +553,6 @@
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_SUCCESS:
New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVER_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVER_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_ERROR:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_NOT_PROVISIONED:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_PROVISION_IN_PROGRESS:
- New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS
UnflaggedApi: android.telephony.satellite.SatelliteManager#deprovisionSatelliteService(String, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.deprovisionSatelliteService(String,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
UnflaggedApi: android.telephony.satellite.SatelliteManager#pollPendingSatelliteDatagrams(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
@@ -3225,141 +617,3 @@
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSatellitePositionChanged(android.telephony.satellite.PointingInfo)
UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onSendDatagramStateChanged(int, int, int):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSendDatagramStateChanged(int,int,int)
-UnflaggedApi: android.text.FontConfig#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.equals(Object)
-UnflaggedApi: android.text.FontConfig#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.hashCode()
-UnflaggedApi: android.text.FontConfig#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.toString()
-UnflaggedApi: android.text.FontConfig.Alias#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.equals(Object)
-UnflaggedApi: android.text.FontConfig.Alias#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.hashCode()
-UnflaggedApi: android.text.FontConfig.Alias#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.toString()
-UnflaggedApi: android.text.FontConfig.Font#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.equals(Object)
-UnflaggedApi: android.text.FontConfig.Font#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.hashCode()
-UnflaggedApi: android.text.FontConfig.Font#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.toString()
-UnflaggedApi: android.text.FontConfig.FontFamily#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.equals(Object)
-UnflaggedApi: android.text.FontConfig.FontFamily#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.hashCode()
-UnflaggedApi: android.text.FontConfig.FontFamily#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.toString()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#equals(Object):
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.equals(Object)
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#hashCode():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.hashCode()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#toString():
- New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.toString()
-UnflaggedApi: android.view.contentcapture.ContentCaptureEvent#toString():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ContentCaptureEvent.toString()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillHints():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillHints()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillId():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillOptions():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillOptions()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillValue():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillValue()
-UnflaggedApi: android.view.contentcapture.ViewNode#getClassName():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getClassName()
-UnflaggedApi: android.view.contentcapture.ViewNode#getContentDescription():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getContentDescription()
-UnflaggedApi: android.view.contentcapture.ViewNode#getExtras():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getExtras()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHeight():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHeight()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHint():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHint()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHintIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHintIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getId():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdPackage():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdPackage()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getInputType():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getInputType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLeft():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLeft()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLocaleList():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLocaleList()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextEms():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextLength():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextLength()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMinTextEms():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMinTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getReceiveContentMimeTypes():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getReceiveContentMimeTypes()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollX():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollX()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollY():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollY()
-UnflaggedApi: android.view.contentcapture.ViewNode#getText():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getText()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextBackgroundColor():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextBackgroundColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextColor():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextIdEntry():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineBaselines():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineBaselines()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineCharOffsets():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineCharOffsets()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionEnd():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionEnd()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionStart():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionStart()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSize():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSize()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextStyle():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextStyle()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTop():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTop()
-UnflaggedApi: android.view.contentcapture.ViewNode#getVisibility():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getVisibility()
-UnflaggedApi: android.view.contentcapture.ViewNode#getWidth():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getWidth()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAccessibilityFocused():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAccessibilityFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isActivated():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isActivated()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAssistBlocked():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAssistBlocked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isCheckable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isCheckable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isChecked():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isChecked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isContextClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isContextClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isEnabled():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isEnabled()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocusable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocusable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocused():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isLongClickable():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isLongClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isOpaque():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isOpaque()
-UnflaggedApi: android.view.contentcapture.ViewNode#isSelected():
- New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isSelected()
-UnflaggedApi: android.view.translation.UiTranslationSpec#equals(Object):
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.equals(Object)
-UnflaggedApi: android.view.translation.UiTranslationSpec#hashCode():
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.hashCode()
-UnflaggedApi: android.view.translation.UiTranslationSpec#toString():
- New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.toString()
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a8a6eb9..40c6fa8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -50,6 +50,7 @@
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
field public static final String SET_GAME_SERVICE = "android.permission.SET_GAME_SERVICE";
field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
+ field public static final String START_ACTIVITIES_FROM_SDK_SANDBOX = "android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_INPUT_METHOD = "android.permission.TEST_INPUT_METHOD";
@@ -3505,10 +3506,6 @@
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
}
- public static final class MotionEvent.PointerCoords {
- method public boolean isResampled();
- }
-
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod {
method public abstract String asyncImpl() default "";
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 1aaedab..107be8b 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,985 +1,341 @@
// Baseline format: 1.0
-AcronymName: android.app.NotificationChannel#isImportanceLockedByOEM():
- Acronyms should not be capitalized in method names: was `isImportanceLockedByOEM`, should this be `isImportanceLockedByOem`?
-AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean):
- Acronyms should not be capitalized in method names: was `setImportanceLockedByOEM`, should this be `setImportanceLockedByOem`?
+KotlinKeyword: android.app.Notification#when:
+ Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
- Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
- Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.view.Display#getSupportedWideColorGamut():
- Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]`
-ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
- Method parameter should be Collection<View> (or subclass) instead of raw array; was `android.view.View[]`
-ArrayReturn: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
- Method parameter should be Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
-
-
-AutoBoxing: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
- Must avoid boxed primitives (`java.lang.Long`)
-
-
-BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
-BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
-
-
-ConcreteCollection: android.content.AutofillOptions#disabledActivities:
- Field type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
- Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#ContentCaptureOptions(int, int, int, int, int, android.util.ArraySet<android.content.ComponentName>) parameter #5:
- Parameter type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#whitelistedComponents:
- Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Field type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>) parameter #2:
- Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms():
- Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-
-
-ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
- Context is distinct, so it must be the first argument (method `get`)
-
-
-EndsWithImpl: android.view.contentcapture.ViewNode.ViewStructureImpl:
- Don't expose your implementation details: `ViewStructureImpl` ends with `Impl`
-
-
-Enum: android.view.inspector.InspectableProperty.ValueType:
- Enums are discouraged in Android APIs
-
-
-EqualsAndHashCode: android.os.StrictMode.ViolationInfo#hashCode():
- Must override both equals and hashCode; missing one in android.os.StrictMode.ViolationInfo
-
-
-ExecutorRegistration: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener):
- Registration methods should have overload that accepts delivery Executor: `setParameterListener`
-ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
-ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `getAppPermissions`
-ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- Registration methods should have overload that accepts delivery Executor: `setCallback`
-ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
- Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
-
-
-ForbiddenSuperClass: android.app.AppDetailsActivity:
- AppDetailsActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
-
-
-GenericException: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- Methods must not throw generic exceptions (`java.lang.Exception`)
-
-
-GetterSetterNames: android.net.NetworkPolicyManager#getRestrictBackground():
- Symmetric method for `setRestrictBackground` must be named `isRestrictBackground`; was `getRestrictBackground`
-
-
-IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
-
-
-IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
- Intent action constant name must be ACTION_FOO: VOICE_INTERACTION_SERVICE
-IntentName: android.provider.Telephony.Sms.Intents#SMS_CARRIER_PROVISION_ACTION:
- Intent action constant name must be ACTION_FOO: SMS_CARRIER_PROVISION_ACTION
-
-
-KotlinOperator: android.os.PackageTagsList#contains(android.os.PackageTagsList):
- Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: android.util.SparseArrayMap#get(int, K):
- Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
-ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
- Listeners should always be at end of argument list (method `countPermissionApps`)
-ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
- Listeners should always be at end of argument list (method `getAppPermissions`)
-
-
-ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context):
- Managers must always be obtained from Context; no direct constructors
-
-
-MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID:
- If min/max could change in future, make them dynamic methods: android.os.UserHandle#MIN_SECONDARY_USER_ID
-MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS:
- If min/max could change in future, make them dynamic methods: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS
-
-
-MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
- android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
-MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
-MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
- android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
- android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
- android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
-MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int):
- android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int)
-
-
-MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
- Missing nullability on parameter `config` in method `onMovedToDisplay`
-MissingNullability: android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName) parameter #0:
- Missing nullability on parameter `activity` in method `alwaysShowUnsupportedCompileSdkWarning`
-MissingNullability: android.app.ActivityManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.app.ActivityManager#scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int) parameter #0:
- Missing nullability on parameter `packages` in method `scheduleApplicationInfoChanged`
-MissingNullability: android.app.ActivityManager.TaskDescription#getIconFilename():
- Missing nullability on method `getIconFilename` return
-MissingNullability: android.app.ActivityTaskManager#clearLaunchParamsForPackages(java.util.List<java.lang.String>) parameter #0:
- Missing nullability on parameter `packageNames` in method `clearLaunchParamsForPackages`
-MissingNullability: android.app.ActivityTaskManager#resizeTask(int, android.graphics.Rect) parameter #1:
- Missing nullability on parameter `bounds` in method `resizeTask`
-MissingNullability: android.app.ActivityTaskManager#supportsMultiWindow(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `supportsMultiWindow`
-MissingNullability: android.app.ActivityTaskManager#supportsSplitScreenMultiWindow(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `supportsSplitScreenMultiWindow`
MissingNullability: android.app.AppDetailsActivity#onCreate(android.os.Bundle) parameter #0:
Missing nullability on parameter `savedInstanceState` in method `onCreate`
-MissingNullability: android.app.AppOpsManager#isOperationActive(int, int, String) parameter #2:
- Missing nullability on parameter `packageName` in method `isOperationActive`
-MissingNullability: android.app.AppOpsManager#opToPermission(int):
- Missing nullability on method `opToPermission` return
-MissingNullability: android.app.AppOpsManager#permissionToOpCode(String) parameter #0:
- Missing nullability on parameter `permission` in method `permissionToOpCode`
-MissingNullability: android.app.AppOpsManager#setMode(int, int, String, int) parameter #2:
- Missing nullability on parameter `packageName` in method `setMode`
-MissingNullability: android.app.NotificationManager#allowAssistantAdjustment(String) parameter #0:
- Missing nullability on parameter `capability` in method `allowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#disallowAssistantAdjustment(String) parameter #0:
- Missing nullability on parameter `capability` in method `disallowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#getEffectsSuppressor():
- Missing nullability on method `getEffectsSuppressor` return
-MissingNullability: android.app.TimePickerDialog#getTimePicker():
- Missing nullability on method `getTimePicker` return
-MissingNullability: android.app.WindowConfiguration#compareTo(android.app.WindowConfiguration) parameter #0:
- Missing nullability on parameter `that` in method `compareTo`
-MissingNullability: android.app.WindowConfiguration#getAppBounds():
- Missing nullability on method `getAppBounds` return
-MissingNullability: android.app.WindowConfiguration#getBounds():
- Missing nullability on method `getBounds` return
-MissingNullability: android.app.WindowConfiguration#setAppBounds(android.graphics.Rect) parameter #0:
- Missing nullability on parameter `rect` in method `setAppBounds`
-MissingNullability: android.app.WindowConfiguration#setBounds(android.graphics.Rect) parameter #0:
- Missing nullability on parameter `rect` in method `setBounds`
-MissingNullability: android.app.WindowConfiguration#setTo(android.app.WindowConfiguration) parameter #0:
- Missing nullability on parameter `other` in method `setTo`
-MissingNullability: android.app.WindowConfiguration#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
- Missing nullability on method `getOwnerInstalledCaCerts` return
-MissingNullability: android.app.admin.SecurityLog.SecurityEvent#SecurityEvent(long, byte[]) parameter #1:
- Missing nullability on parameter `data` in method `SecurityEvent`
-MissingNullability: android.app.prediction.AppPredictor#getSessionId():
- Missing nullability on method `getSessionId` return
-MissingNullability: android.content.AutofillOptions#forWhitelistingItself():
- Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.AutofillOptions#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentCaptureOptions#forWhitelistingItself():
- Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.ContentCaptureOptions#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int):
- Missing nullability on method `getSyncAdapterPackagesForAuthorityAsUser` return
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int) parameter #0:
- Missing nullability on parameter `authority` in method `getSyncAdapterPackagesForAuthorityAsUser`
-MissingNullability: android.content.pm.ActivityInfo#isTranslucentOrFloating(android.content.res.TypedArray) parameter #0:
- Missing nullability on parameter `attributes` in method `isTranslucentOrFloating`
-MissingNullability: android.content.pm.LauncherApps#LauncherApps(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `LauncherApps`
-MissingNullability: android.content.pm.PackageManager#getHoldLockToken():
- Missing nullability on method `getHoldLockToken` return
-MissingNullability: android.content.pm.PackageManager#getNamesForUids(int[]) parameter #0:
- Missing nullability on parameter `uids` in method `getNamesForUids`
-MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `ShortcutManager`
-MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0:
- Missing nullability on parameter `orig` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2:
- Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2:
- Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4:
- Missing nullability on parameter `userType` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1:
- Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#getUserHandle():
- Missing nullability on method `getUserHandle` return
-MissingNullability: android.content.pm.UserInfo#iconPath:
- Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint:
- Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#name:
- Missing nullability on field `name` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#userType:
- Missing nullability on field `userType` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
- Missing nullability on parameter `packageName` in method `getOverlayablesToString`
-MissingNullability: android.content.res.Configuration#windowConfiguration:
- Missing nullability on field `windowConfiguration` in class `class android.content.res.Configuration`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #0:
- Missing nullability on parameter `printer` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #1:
- Missing nullability on parameter `args` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#getDatabaseInfo():
- Missing nullability on method `getDatabaseInfo` return
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#DbStats(String, long, long, int, int, int, int) parameter #0:
- Missing nullability on parameter `dbName` in method `DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#cache:
- Missing nullability on field `cache` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#dbName:
- Missing nullability on field `dbName` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Missing nullability on field `dbStats` in class `class android.database.sqlite.SQLiteDebug.PagerStats`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #0:
- Missing nullability on parameter `db` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #1:
- Missing nullability on parameter `sql` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #2:
- Missing nullability on parameter `editTable` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #3:
- Missing nullability on parameter `cancellationSignal` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#cursorRequeried(android.database.Cursor) parameter #0:
- Missing nullability on parameter `cursor` in method `cursorRequeried`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
- Missing nullability on method `query` return
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #0:
- Missing nullability on parameter `factory` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #1:
- Missing nullability on parameter `selectionArgs` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#setBindArguments(String[]) parameter #0:
- Missing nullability on parameter `bindArgs` in method `setBindArguments`
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultJournalMode():
- Missing nullability on method `getDefaultJournalMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultSyncMode():
- Missing nullability on method `getDefaultSyncMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getWALSyncMode():
- Missing nullability on method `getWALSyncMode` return
-MissingNullability: android.graphics.ImageDecoder#createSource(android.content.res.Resources, java.io.InputStream, int) parameter #0:
- Missing nullability on parameter `res` in method `createSource`
-MissingNullability: android.graphics.drawable.AdaptiveIconDrawable#getSafeZone():
- Missing nullability on method `getSafeZone` return
-MissingNullability: android.graphics.drawable.ColorDrawable#getXfermode():
- Missing nullability on method `getXfermode` return
-MissingNullability: android.hardware.camera2.CameraManager#getCameraIdListNoLazy():
- Missing nullability on method `getCameraIdListNoLazy` return
-MissingNullability: android.hardware.display.AmbientDisplayConfiguration#AmbientDisplayConfiguration(android.content.Context) parameter #0:
- Missing nullability on parameter `context` in method `AmbientDisplayConfiguration`
-MissingNullability: android.media.AudioAttributes#getSdkUsages():
- Missing nullability on method `getSdkUsages` return
-MissingNullability: android.media.AudioManager#getPublicStreamTypes():
- Missing nullability on method `getPublicStreamTypes` return
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
- Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
- Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #6:
- Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
- Missing nullability on parameter `clientEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
- Missing nullability on parameter `deviceEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #3:
- Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #4:
- Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
- Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
- Missing nullability on method `setAudioStretchMode` return
-MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
- Missing nullability on field `EFFECT_TYPE_NULL` in class `class android.media.audiofx.AudioEffect`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToInt(byte[]) parameter #0:
- Missing nullability on parameter `valueBuf` in method `byteArrayToInt`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]) parameter #0:
- Missing nullability on parameter `valueBuf` in method `byteArrayToShort`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, byte[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, int[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, short[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #0:
- Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #1:
- Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#intToByteArray(int):
- Missing nullability on method `intToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect#isEffectTypeAvailable(java.util.UUID) parameter #0:
- Missing nullability on parameter `type` in method `isEffectTypeAvailable`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int, byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #0:
- Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #1:
- Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener) parameter #0:
- Missing nullability on parameter `listener` in method `setParameterListener`
-MissingNullability: android.media.audiofx.AudioEffect#shortToByteArray(short):
- Missing nullability on method `shortToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#Descriptor(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `Descriptor`
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#writeToParcel(android.os.Parcel) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #0:
- Missing nullability on parameter `effect` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #2:
- Missing nullability on parameter `param` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #3:
- Missing nullability on parameter `value` in method `onParameterChange`
-MissingNullability: android.os.Build#is64BitAbi(String) parameter #0:
- Missing nullability on parameter `abi` in method `is64BitAbi`
-MissingNullability: android.os.Build.VERSION#ACTIVE_CODENAMES:
- Missing nullability on field `ACTIVE_CODENAMES` in class `class android.os.Build.VERSION`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...):
- Missing nullability on method `buildPath` return
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #0:
- Missing nullability on parameter `base` in method `buildPath`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #1:
- Missing nullability on parameter `segments` in method `buildPath`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #0:
- Missing nullability on parameter `dir` in method `contains`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #1:
- Missing nullability on parameter `file` in method `contains`
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor):
- Missing nullability on method `getFile` return
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `getFile`
-MissingNullability: android.os.StrictMode#setViolationLogger(android.os.StrictMode.ViolationLogger) parameter #0:
- Missing nullability on parameter `listener` in method `setViolationLogger`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel, boolean) parameter #0:
- Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
- Missing nullability on field `broadcastIntentAction` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #0:
- Missing nullability on parameter `pw` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #1:
- Missing nullability on parameter `prefix` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#getStackTrace():
- Missing nullability on method `getStackTrace` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationClass():
- Missing nullability on method `getViolationClass` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationDetails():
- Missing nullability on method `getViolationDetails` return
-MissingNullability: android.os.StrictMode.ViolationInfo#tags:
- Missing nullability on field `tags` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.os.StrictMode.ViolationLogger#log(android.os.StrictMode.ViolationInfo) parameter #0:
- Missing nullability on parameter `info` in method `log`
-MissingNullability: android.os.VibrationEffect#RINGTONES:
- Missing nullability on field `RINGTONES` in class `class android.os.VibrationEffect`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #0:
- Missing nullability on parameter `uri` in method `get`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
- Missing nullability on parameter `context` in method `get`
-MissingNullability: android.os.VibrationEffect#get(int):
- Missing nullability on method `get` return
-MissingNullability: android.os.VibrationEffect#get(int, boolean):
- Missing nullability on method `get` return
-MissingNullability: android.os.VintfObject#getHalNamesAndVersions():
- Missing nullability on method `getHalNamesAndVersions` return
-MissingNullability: android.os.VintfObject#getSepolicyVersion():
- Missing nullability on method `getSepolicyVersion` return
-MissingNullability: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
- Missing nullability on method `getTargetFrameworkCompatibilityMatrixVersion` return
-MissingNullability: android.os.VintfObject#getVndkSnapshots():
- Missing nullability on method `getVndkSnapshots` return
-MissingNullability: android.os.VintfObject#report():
- Missing nullability on method `report` return
-MissingNullability: android.os.VintfRuntimeInfo#getCpuInfo():
- Missing nullability on method `getCpuInfo` return
-MissingNullability: android.os.VintfRuntimeInfo#getHardwareId():
- Missing nullability on method `getHardwareId` return
-MissingNullability: android.os.VintfRuntimeInfo#getKernelVersion():
- Missing nullability on method `getKernelVersion` return
-MissingNullability: android.os.VintfRuntimeInfo#getNodeName():
- Missing nullability on method `getNodeName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsName():
- Missing nullability on method `getOsName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsRelease():
- Missing nullability on method `getOsRelease` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsVersion():
- Missing nullability on method `getOsVersion` return
-MissingNullability: android.os.WorkSource#add(int, String) parameter #1:
- Missing nullability on parameter `name` in method `add`
-MissingNullability: android.os.health.HealthKeys.Constants#Constants(Class) parameter #0:
- Missing nullability on parameter `clazz` in method `Constants`
-MissingNullability: android.os.health.HealthKeys.Constants#getDataType():
- Missing nullability on method `getDataType` return
-MissingNullability: android.os.health.HealthKeys.Constants#getKeys(int):
- Missing nullability on method `getKeys` return
-MissingNullability: android.os.health.HealthStats#HealthStats(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `HealthStats`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel) parameter #0:
- Missing nullability on parameter `in` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.health.HealthStatsWriter) parameter #0:
- Missing nullability on parameter `writer` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#getHealthStats():
- Missing nullability on method `getHealthStats` return
-MissingNullability: android.os.health.HealthStatsParceler#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
-MissingNullability: android.os.health.HealthStatsWriter#HealthStatsWriter(android.os.health.HealthKeys.Constants) parameter #0:
- Missing nullability on parameter `constants` in method `HealthStatsWriter`
-MissingNullability: android.os.health.HealthStatsWriter#addMeasurements(int, String, long) parameter #1:
- Missing nullability on parameter `name` in method `addMeasurements`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #1:
- Missing nullability on parameter `name` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #2:
- Missing nullability on parameter `value` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #1:
- Missing nullability on parameter `name` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #2:
- Missing nullability on parameter `value` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#flattenToParcel(android.os.Parcel) parameter #0:
- Missing nullability on parameter `out` in method `flattenToParcel`
-MissingNullability: android.os.storage.StorageVolume#getPath():
- Missing nullability on method `getPath` return
-MissingNullability: android.provider.CalendarContract.Calendars#SYNC_WRITABLE_COLUMNS:
- Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Calendars`
-MissingNullability: android.provider.CalendarContract.Events#SYNC_WRITABLE_COLUMNS:
- Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Events`
-MissingNullability: android.provider.ContactsContract.RawContactsEntity#CORP_CONTENT_URI:
- Missing nullability on field `CORP_CONTENT_URI` in class `class android.provider.ContactsContract.RawContactsEntity`
-MissingNullability: android.security.keystore.KeyProtection.Builder#setBoundToSpecificSecureUserId(long):
- Missing nullability on method `setBoundToSpecificSecureUserId` return
-MissingNullability: android.service.autofill.CompositeUserData#getCategoryIds():
- Missing nullability on method `getCategoryIds` return
-MissingNullability: android.service.autofill.CompositeUserData#getDefaultFieldClassificationArgs():
- Missing nullability on method `getDefaultFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
- Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
- Missing nullability on method `getFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getValues():
- Missing nullability on method `getValues` return
-MissingNullability: android.service.autofill.CompositeUserData#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.service.autofill.UserData#getFieldClassificationAlgorithms():
- Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.telecom.Call.Details#getTelecomCallId():
- Missing nullability on method `getTelecomCallId` return
-MissingNullability: android.telephony.ServiceState#addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo) parameter #0:
- Missing nullability on parameter `nri` in method `addNetworkRegistrationInfo`
-MissingNullability: android.telephony.ServiceState#setCellBandwidths(int[]) parameter #0:
- Missing nullability on parameter `bandwidths` in method `setCellBandwidths`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #0:
- Missing nullability on parameter `destAddress` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #1:
- Missing nullability on parameter `countryIso` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNKNOWN:
- Missing nullability on field `HAL_VERSION_UNKNOWN` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNSUPPORTED:
- Missing nullability on field `HAL_VERSION_UNSUPPORTED` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
- Missing nullability on method `getLine1AlphaTag` return
-MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
- Missing nullability on method `getRadioHalVersion` return
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #0:
- Missing nullability on parameter `mccmnc` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #1:
- Missing nullability on parameter `imsi` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #2:
- Missing nullability on parameter `iccid` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #3:
- Missing nullability on parameter `gid1` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #4:
- Missing nullability on parameter `gid2` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #5:
- Missing nullability on parameter `plmn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #6:
- Missing nullability on parameter `spn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #7:
- Missing nullability on parameter `carrierPriviledgeRules` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8:
- Missing nullability on parameter `apn` in method `setCarrierTestOverride`
-MissingNullability: android.text.Selection.MemoryTextWatcher#afterTextChanged(android.text.Editable) parameter #0:
- Missing nullability on parameter `s` in method `afterTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#beforeTextChanged(CharSequence, int, int, int) parameter #0:
- Missing nullability on parameter `s` in method `beforeTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#onTextChanged(CharSequence, int, int, int) parameter #0:
- Missing nullability on parameter `s` in method `onTextChanged`
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene):
- Missing nullability on method `getTransition` return
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene) parameter #0:
- Missing nullability on parameter `scene` in method `getTransition`
-MissingNullability: android.util.FeatureFlagUtils#getAllFeatureFlags():
- Missing nullability on method `getAllFeatureFlags` return
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #0:
- Missing nullability on parameter `context` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #1:
- Missing nullability on parameter `feature` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #0:
- Missing nullability on parameter `context` in method `setEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #1:
- Missing nullability on parameter `feature` in method `setEnabled`
-MissingNullability: android.util.TimeUtils#formatDuration(long):
- Missing nullability on method `formatDuration` return
-MissingNullability: android.util.proto.EncodedBuffer#dumpBuffers(String) parameter #0:
- Missing nullability on parameter `tag` in method `dumpBuffers`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #0:
- Missing nullability on parameter `tag` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #1:
- Missing nullability on parameter `prefix` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #2:
- Missing nullability on parameter `buf` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#getBytes(int):
- Missing nullability on method `getBytes` return
-MissingNullability: android.util.proto.EncodedBuffer#getDebugString():
- Missing nullability on method `getDebugString` return
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[]) parameter #0:
- Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[], int, int) parameter #0:
- Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.ProtoParseException#ProtoParseException(String) parameter #0:
- Missing nullability on parameter `msg` in method `ProtoParseException`
-MissingNullability: android.util.proto.WireTypeMismatchException#WireTypeMismatchException(String) parameter #0:
- Missing nullability on parameter `msg` in method `WireTypeMismatchException`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #1:
- Missing nullability on parameter `action` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #2:
- Missing nullability on parameter `token` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #1:
- Missing nullability on parameter `action` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #2:
- Missing nullability on parameter `token` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #1:
- Missing nullability on parameter `action` in method `removeCallbacks`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #2:
- Missing nullability on parameter `token` in method `removeCallbacks`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
- Missing nullability on parameter `views` in method `sort`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #3:
- Missing nullability on parameter `root` in method `sort`
-MissingNullability: android.view.KeyEvent#actionToString(int):
- Missing nullability on method `actionToString` return
-MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
- Missing nullability on parameter `attrs` in method `relayout`
-MissingNullability: android.view.View#getTooltipView():
- Missing nullability on method `getTooltipView` return
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
- Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
- Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #0:
- Missing nullability on parameter `tree` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #1:
- Missing nullability on parameter `executor` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #2:
- Missing nullability on parameter `callback` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.WindowManager#holdLock(android.os.IBinder, int) parameter #0:
- Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
- Missing nullability on field `accessibilityTitle` in class `class android.view.WindowManager.LayoutParams`
-MissingNullability: android.view.accessibility.AccessibilityNodeInfo#writeToParcelNoRecycle(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `parcel` in method `writeToParcelNoRecycle`
-MissingNullability: android.view.accessibility.AccessibilityWindowInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0:
- Missing nullability on parameter `counter` in method `setNumInstancesInUseCounter`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#asyncNewChild(int):
- Missing nullability on method `asyncNewChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getAutofillId():
- Missing nullability on method `getAutofillId` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getExtras():
- Missing nullability on method `getExtras` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getHint():
- Missing nullability on method `getHint` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getNode():
- Missing nullability on method `getNode` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getTempRect():
- Missing nullability on method `getTempRect` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getText():
- Missing nullability on method `getText` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newChild(int):
- Missing nullability on method `newChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String):
- Missing nullability on method `newHtmlInfoBuilder` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String) parameter #0:
- Missing nullability on parameter `tagName` in method `newHtmlInfoBuilder`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillHints(String[]) parameter #0:
- Missing nullability on parameter `hints` in method `setAutofillHints`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId) parameter #0:
- Missing nullability on parameter `id` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId, int) parameter #0:
- Missing nullability on parameter `parentId` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
- Missing nullability on parameter `options` in method `setAutofillOptions`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillValue(android.view.autofill.AutofillValue) parameter #0:
- Missing nullability on parameter `value` in method `setAutofillValue`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setClassName(String) parameter #0:
- Missing nullability on parameter `className` in method `setClassName`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setContentDescription(CharSequence) parameter #0:
- Missing nullability on parameter `contentDescription` in method `setContentDescription`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHint(CharSequence) parameter #0:
- Missing nullability on parameter `hint` in method `setHint`
MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHintIdEntry(String) parameter #0:
Missing nullability on parameter `entryName` in method `setHintIdEntry`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHtmlInfo(android.view.ViewStructure.HtmlInfo) parameter #0:
- Missing nullability on parameter `htmlInfo` in method `setHtmlInfo`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #1:
- Missing nullability on parameter `packageName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #2:
- Missing nullability on parameter `typeName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #3:
- Missing nullability on parameter `entryName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setLocaleList(android.os.LocaleList) parameter #0:
- Missing nullability on parameter `localeList` in method `setLocaleList`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence) parameter #0:
- Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence, int, int) parameter #0:
- Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #0:
- Missing nullability on parameter `charOffsets` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #1:
- Missing nullability on parameter `baselines` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTransformation(android.graphics.Matrix) parameter #0:
- Missing nullability on parameter `matrix` in method `setTransformation`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setWebDomain(String) parameter #0:
- Missing nullability on parameter `domain` in method `setWebDomain`
-MissingNullability: android.widget.CalendarView#getBoundsForDate(long, android.graphics.Rect) parameter #1:
- Missing nullability on parameter `outBounds` in method `getBoundsForDate`
MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.widget.Magnifier#getMagnifierDefaultSize():
- Missing nullability on method `getMagnifierDefaultSize` return
-MissingNullability: android.widget.Magnifier#setOnOperationCompleteCallback(android.widget.Magnifier.Callback) parameter #0:
- Missing nullability on parameter `callback` in method `setOnOperationCompleteCallback`
-MissingNullability: android.widget.NumberPicker#getDisplayedValueForCurrentSelection():
- Missing nullability on method `getDisplayedValueForCurrentSelection` return
-MissingNullability: android.widget.PopupMenu#getMenuListView():
- Missing nullability on method `getMenuListView` return
-MissingNullability: android.widget.TimePicker#getAmView():
- Missing nullability on method `getAmView` return
-MissingNullability: android.widget.TimePicker#getHourView():
- Missing nullability on method `getHourView` return
-MissingNullability: android.widget.TimePicker#getMinuteView():
- Missing nullability on method `getMinuteView` return
-MissingNullability: android.widget.TimePicker#getPmView():
- Missing nullability on method `getPmView` return
-
-
-MutableBareField: android.content.AutofillOptions#appDisabledExpiration:
- Bare field appDisabledExpiration must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#augmentedAutofillEnabled:
- Bare field augmentedAutofillEnabled must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#disabledActivities:
- Bare field disabledActivities must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
- Bare field whitelistedActivitiesForAugmentedAutofill must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated:
- Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#creationTime:
- Bare field creationTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#flags:
- Bare field flags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#guestToRemove:
- Bare field guestToRemove must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#iconPath:
- Bare field iconPath must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#id:
- Bare field id must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint:
- Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInTime:
- Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#name:
- Bare field name must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#partial:
- Bare field partial must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#preCreated:
- Bare field preCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileBadge:
- Bare field profileBadge must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileGroupId:
- Bare field profileGroupId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId:
- Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#serialNumber:
- Bare field serialNumber must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#userType:
- Bare field userType must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache:
- Bare field cache must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName:
- Bare field dbName must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbSize:
- Bare field dbSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#lookaside:
- Bare field lookaside must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#pageSize:
- Bare field pageSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
- Bare field dbStats must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#largestMemAlloc:
- Bare field largestMemAlloc must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#memoryUsed:
- Bare field memoryUsed must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#pageCacheOverflow:
- Bare field pageCacheOverflow must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
- Bare field broadcastIntentAction must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#durationMillis:
- Bare field durationMillis must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numAnimationsRunning:
- Bare field numAnimationsRunning must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numInstances:
- Bare field numInstances must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#tags:
- Bare field tags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationNumThisLoop:
- Bare field violationNumThisLoop must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationUptimeMillis:
- Bare field violationUptimeMillis must be marked final, or moved behind accessors if mutable
-
-
-NoByteOrShort: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]):
- Should avoid odd sized primitives; use `int` instead of `short` in method android.media.audiofx.AudioEffect.byteArrayToShort(byte[])
-NoByteOrShort: android.media.audiofx.AudioEffect#setParameter(int, short) parameter #1:
- Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.setParameter(int param, short value)
-NoByteOrShort: android.media.audiofx.AudioEffect#shortToByteArray(short) parameter #0:
- Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.shortToByteArray(short value)
-NoByteOrShort: android.util.proto.EncodedBuffer#readRawByte():
- Should avoid odd sized primitives; use `int` instead of `byte` in method android.util.proto.EncodedBuffer.readRawByte()
-NoByteOrShort: android.util.proto.EncodedBuffer#writeRawByte(byte) parameter #0:
- Should avoid odd sized primitives; use `int` instead of `byte` in parameter val in android.util.proto.EncodedBuffer.writeRawByte(byte val)
-
-
-NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS:
- New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
- New setting keys are not allowed (Field: AUTOMATIC_POWER_SAVE_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS:
- New setting keys are not allowed (Field: BATTERY_SAVER_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD:
- New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED:
- New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_CONVERSION_MODE:
- New setting keys are not allowed (Field: HDR_CONVERSION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_FORCE_CONVERSION_TYPE:
- New setting keys are not allowed (Field: HDR_FORCE_CONVERSION_TYPE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
- New setting keys are not allowed (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY:
- New setting keys are not allowed (Field: HIDDEN_API_POLICY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
- New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE:
- New setting keys are not allowed (Field: LOW_POWER_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY:
- New setting keys are not allowed (Field: LOW_POWER_MODE_STICKY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES:
- New setting keys are not allowed (Field: OVERLAY_DISPLAY_DEVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
- New setting keys are not allowed (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
- New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_CAPABILITY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
- New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
- New setting keys are not allowed (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
- New setting keys are not allowed (Field: AUTOFILL_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#BIOMETRIC_VIRTUAL_ENABLED:
- New setting keys are not allowed (Field: BIOMETRIC_VIRTUAL_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
- New setting keys are not allowed (Field: CONTENT_CAPTURE_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
- New setting keys are not allowed (Field: DISABLED_PRINT_SERVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
- New setting keys are not allowed (Field: ENABLED_VR_LISTENERS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS:
- New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
- New setting keys are not allowed (Field: NOTIFICATION_BADGING); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT:
- New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
- New setting keys are not allowed (Field: SYNC_PARENT_SOUNDS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
- New setting keys are not allowed (Field: VOICE_INTERACTION_SERVICE); use getters/setters in relevant manager class
-
-
-OnNameExpected: android.service.notification.ConditionProviderService#isBound():
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-
-
-PackageLayering: android.util.FeatureFlagUtils:
- Method parameter type `android.content.Context` violates package layering: nothing in `package android.util` should depend on `package android.content`
-
-
-ParcelConstructor: android.credentials.ui.ProviderData#ProviderData(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.credentials.ui.ProviderData
-ParcelConstructor: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.StrictMode.ViolationInfo
-ParcelConstructor: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel):
- Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.health.HealthStatsParceler
-
-
-ParcelCreator: android.app.WindowConfiguration:
- Parcelable requires a `CREATOR` field; missing in android.app.WindowConfiguration
-ParcelCreator: android.service.autofill.InternalOnClickAction:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalOnClickAction
-ParcelCreator: android.service.autofill.InternalSanitizer:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalSanitizer
-ParcelCreator: android.service.autofill.InternalTransformation:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalTransformation
-ParcelCreator: android.service.autofill.InternalValidator:
- Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalValidator
-
-
-ParcelNotFinal: android.app.WindowConfiguration:
- Parcelable classes must be final: android.app.WindowConfiguration is not final
-ParcelNotFinal: android.content.pm.UserInfo:
- Parcelable classes must be final: android.content.pm.UserInfo is not final
-ParcelNotFinal: android.os.health.HealthStatsParceler:
- Parcelable classes must be final: android.os.health.HealthStatsParceler is not final
-ParcelNotFinal: android.service.autofill.InternalOnClickAction:
- Parcelable classes must be final: android.service.autofill.InternalOnClickAction is not final
-ParcelNotFinal: android.service.autofill.InternalSanitizer:
- Parcelable classes must be final: android.service.autofill.InternalSanitizer is not final
-ParcelNotFinal: android.service.autofill.InternalTransformation:
- Parcelable classes must be final: android.service.autofill.InternalTransformation is not final
-ParcelNotFinal: android.service.autofill.InternalValidator:
- Parcelable classes must be final: android.service.autofill.InternalValidator is not final
ProtectedMember: android.app.AppDetailsActivity#onCreate(android.os.Bundle):
Protected methods not allowed; must be public: method android.app.AppDetailsActivity.onCreate(android.os.Bundle)}
-ProtectedMember: android.view.View#resetResolvedDrawables():
- Protected methods not allowed; must be public: method android.view.View.resetResolvedDrawables()}
ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
Protected methods not allowed; must be public: method android.view.ViewGroup.resetResolvedDrawables()}
-RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches():
- Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+SamShouldBeLast: android.animation.ValueAnimator#ofObject(android.animation.TypeEvaluator, java.lang.Object...):
+ SAM-compatible parameters (such as parameter 1, "evaluator", in android.animation.ValueAnimator.ofObject) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.ActivityManager.addOnUidImportanceListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.setExact) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.setWindow) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String, android.os.Bundle):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(int, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 2, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.UiAutomation#executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long):
+ SAM-compatible parameters (such as parameter 2, "filter", in android.app.UiAutomation.executeAndWaitForEvent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ActivityInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ActivityInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.database.sqlite.SQLiteCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
+ SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.AdaptiveIconDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.AdaptiveIconDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable#scheduleSelf(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "what", in android.graphics.drawable.Drawable.scheduleSelf) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable.Callback#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.Drawable.Callback.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.LayerDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.LayerDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.abandonAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaCas#setEventListener(android.media.MediaCas.EventListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaCas.setEventListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.os.Parcel#createFixedArray(Class<T>, java.util.function.Function<android.os.IBinder,S>, int...):
+ SAM-compatible parameters (such as parameter 2, "asInterface", in android.os.Parcel.createFixedArray) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Choreographer#postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.view.Choreographer.postFrameCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postOnAnimationDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.view.View.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.Window.addOnFrameMetricsAvailableListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]):
- SAM-compatible parameters (such as parameter 1, "printer", in android.database.sqlite.SQLiteDebug.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
- SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteDirectCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
- SAM-compatible parameters (such as parameter 1, "pw", in android.os.StrictMode.ViolationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 2, "callback", in android.permission.PermissionControllerManager.getAppPermissions) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.CharSequenceTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.DateTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.ImageTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>):
- SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.InternalTransformation.batchApply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#removeCallbacks(int, Runnable, Object):
- SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-
-
-StaticUtils: android.os.health.HealthKeys:
- Fully-static utility classes must not have constructor
-StaticUtils: android.service.autofill.InternalTransformation:
- Fully-static utility classes must not have constructor
-StaticUtils: android.util.FeatureFlagUtils:
- Fully-static utility classes must not have constructor
-
-
-StreamFiles: android.os.Environment#buildPath(java.io.File, java.lang.String...):
- Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.Environment.buildPath(java.io.File,java.lang.String...)
-StreamFiles: android.os.FileUtils#contains(java.io.File, java.io.File):
- Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.FileUtils.contains(java.io.File,java.io.File)
-
-
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-
-
-UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-
-
-UserHandleName: android.content.AutofillOptions:
- Classes holding a set of parameters should be called `FooParams`, was `AutofillOptions`
-UserHandleName: android.content.ContentCaptureOptions:
- Classes holding a set of parameters should be called `FooParams`, was `ContentCaptureOptions`
-
-
-VisiblySynchronized: PsiThisExpression:
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getApkPaths():
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getLastResourceResolution():
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getLastResourceResolution()
-VisiblySynchronized: android.content.res.AssetManager#getOverlayablesToString(String):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getOverlayablesToString(String)
-VisiblySynchronized: android.content.res.AssetManager#setResourceResolutionLoggingEnabled(boolean):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.setResourceResolutionLoggingEnabled(boolean)
-VisiblySynchronized: android.os.MessageQueue#removeSyncBarrier(int):
- Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.os.MessageQueue.removeSyncBarrier(int)
+UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
+UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX
+UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_REMOTE_AUTH
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERMITTED_INPUT_METHODS_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERSONAL_APPS_SUSPENDED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERSONAL_APPS_SUSPENDED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#SCREEN_CAPTURE_DISABLED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY
+UnflaggedApi: android.companion.AssociationInfo.Builder:
+ New API must be flagged with @FlaggedApi: class android.companion.AssociationInfo.Builder
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(android.companion.AssociationInfo):
+ New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(android.companion.AssociationInfo)
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(int, int, String):
+ New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(int,int,String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#build():
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.build()
+UnflaggedApi: android.companion.AssociationInfo.Builder#setAssociatedDevice(android.companion.AssociatedDevice):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setAssociatedDevice(android.companion.AssociatedDevice)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceMacAddress(android.net.MacAddress):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceMacAddress(android.net.MacAddress)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceProfile(String):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceProfile(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDisplayName(CharSequence):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDisplayName(CharSequence)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setLastTimeConnected(long):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setLastTimeConnected(long)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setNotifyOnDeviceNearby(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setNotifyOnDeviceNearby(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setRevoked(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setRevoked(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSelfManaged(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSelfManaged(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSystemDataSyncFlags(int):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSystemDataSyncFlags(int)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTag(String):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTag(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTimeApproved(long):
+ New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTimeApproved(long)
+UnflaggedApi: android.companion.CompanionDeviceManager#MESSAGE_REQUEST_PING:
+ New API must be flagged with @FlaggedApi: field android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING
+UnflaggedApi: android.companion.CompanionDeviceManager#enableSecureTransport(boolean):
+ New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.enableSecureTransport(boolean)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], android.content.AttributionSource):
+ New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],android.content.AttributionSource)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], int, android.content.AttributionSource):
+ New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],int,android.content.AttributionSource)
+UnflaggedApi: android.content.pm.UserInfo#isCommunalProfile():
+ New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isCommunalProfile()
+UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile():
+ New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isPrivateProfile()
+UnflaggedApi: android.credentials.CredentialProviderInfo#isPrimary():
+ New API must be flagged with @FlaggedApi: method android.credentials.CredentialProviderInfo.isPrimary()
+UnflaggedApi: android.media.AudioManager#enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>):
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>)
+UnflaggedApi: android.media.AudioManager#exitAudioFocusFreezeForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.exitAudioFocusFreezeForTest()
+UnflaggedApi: android.media.AudioManager#getFocusDuckedUidsForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusDuckedUidsForTest()
+UnflaggedApi: android.media.AudioManager#getFocusFadeOutDurationForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusFadeOutDurationForTest()
+UnflaggedApi: android.media.AudioManager#getFocusUnmuteDelayAfterFadeOutForTest():
+ New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusUnmuteDelayAfterFadeOutForTest()
+UnflaggedApi: android.media.RingtoneSelection:
+ New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection
+UnflaggedApi: android.media.RingtoneSelection#DEFAULT_SELECTION_URI_STRING:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.DEFAULT_SELECTION_URI_STRING
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_ONLY:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_ONLY
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_SOUND:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_SOUND
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_VIBRATION
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_AUDIO_CHANNEL
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.fromUri(android.net.Uri,int)
+UnflaggedApi: android.media.RingtoneSelection#getSoundSource():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundSource()
+UnflaggedApi: android.media.RingtoneSelection#getSoundUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundUri()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationSource():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationSource()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationUri()
+UnflaggedApi: android.media.RingtoneSelection#isRingtoneSelectionUri(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.isRingtoneSelectionUri(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection#toUri():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.toUri()
+UnflaggedApi: android.media.RingtoneSelection.Builder:
+ New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection.Builder
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder():
+ New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder()
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder(android.media.RingtoneSelection):
+ New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder(android.media.RingtoneSelection)
+UnflaggedApi: android.media.RingtoneSelection.Builder#build():
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.build()
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(int)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(android.net.Uri):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(int):
+ New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(int)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerInstrumentation#setInPhoneCallState(boolean):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerInstrumentation.setInPhoneCallState(boolean)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForTestModule():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForTestModule()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#listModuleProperties():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.listModuleProperties()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager.Model#getSoundModel():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.Model.getSoundModel()
+UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver():
+ New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivityManager.getBroadcastReceiver()
+UnflaggedApi: android.os.BatteryManager#BATTERY_PLUGGED_ANY:
+ New API must be flagged with @FlaggedApi: field android.os.BatteryManager.BATTERY_PLUGGED_ANY
+UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_MAX_VALUE:
+ New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_MAX_VALUE
+UnflaggedApi: android.os.PowerManager#isBatterySaverSupported():
+ New API must be flagged with @FlaggedApi: method android.os.PowerManager.isBatterySaverSupported()
+UnflaggedApi: android.os.UserHandle#USER_CURRENT:
+ New API must be flagged with @FlaggedApi: field android.os.UserHandle.USER_CURRENT
+UnflaggedApi: android.os.UserManager#getAliveUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getAliveUsers()
+UnflaggedApi: android.os.UserManager#getUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getUsers()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.ParsedVibration
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#getVibrationEffects():
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.getVibrationEffects()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#resolve(android.os.Vibrator):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.resolve(android.os.Vibrator)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlParser
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseDocument(java.io.Reader):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseDocument(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseVibrationEffect(java.io.Reader):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseVibrationEffect(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
+ New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlSerializer.serialize(android.os.VibrationEffect,java.io.Writer)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException:
+ New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException
+UnflaggedApi: android.service.notification.NotificationRankingUpdate:
+ New API must be flagged with @FlaggedApi: class android.service.notification.NotificationRankingUpdate
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#CONTENTS_FILE_DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#PARCELABLE_WRITE_RETURN_VALUE:
+ New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#isFdNotNullAndClosed():
+ New API must be flagged with @FlaggedApi: method android.service.notification.NotificationRankingUpdate.isFdNotNullAndClosed()
+UnflaggedApi: android.telephony.TelephonyManager#HAL_SERVICE_SATELLITE:
+ New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE
+UnflaggedApi: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities:
+ New API must be flagged with @FlaggedApi: class android.telephony.ims.feature.MmTelFeature.MmTelCapabilities
+UnflaggedApi: android.text.MeasuredParagraph:
+ New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph
+UnflaggedApi: android.text.MeasuredParagraph#buildForStaticLayoutTest(android.text.TextPaint, android.graphics.text.LineBreakConfig, CharSequence, int, int, android.text.TextDirectionHeuristic, int, boolean, android.text.MeasuredParagraph.StyleRunCallback):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.buildForStaticLayoutTest(android.text.TextPaint,android.graphics.text.LineBreakConfig,CharSequence,int,int,android.text.TextDirectionHeuristic,int,boolean,android.text.MeasuredParagraph.StyleRunCallback)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback:
+ New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph.StyleRunCallback
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendReplacementRun(android.graphics.Paint, int, float):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendReplacementRun(android.graphics.Paint,int,float)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendStyleRun(android.graphics.Paint, android.graphics.text.LineBreakConfig, int, boolean):
+ New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendStyleRun(android.graphics.Paint,android.graphics.text.LineBreakConfig,int,boolean)
+UnflaggedApi: android.view.Choreographer#getFrameTimeNanos():
+ New API must be flagged with @FlaggedApi: method android.view.Choreographer.getFrameTimeNanos()
+UnflaggedApi: android.view.InputDevice#getAssociatedDisplayId():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getAssociatedDisplayId()
+UnflaggedApi: android.view.InputDevice#getKeyboardLanguageTag():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLanguageTag()
+UnflaggedApi: android.view.InputDevice#getKeyboardLayoutType():
+ New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLayoutType()
+UnflaggedApi: android.view.MotionEvent.PointerCoords#isResampled():
+ New API must be flagged with @FlaggedApi: method android.view.MotionEvent.PointerCoords.isResampled()
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithMirror(int, android.view.Window):
+ New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithMirror(int,android.view.Window)
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithSc(int, android.view.SurfaceControl):
+ New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithSc(int,android.view.SurfaceControl)
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMaxDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMaxDisplayRefreshRate
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMinDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMinDisplayRefreshRate
+UnflaggedApi: android.view.accessibility.AccessibilityWindowInfo#UNDEFINED_WINDOW_ID:
+ New API must be flagged with @FlaggedApi: field android.view.accessibility.AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+UnflaggedApi: android.view.animation.AnimationUtils#lockAnimationClock(long, long):
+ New API must be flagged with @FlaggedApi: method android.view.animation.AnimationUtils.lockAnimationClock(long,long)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodListAsUser(android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodListAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodSubtypeListAsUser(String, boolean, android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodSubtypeListAsUser(String,boolean,android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isCurrentRootView(android.view.View):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isCurrentRootView(android.view.View)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isStylusHandwritingAvailableAsUser(android.os.UserHandle):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isStylusHandwritingAvailableAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InsertModeGesture:
+ New API must be flagged with @FlaggedApi: class android.view.inputmethod.InsertModeGesture
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#displayId:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.displayId
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#isVisible:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.isVisible
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#transform:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.transform
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b4f6d6f..4f45691 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5192,11 +5192,21 @@
* @hide
*/
public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
+ broadcastStickyIntent(intent, null, appOp, options, userId);
+ }
+
+ /**
+ * Convenience for sending a sticky broadcast. For internal use only.
+ *
+ * @hide
+ */
+ public static void broadcastStickyIntent(Intent intent, String[] excludedPackages,
+ int appOp, Bundle options, int userId) {
try {
getService().broadcastIntentWithFeature(
null, null, intent, null, null, Activity.RESULT_OK, null, null,
null /*requiredPermissions*/, null /*excludedPermissions*/,
- null /*excludedPackages*/, appOp, options, false, true, userId);
+ excludedPackages, appOp, options, false, true, userId);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 00e546a..25c48e6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -36,6 +36,7 @@
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
@@ -166,6 +167,8 @@
import android.provider.FontsContract;
import android.provider.Settings;
import android.renderscript.RenderScriptCacheDir;
+import android.se.omapi.SeFrameworkInitializer;
+import android.se.omapi.SeServiceManager;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.NetworkSecurityConfigProvider;
import android.system.ErrnoException;
@@ -265,6 +268,7 @@
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* This manages the execution of the main thread in an
@@ -382,6 +386,11 @@
@GuardedBy("mAppThread")
private int mLastProcessState = PROCESS_STATE_UNKNOWN;
final ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();
+
+ @NonNull
+ private final ConfigurationChangedListenerController mConfigurationChangedListenerController =
+ new ConfigurationChangedListenerController();
+
private int mLastSessionId;
// Holds the value of the last reported device ID value from the server for the top activity.
int mLastReportedDeviceId;
@@ -3608,6 +3617,21 @@
return mConfigurationController.getConfiguration();
}
+ /**
+ * @hide
+ */
+ public void addConfigurationChangedListener(Executor executor,
+ Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.addListener(executor, consumer);
+ }
+
+ /**
+ * @hide
+ */
+ public void removeConfigurationChangedListener(Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.removeListener(consumer);
+ }
+
@Override
public void updatePendingConfiguration(Configuration config) {
final Configuration updatedConfig =
@@ -3663,10 +3687,9 @@
int resultCode, Intent data) {
if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id
+ " req=" + requestCode + " res=" + resultCode + " data=" + data);
- ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ final ArrayList<ResultInfo> list = new ArrayList<>();
list.add(new ResultInfo(id, requestCode, resultCode, data));
- final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread,
- activityToken);
+ final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
clientTransaction.addCallback(ActivityResultItem.obtain(activityToken, list));
try {
mAppThread.scheduleTransaction(clientTransaction);
@@ -3720,7 +3743,19 @@
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
- if (r.packageInfo == null) {
+
+ if (getInstrumentation() != null
+ && getInstrumentation().getContext() != null
+ && getInstrumentation().getContext().getApplicationInfo() != null
+ && getInstrumentation().isSdkSandboxAllowedToStartActivities()) {
+ // Activities launched from CTS-in-sandbox tests use a customized ApplicationInfo. See
+ // also {@link SdkSandboxManagerLocal#getSdkSandboxApplicationInfoForInstrumentation}.
+ r.packageInfo =
+ getPackageInfo(
+ getInstrumentation().getContext().getApplicationInfo(),
+ mCompatibilityInfo,
+ Context.CONTEXT_INCLUDE_CODE);
+ } else if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo,
Context.CONTEXT_INCLUDE_CODE);
}
@@ -4429,7 +4464,7 @@
}
private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
- final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.token,
r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
/* dontReport */ false, /* autoEnteringPip */ false));
@@ -4437,7 +4472,7 @@
}
private void scheduleResume(ActivityClientRecord r) {
- final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(r.token,
/* isForward */ false, /* shouldSendCompatFakeFocus */ false));
executeTransaction(transaction);
@@ -6029,7 +6064,7 @@
final ActivityLifecycleItem lifecycleRequest =
TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
// Schedule the transaction.
- final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
transaction.addCallback(activityRelaunchItem);
transaction.setLifecycleStateRequest(lifecycleRequest);
executeTransaction(transaction);
@@ -6240,6 +6275,8 @@
" did not call through to super.onConfigurationChanged()");
}
}
+ mConfigurationChangedListenerController
+ .dispatchOnConfigurationChanged(activity.getActivityToken());
return configToReport;
}
@@ -8367,8 +8404,8 @@
BinderCallsStats.startForBluetooth(context);
});
NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager());
-
DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager());
+ SeFrameworkInitializer.setSeServiceManager(new SeServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0255860..e5a73be 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2943,6 +2943,17 @@
return isPackageSuspendedForUser(mContext.getOpPackageName(), getUserId());
}
+ @Override
+ public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ return mPM.isPackageQuarantinedForUser(packageName, getUserId());
+ } catch (IllegalArgumentException ie) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
@Override
public void setApplicationCategoryHint(String packageName, int categoryHint) {
@@ -3355,7 +3366,11 @@
}
Drawable dr = null;
if (itemInfo.packageName != null) {
- dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+ if (itemInfo.isArchived) {
+ dr = getArchivedAppIcon(itemInfo.packageName);
+ } else {
+ dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+ }
}
if (dr == null && itemInfo != appInfo && appInfo != null) {
dr = loadUnbadgedItemIcon(appInfo, appInfo);
@@ -3964,4 +3979,14 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @Nullable
+ private Drawable getArchivedAppIcon(String packageName) {
+ try {
+ return new BitmapDrawable(null,
+ mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId())));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ConfigurationChangedListenerController.java b/core/java/android/app/ConfigurationChangedListenerController.java
new file mode 100644
index 0000000..c644d57
--- /dev/null
+++ b/core/java/android/app/ConfigurationChangedListenerController.java
@@ -0,0 +1,116 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages listeners for unfiltered configuration changes.
+ * @hide
+ */
+class ConfigurationChangedListenerController {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<ListenerContainer> mListenerContainers = new ArrayList<>();
+
+ /**
+ * Adds a listener to receive updates when they are dispatched. This only dispatches updates and
+ * does not relay the last emitted value. If called with the same listener then this method does
+ * not have any effect.
+ * @param executor an executor that is used to dispatch the updates.
+ * @param consumer a listener interested in receiving updates.
+ */
+ void addListener(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ if (indexOf(consumer) > -1) {
+ return;
+ }
+ mListenerContainers.add(new ListenerContainer(executor, consumer));
+ }
+ }
+
+ /**
+ * Removes the listener that was previously registered. If the listener was not registered this
+ * method does not have any effect.
+ */
+ void removeListener(@NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ final int index = indexOf(consumer);
+ if (index > -1) {
+ mListenerContainers.remove(index);
+ }
+ }
+ }
+
+ /**
+ * Dispatches the update to all registered listeners
+ * @param activityToken a token for the {@link Activity} that received a configuration update.
+ */
+ void dispatchOnConfigurationChanged(@NonNull IBinder activityToken) {
+ final List<ListenerContainer> consumers;
+ synchronized (mLock) {
+ consumers = new ArrayList<>(mListenerContainers);
+ }
+ for (int i = 0; i < consumers.size(); i++) {
+ consumers.get(i).accept(activityToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private int indexOf(Consumer<IBinder> consumer) {
+ for (int i = 0; i < mListenerContainers.size(); i++) {
+ if (mListenerContainers.get(i).isMatch(consumer)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static final class ListenerContainer {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final Consumer<IBinder> mConsumer;
+
+ ListenerContainer(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ mExecutor = executor;
+ mConsumer = consumer;
+ }
+
+ public boolean isMatch(@NonNull Consumer<IBinder> consumer) {
+ return mConsumer.equals(consumer);
+ }
+
+ public void accept(@NonNull IBinder activityToken) {
+ mExecutor.execute(() -> mConsumer.accept(activityToken));
+ }
+
+ }
+}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 0b5a5ed1..1f5f2e4 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -17,7 +17,6 @@
package android.app;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.SuppressLint;
@@ -108,13 +107,12 @@
if (DBG) {
Log.d(TAG, "Task#" + i + ": activity=" + task.topActivity
+ ", visible=" + task.isVisible()
- + ", flg=" + Integer.toHexString(task.baseIntent.getFlags()));
+ + ", flg=" + Integer.toHexString(task.baseIntent.getFlags())
+ + ", type=" + task.getActivityType());
}
- if (!task.isVisible()
- || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
- continue;
+ if (task.isVisible() && (task.getActivityType() == ACTIVITY_TYPE_HOME)) {
+ return true;
}
- return task.getActivityType() == ACTIVITY_TYPE_HOME;
}
return false;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e31486f..10747bb 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -474,6 +475,56 @@
sr.waitForComplete();
}
+ boolean isSdkSandboxAllowedToStartActivities() {
+ return Process.isSdkSandbox()
+ && mThread != null
+ && mThread.mBoundApplication != null
+ && mThread.mBoundApplication.isSdkInSandbox
+ && getContext() != null
+ && (getContext()
+ .checkSelfPermission(
+ android.Manifest.permission
+ .START_ACTIVITIES_FROM_SDK_SANDBOX)
+ == PackageManager.PERMISSION_GRANTED);
+ }
+
+ /**
+ * Activity name resolution for CTS-in-SdkSandbox tests requires some adjustments. Intents
+ * generated using {@link Context#getPackageName()} use the SDK sandbox package name in the
+ * component field instead of the test package name. An SDK-in-sandbox test attempting to launch
+ * an activity in the test package will encounter name resolution errors when resolving the
+ * activity name in the SDK sandbox package.
+ *
+ * <p>This function replaces the package name of the input intent component to allow activities
+ * belonging to a CTS-in-sandbox test to resolve correctly.
+ *
+ * @param intent the intent to modify to allow CTS-in-sandbox activity resolution.
+ */
+ private void adjustIntentForCtsInSdkSandboxInstrumentation(@NonNull Intent intent) {
+ if (mComponent != null
+ && intent.getComponent() != null
+ && getContext()
+ .getPackageManager()
+ .getSdkSandboxPackageName()
+ .equals(intent.getComponent().getPackageName())) {
+ // Resolve the intent target for the test package, not for the sandbox package.
+ intent.setComponent(
+ new ComponentName(
+ mComponent.getPackageName(), intent.getComponent().getClassName()));
+ }
+ // We match the intent identifier against the running instrumentations for the sandbox.
+ intent.setIdentifier(mComponent.getPackageName());
+ }
+
+ private ActivityInfo resolveActivityInfoForCtsInSandbox(@NonNull Intent intent) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ ActivityInfo ai = intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
+ if (ai != null) {
+ ai.processName = mThread.getProcessName();
+ }
+ return ai;
+ }
+
/**
* Start a new activity and wait for it to begin running before returning.
* In addition to being synchronous, this method as some semantic
@@ -531,8 +582,10 @@
synchronized (mSync) {
intent = new Intent(intent);
- ActivityInfo ai = intent.resolveActivityInfo(
- getTargetContext().getPackageManager(), 0);
+ ActivityInfo ai =
+ isSdkSandboxAllowedToStartActivities()
+ ? resolveActivityInfoForCtsInSandbox(intent)
+ : intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
if (ai == null) {
throw new RuntimeException("Unable to resolve activity for: " + intent);
}
@@ -1842,6 +1895,9 @@
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
+ if (isSdkSandboxAllowedToStartActivities()) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -1914,6 +1970,11 @@
IBinder token, Activity target, Intent[] intents, Bundle options,
int userId) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (isSdkSandboxAllowedToStartActivities()) {
+ for (Intent intent : intents) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -1989,6 +2050,9 @@
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (isSdkSandboxAllowedToStartActivities()) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -2060,6 +2124,9 @@
Context who, IBinder contextThread, IBinder token, String resultWho,
Intent intent, int requestCode, Bundle options, UserHandle user) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (isSdkSandboxAllowedToStartActivities()) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -2110,6 +2177,9 @@
Intent intent, int requestCode, Bundle options,
boolean ignoreTargetSecurity, int userId) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (isSdkSandboxAllowedToStartActivities()) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -2161,6 +2231,9 @@
Context who, IBinder contextThread, IAppTask appTask,
Intent intent, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (isSdkSandboxAllowedToStartActivities()) {
+ adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+ }
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index e447dc5..2d55403 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -49,7 +49,6 @@
import android.util.Log;
import android.view.IOnKeyguardExitResult;
import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,9 +70,7 @@
import java.util.concurrent.Executor;
/**
- * Class that can be used to lock and unlock the keyguard. The
- * actual class to control the keyguard locking is
- * {@link android.app.KeyguardManager.KeyguardLock}.
+ * Class to manage and query the state of the lock screen (also known as Keyguard).
*/
@SystemService(Context.KEYGUARD_SERVICE)
public class KeyguardManager {
@@ -259,7 +256,9 @@
* {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
*
* @return the intent for launching the activity or null if no password is required.
- * @deprecated see BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)
+ *
+ * @deprecated see {@link
+ * android.hardware.biometrics.BiometricPrompt.Builder#setAllowedAuthenticators(int)}
*/
@Deprecated
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@@ -477,13 +476,12 @@
/**
* Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
- * you to disable / reenable the keyguard.
+ * you to temporarily disable / reenable the keyguard (lock screen).
*
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+ * occlude and unocclude the keyguard as your application moves in and out of the foreground
+ * and does not require that any special permissions be requested.
*/
@Deprecated
public class KeyguardLock {
@@ -498,12 +496,12 @@
* Disable the keyguard from showing. If the keyguard is currently
* showing, hide it. The keyguard will be prevented from showing again
* until {@link #reenableKeyguard()} is called.
- *
+ * <p>
+ * This only works if the keyguard is not secure.
+ * <p>
* A good place to call this is from {@link android.app.Activity#onResume()}
*
- * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
- * is enabled that requires a password.
- *
+ * @see KeyguardManager#isKeyguardSecure()
* @see #reenableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -520,9 +518,6 @@
*
* A good place to call this is from {@link android.app.Activity#onPause()}
*
- * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
- * is enabled that requires a password.
- *
* @see #disableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -621,20 +616,18 @@
}
/**
- * Enables you to lock or unlock the keyguard. Get an instance of this class by
- * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
- * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
+ * Enables you to temporarily disable / reenable the keyguard (lock screen).
+ *
* @param tag A tag that informally identifies who you are (for debugging who
* is disabling the keyguard).
*
* @return A {@link KeyguardLock} handle to use to disable and reenable the
* keyguard.
*
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+ * occlude and unocclude the keyguard as your application moves in and out of the foreground
+ * and does not require that any special permissions be requested.
*/
@Deprecated
public KeyguardLock newKeyguardLock(String tag) {
@@ -642,9 +635,36 @@
}
/**
- * Return whether the keyguard is currently locked.
+ * Returns whether the lock screen (also known as Keyguard) is showing.
+ * <p>
+ * Specifically, this returns {@code true} in the following cases:
+ * <ul>
+ * <li>The lock screen is showing in the foreground.</li>
+ * <li>The lock screen is showing, but it is occluded by an activity that is showing on top of
+ * it. A common example is the phone app receiving a call or making an emergency call.</li>
+ * <li>The lock screen was showing but is temporarily disabled as a result of <a
+ * href="https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode">lock task
+ * mode</a> or an app using the deprecated {@link KeyguardLock} API.</li>
+ * </ul>
+ * <p>
+ * "Showing" refers to a logical state of the UI, regardless of whether the screen happens to be
+ * on. When the power button is pressed on an unlocked device, the lock screen starts "showing"
+ * immediately when the screen turns off.
+ * <p>
+ * This method does not distinguish a lock screen that is requiring authentication (e.g. with
+ * PIN, pattern, password, or biometric) from a lock screen that is trivially dismissible (e.g.
+ * with swipe). It also does not distinguish a lock screen requesting a SIM card PIN from a
+ * normal device lock screen. Finally, it always returns the global lock screen state and does
+ * not consider the {@link Context}'s user specifically.
+ * <p>
+ * Note that {@code isKeyguardLocked()} is confusingly named and probably should be called
+ * {@code isKeyguardShowing()}. On many devices, the lock screen displays an <i>unlocked</i>
+ * padlock icon when it is trivially dismissible. As mentioned above, {@code isKeyguardLocked()}
+ * actually returns {@code true} in this case, not {@code false} as might be expected. {@link
+ * #isDeviceLocked()} is an alternative API that has slightly different semantics.
*
- * @return {@code true} if the keyguard is locked.
+ * @return {@code true} if the lock screen is showing
+ * @see #isDeviceLocked()
*/
public boolean isKeyguardLocked() {
try {
@@ -655,12 +675,23 @@
}
/**
- * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
- * is currently locked.
+ * Returns whether the user has a secure lock screen or there is a locked SIM card.
+ * <p>
+ * Specifically, this returns {@code true} if at least one of the following is true:
+ * <ul>
+ * <li>The {@link Context}'s user has a secure lock screen. A full user has a secure lock
+ * screen if its lock screen is set to PIN, pattern, or password, as opposed to swipe or none.
+ * A profile that uses a unified challenge is considered to have a secure lock screen if and
+ * only if its parent user has a secure lock screen.</li>
+ * <li>At least one SIM card is currently locked and requires a PIN.</li>
+ * </ul>
+ * <p>
+ * This method does not consider whether the lock screen is currently showing or not.
+ * <p>
+ * See also {@link #isDeviceSecure()} which excludes locked SIM cards.
*
- * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
- *
- * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
+ * @return {@code true} if the user has a secure lock screen or there is a locked SIM card
+ * @see #isDeviceSecure()
*/
public boolean isKeyguardSecure() {
try {
@@ -671,11 +702,11 @@
}
/**
- * If keyguard screen is showing or in restricted key input mode (i.e. in
- * keyguard password emergency screen). When in such mode, certain keys,
- * such as the Home key and the right soft keys, don't work.
+ * Returns whether the lock screen is showing.
+ * <p>
+ * This is exactly the same as {@link #isKeyguardLocked()}.
*
- * @return {@code true} if in keyguard restricted input mode.
+ * @return the value of {@link #isKeyguardLocked()}
* @deprecated Use {@link #isKeyguardLocked()} instead.
*/
public boolean inKeyguardRestrictedInputMode() {
@@ -683,11 +714,26 @@
}
/**
- * Returns whether the device is currently locked and requires a PIN, pattern or
- * password to unlock.
+ * Returns whether the device is currently locked for the user.
+ * <p>
+ * This returns the device locked state for the {@link Context}'s user. If this user is the
+ * current user, then the device is considered "locked" when the lock screen is showing (i.e.
+ * {@link #isKeyguardLocked()} returns {@code true}) and is not trivially dismissible (e.g. with
+ * swipe), and the user has a PIN, pattern, or password.
+ * <p>
+ * Note: the above definition implies that a user with no PIN, pattern, or password is never
+ * considered locked, even if the lock screen is showing and requesting a SIM card PIN. The
+ * device PIN and SIM PIN are separate. Also, the user is not considered locked if face
+ * authentication has just completed or a trust agent is keeping the device unlocked, since in
+ * these cases the lock screen is dismissible with swipe.
+ * <p>
+ * For a user that is not the current user but can be switched to (usually this means "another
+ * full user"), and that has a PIN, pattern, or password, the device is always considered
+ * locked. For a profile with a unified challenge, the device is considered locked if and only
+ * if the device is locked for the parent user.
*
- * @return {@code true} if unlocking the device currently requires a PIN, pattern or
- * password.
+ * @return {@code true} if the device is currently locked for the user
+ * @see #isKeyguardLocked()
*/
public boolean isDeviceLocked() {
return isDeviceLocked(mContext.getUserId());
@@ -708,12 +754,19 @@
}
/**
- * Returns whether the device is secured with a PIN, pattern or
- * password.
+ * Returns whether the user has a secure lock screen.
+ * <p>
+ * This returns {@code true} if the {@link Context}'s user has a secure lock screen. A full user
+ * has a secure lock screen if its lock screen is set to PIN, pattern, or password, as opposed
+ * to swipe or none. A profile that uses a unified challenge is considered to have a secure lock
+ * screen if and only if its parent user has a secure lock screen.
+ * <p>
+ * This method does not consider whether the lock screen is currently showing or not.
+ * <p>
+ * See also {@link #isKeyguardSecure()} which includes locked SIM cards.
*
- * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
- *
- * @return {@code true} if a PIN, pattern or password was set.
+ * @return {@code true} if the user has a secure lock screen
+ * @see #isKeyguardSecure()
*/
public boolean isDeviceSecure() {
return isDeviceSecure(mContext.getUserId());
@@ -734,8 +787,7 @@
}
/**
- * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
- * be dismissed.
+ * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
* <p>
* If the Keyguard is not secure or the device is currently in a trusted state, calling this
* method will immediately dismiss the Keyguard without any user interaction.
@@ -746,8 +798,9 @@
* If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
* the screen will turn on when the keyguard is dismissed.
*
- * @param activity The activity requesting the dismissal. The activity must be either visible
- * by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+ * @param activity The activity requesting the dismissal. The activity must either be visible
+ * by using {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
* which it would be visible if Keyguard would not be hiding it. If that's not
* the case, the request will fail immediately and
* {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -762,8 +815,7 @@
}
/**
- * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
- * be dismissed.
+ * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
* <p>
* If the Keyguard is not secure or the device is currently in a trusted state, calling this
* method will immediately dismiss the Keyguard without any user interaction.
@@ -774,8 +826,9 @@
* If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
* the screen will turn on when the keyguard is dismissed.
*
- * @param activity The activity requesting the dismissal. The activity must be either visible
- * by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+ * @param activity The activity requesting the dismissal. The activity must either be visible
+ * by using {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
* which it would be visible if Keyguard would not be hiding it. If that's not
* the case, the request will fail immediately and
* {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -829,12 +882,12 @@
* @param callback Lets you know whether the operation was successful and
* it is safe to launch anything that would normally be considered safe
* once the user has gotten past the keyguard.
-
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
+ *
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+ * android.app.Activity#setShowWhenLocked(boolean)} to seamlessly occlude and unocclude the
+ * keyguard as your application moves in and out of the foreground, without requiring any
+ * special permissions. Use {@link #requestDismissKeyguard(android.app.Activity,
+ * KeyguardDismissCallback)} to request dismissal of the keyguard.
*/
@Deprecated
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cbbf4e0..fa52968 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1647,6 +1647,12 @@
case Context.VIRTUALIZATION_SERVICE:
case Context.VIRTUAL_DEVICE_SERVICE:
return null;
+ case Context.SEARCH_SERVICE:
+ // Wear device does not support SEARCH_SERVICE so we do not print WTF here
+ PackageManager manager = ctx.getPackageManager();
+ if (manager != null && manager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ return null;
+ }
}
Slog.wtf(TAG, "Manager wrapper not available: " + name);
return null;
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 06e1b5b..315a055 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -138,7 +138,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
@@ -350,21 +350,6 @@
"file_patterns": ["(/|^)ContextImpl.java"]
},
{
- "name": "CtsWindowManagerDeviceScvh",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.wm.cts"
- }
- ],
- "file_patterns": ["(/|^)ContextImpl.java"]
- },
- {
"name": "CtsWindowManagerDeviceWindow",
"options": [
{
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 634089b..5f8b765 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -255,6 +255,12 @@
public boolean isUserFullscreenOverrideEnabled;
/**
+ * Whether the top activity fillsParent() is false
+ * @hide
+ */
+ public boolean isTopActivityTransparent;
+
+ /**
* Hint about the letterbox state of the top activity.
* @hide
*/
@@ -551,7 +557,8 @@
&& Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId)
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isTopActivityTransparent == that.isTopActivityTransparent;
}
/**
@@ -583,7 +590,9 @@
== that.configuration.getLayoutDirection())
&& (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode)
&& (!hasCompatUI() || isVisible == that.isVisible)
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isFocused == that.isFocused
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isTopActivityTransparent == that.isTopActivityTransparent;
}
/**
@@ -640,6 +649,7 @@
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
isUserFullscreenOverrideEnabled = source.readBoolean();
+ isTopActivityTransparent = source.readBoolean();
}
/**
@@ -697,6 +707,7 @@
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
dest.writeBoolean(isUserFullscreenOverrideEnabled);
+ dest.writeBoolean(isTopActivityTransparent);
}
@Override
@@ -744,6 +755,7 @@
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled
+ + " isTopActivityTransparent=" + isTopActivityTransparent
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " cameraCompatControlState="
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index c31aa01..fce5e4f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2429,11 +2429,8 @@
}
/**
- * Reset all wallpaper to the factory default. As opposed to {@link #clear()}, if the device
- * is configured to have a live wallpaper by default, apply it.
- *
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#SET_WALLPAPER}.
+ * Equivalent to {@link #clear()}.
+ * @see #clear()
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clearWallpaper() {
@@ -2767,8 +2764,7 @@
/**
* Remove any currently set system wallpaper, reverting to the system's built-in
- * wallpaper. As opposed to {@link #clearWallpaper()}, this method always set a static wallpaper
- * with the default image, even if the device is configured to have a live wallpaper by default.
+ * wallpaper.
* On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
*
* <p>This method requires the caller to hold the permission
@@ -2779,6 +2775,10 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear() throws IOException {
+ if (isLockscreenLiveWallpaperEnabled()) {
+ clear(FLAG_SYSTEM | FLAG_LOCK);
+ return;
+ }
setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false);
}
@@ -2787,10 +2787,15 @@
* display for each one. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
* is broadcast.
* <ul>
- * <li> If {@link #FLAG_SYSTEM} is set in the {@code which} parameter, put the default
- * wallpaper on both home and lock screen, removing any user defined wallpaper. </li>
* <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper.
* The home screen wallpaper will become visible on the lock screen. </li>
+ *
+ * <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen
+ * wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous
+ * wallpaper was shared between home and lock screen, it will become lock screen only. </li>
+ *
+ * <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the
+ * default wallpaper on both home and lock screen, removing any user defined wallpaper.</li>
* </ul>
*
* @param which A bitwise combination of {@link #FLAG_SYSTEM} or
@@ -2799,9 +2804,12 @@
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clear(@SetWallpaperFlags int which) throws IOException {
+ if (isLockscreenLiveWallpaperEnabled()) {
+ clearWallpaper(which, mContext.getUserId());
+ return;
+ }
if ((which & FLAG_SYSTEM) != 0) {
clear();
- if (isLockscreenLiveWallpaperEnabled()) return;
}
if ((which & FLAG_LOCK) != 0) {
clearWallpaper(FLAG_LOCK, mContext.getUserId());
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 715edc5..7704486 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10335,11 +10335,14 @@
* @return the current credential manager policy if null then this policy has not been
* configured.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public @Nullable PackagePolicy getCredentialManagerPolicy() {
throwIfParentInstance("getCredentialManagerPolicy");
if (mService != null) {
try {
- return mService.getCredentialManagerPolicy();
+ return mService.getCredentialManagerPolicy(myUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -11806,6 +11809,34 @@
}
/**
+ * Returns the list of {@link EnforcingAdmin}s who have set this restriction.
+ *
+ * <p>Note that for {@link #POLICY_SUSPEND_PACKAGES} it returns the PO or DO to keep the
+ * behavior the same as before the bug fix for b/192245204.
+ *
+ * <p>This API is only callable by the system UID
+ *
+ * @param userId The user for whom to retrieve the information.
+ * @param restriction The restriction enforced by admins. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ *
+ * @hide
+ */
+ public @NonNull Set<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,
+ @NonNull String restriction) {
+ if (mService != null) {
+ try {
+ return new HashSet<>(mService.getEnforcingAdminsForRestriction(
+ userId, restriction));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
diff --git a/core/java/android/app/admin/EnforcingAdmin.aidl b/core/java/android/app/admin/EnforcingAdmin.aidl
new file mode 100644
index 0000000..bfbfdbe
--- /dev/null
+++ b/core/java/android/app/admin/EnforcingAdmin.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.admin;
+
+parcelable EnforcingAdmin;
\ No newline at end of file
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 771794d..7c718f6 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -38,6 +39,11 @@
private final UserHandle mUserHandle;
/**
+ * @hide
+ */
+ private final ComponentName mComponentName;
+
+ /**
* Creates an enforcing admin with the given params.
*/
public EnforcingAdmin(
@@ -46,6 +52,21 @@
mPackageName = Objects.requireNonNull(packageName);
mAuthority = Objects.requireNonNull(authority);
mUserHandle = Objects.requireNonNull(userHandle);
+ mComponentName = null;
+ }
+
+ /**
+ * Creates an enforcing admin with the given params.
+ *
+ * @hide
+ */
+ public EnforcingAdmin(
+ @NonNull String packageName, @NonNull Authority authority,
+ @NonNull UserHandle userHandle, @Nullable ComponentName componentName) {
+ mPackageName = Objects.requireNonNull(packageName);
+ mAuthority = Objects.requireNonNull(authority);
+ mUserHandle = Objects.requireNonNull(userHandle);
+ mComponentName = componentName;
}
private EnforcingAdmin(Parcel source) {
@@ -53,6 +74,7 @@
mUserHandle = new UserHandle(source.readInt());
mAuthority = Objects.requireNonNull(
source.readParcelable(Authority.class.getClassLoader()));
+ mComponentName = source.readParcelable(ComponentName.class.getClassLoader());
}
/**
@@ -86,7 +108,8 @@
EnforcingAdmin other = (EnforcingAdmin) o;
return Objects.equals(mPackageName, other.mPackageName)
&& Objects.equals(mAuthority, other.mAuthority)
- && Objects.equals(mUserHandle, other.mUserHandle);
+ && Objects.equals(mUserHandle, other.mUserHandle)
+ && Objects.equals(mComponentName, other.mComponentName);
}
@Override
@@ -97,7 +120,7 @@
@Override
public String toString() {
return "EnforcingAdmin { mPackageName= " + mPackageName + ", mAuthority= " + mAuthority
- + ", mUserHandle= " + mUserHandle + " }";
+ + ", mUserHandle= " + mUserHandle + ", mComponentName= " + mComponentName + " }";
}
@Override
@@ -110,6 +133,7 @@
dest.writeString(mPackageName);
dest.writeInt(mUserHandle.getIdentifier());
dest.writeParcelable(mAuthority, flags);
+ dest.writeParcelable(mComponentName, flags);
}
@NonNull
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 95ec89e..58f9d57 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -54,6 +54,7 @@
import android.telephony.data.ApnSetting;
import com.android.internal.infra.AndroidFuture;
import android.app.admin.DevicePolicyState;
+import android.app.admin.EnforcingAdmin;
import java.util.List;
@@ -274,6 +275,7 @@
Intent createAdminSupportIntent(in String restriction);
Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
+ List<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,String restriction);
boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
@@ -344,7 +346,7 @@
boolean hasManagedProfileCallerIdAccess(int userId, String packageName);
void setCredentialManagerPolicy(in PackagePolicy policy);
- PackagePolicy getCredentialManagerPolicy();
+ PackagePolicy getCredentialManagerPolicy(int userId);
void setManagedProfileContactsAccessPolicy(in PackagePolicy policy);
PackagePolicy getManagedProfileContactsAccessPolicy();
diff --git a/core/java/android/app/admin/Provisioning_OWNERS b/core/java/android/app/admin/Provisioning_OWNERS
index fa0a1f0..8f71fc0 100644
--- a/core/java/android/app/admin/Provisioning_OWNERS
+++ b/core/java/android/app/admin/Provisioning_OWNERS
@@ -1,4 +1,4 @@
# Assign bugs to android-enterprise-triage@google.com
-mdb.ae-provisioning-reviews@google.com
+ae-provisioning-reviews@google.com
+petuska@google.com #{LAST_RESORT_SUGGESTION}
file:EnterprisePlatform_OWNERS
-petuska@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index f73366b..812bf8e 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.backup.BackupAnnotations.OperationType;
+import android.content.pm.PackageInfo;
import android.os.Bundle;
/**
@@ -190,6 +191,56 @@
public static final int LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED = 51;
public static final int LOG_EVENT_ID_AGENT_LOGGING_RESULTS = 52;
+ /** @hide */
+ public static final int LOG_EVENT_ID_START_SYSTEM_RESTORE = 53;
+ /** @hide */
+ public static final int LOG_EVENT_ID_START_RESTORE_AT_INSTALL = 54;
+ /** A transport error happened during {@link PerformUnifiedRestoreTask#startRestore()}
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE = 55;
+ /** Unable to get the name of the next package in the queue during a restore operation
+ @hide */
+ public static final int LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME = 56;
+ /** Attempting a restore operation that is neither KV nor full
+ @hide */
+ public static final int LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE = 57;
+ /** The package is part of KeyValue restore
+ @hide */
+ public static final int LOG_EVENT_ID_KV_RESTORE = 58;
+ /** The package is part of Full restore
+ @hide */
+ public static final int LOG_EVENT_ID_FULL_RESTORE = 59;
+ /** Unable to fetch the nest restore target in the queue
+ @hide */
+ public static final int LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET = 60;
+ /** An error occurred while attempting KeyValueRestore
+ @hide */
+ public static final int LOG_EVENT_ID_KV_AGENT_ERROR = 61;
+ /** Restore operation finished for the given package
+ @hide */
+ public static final int LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED= 62;
+ /** A transport error happened during
+ * {@link PerformUnifiedRestoreTask#initiateOneRestore(PackageInfo, long)}
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE = 63;
+ /** Unable to instantiate the feeder thread in full restore
+ @hide */
+ public static final int LOG_EVENT_ID_NO_FEEDER_THREAD = 64;
+ /** An error occurred while attempting Full restore
+ @hide */
+ public static final int LOG_EVENT_ID_FULL_AGENT_ERROR = 65;
+ /** A transport error happened during a full restore
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE = 66;
+ /** Start restore operation for a given package
+ @hide */
+ public static final int LOG_EVENT_ID_START_PACKAGE_RESTORE = 67;
+ /** Whole restore operation is complete
+ @hide */
+ public static final int LOG_EVENT_ID_RESTORE_COMPLETE = 68;
+ /** Agent error during {@link PerformUnifiedRestoreTask#restoreFinished()}
+ @hide */
+ public static final int LOG_EVENT_ID_AGENT_FAILURE = 69;
/**
* This method will be called each time something important happens on BackupManager.
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index c2c5427..dd332c8 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -41,7 +41,7 @@
private Configuration mConfiguration;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @Nullable IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
@@ -59,8 +59,7 @@
@Nullable
@Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
- @Nullable IBinder token) {
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
return client.getActivity(getActivityToken());
}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 491d026..a5dd115 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -56,7 +56,7 @@
private ActivityClientRecord mActivityClientRecord;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
// The local config is already scaled so only apply if this item is from server side.
if (!client.isExecutingLocalTransaction()) {
CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
@@ -78,7 +78,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
final ActivityClientRecord r = getActivityClientRecord(client);
client.reportRelaunch(r);
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 0f8879e..2a65b35 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -16,6 +16,8 @@
package android.app.servertransaction;
+import static android.app.servertransaction.TransactionExecutorHelper.getActivityName;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import android.annotation.CallSuper;
@@ -28,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.Objects;
/**
@@ -49,14 +52,14 @@
ActivityTransactionItem() {}
@Override
- public final void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public final void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
final ActivityClientRecord r = getActivityClientRecord(client);
execute(client, r, pendingActions);
}
/**
- * Like {@link #execute(ClientTransactionHandler, IBinder, PendingTransactionActions)},
+ * Like {@link #execute(ClientTransactionHandler, PendingTransactionActions)},
* but take non-null {@link ActivityClientRecord} as a parameter.
*/
@VisibleForTesting(visibility = PACKAGE)
@@ -111,6 +114,14 @@
mActivityToken = null;
}
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+ @NonNull ClientTransactionHandler transactionHandler) {
+ super.dump(prefix, pw, transactionHandler);
+ pw.append(prefix).append("Target activity: ")
+ .println(getActivityName(mActivityToken, transactionHandler));
+ }
+
// Subclass must override and call super.equals to compare the mActivityToken.
@SuppressWarnings("EqualsGetClass")
@CallSuper
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
index c91e0ca..f275175 100644
--- a/core/java/android/app/servertransaction/BaseClientRequest.java
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -16,8 +16,8 @@
package android.app.servertransaction;
+import android.annotation.NonNull;
import android.app.ClientTransactionHandler;
-import android.os.IBinder;
/**
* Base interface for individual requests from server to client.
@@ -27,31 +27,28 @@
public interface BaseClientRequest extends ObjectPoolItem {
/**
- * Prepare the client request before scheduling.
+ * Prepares the client request before scheduling.
* An example of this might be informing about pending updates for some values.
*
* @param client Target client handler.
- * @param token Target activity token.
*/
- default void preExecute(ClientTransactionHandler client, IBinder token) {
+ default void preExecute(@NonNull ClientTransactionHandler client) {
}
/**
- * Execute the request.
+ * Executes the request.
* @param client Target client handler.
- * @param token Target activity token.
* @param pendingActions Container that may have data pending to be used.
*/
- void execute(ClientTransactionHandler client, IBinder token,
- PendingTransactionActions pendingActions);
+ void execute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions);
/**
- * Perform all actions that need to happen after execution, e.g. report the result to server.
+ * Performs all actions that need to happen after execution, e.g. report the result to server.
* @param client Target client handler.
- * @param token Target activity token.
* @param pendingActions Container that may have data pending to be used.
*/
- default void postExecute(ClientTransactionHandler client, IBinder token,
- PendingTransactionActions pendingActions) {
+ default void postExecute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
}
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index ee14708..a5b0f18 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -16,6 +16,9 @@
package android.app.servertransaction;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
@@ -36,7 +39,7 @@
* A container that holds a sequence of messages, which may be sent to a client.
* This includes a list of callbacks and a final lifecycle state.
*
- * @see com.android.server.am.ClientLifecycleManager
+ * @see com.android.server.wm.ClientLifecycleManager
* @see ClientTransactionItem
* @see ActivityLifecycleItem
* @hide
@@ -56,9 +59,6 @@
/** Target client. */
private IApplicationThread mClient;
- /** Target client activity. Might be null if the entire transaction is targeting an app. */
- private IBinder mActivityToken;
-
/** Get the target client of the transaction. */
public IApplicationThread getClient() {
return mClient;
@@ -68,7 +68,7 @@
* Add a message to the end of the sequence of callbacks.
* @param activityCallback A single message that can contain a lifecycle request/callback.
*/
- public void addCallback(ClientTransactionItem activityCallback) {
+ public void addCallback(@NonNull ClientTransactionItem activityCallback) {
if (mActivityCallbacks == null) {
mActivityCallbacks = new ArrayList<>();
}
@@ -87,11 +87,21 @@
@Nullable
@UnsupportedAppUsage
public IBinder getActivityToken() {
- return mActivityToken;
+ // TODO(b/260873529): remove after we allow multiple activity items in one transaction.
+ if (mLifecycleStateRequest != null) {
+ return mLifecycleStateRequest.getActivityToken();
+ }
+ for (int i = mActivityCallbacks.size() - 1; i >= 0; i--) {
+ final IBinder token = mActivityCallbacks.get(i).getActivityToken();
+ if (token != null) {
+ return token;
+ }
+ }
+ return null;
}
/** Get the target state lifecycle request. */
- @VisibleForTesting
+ @VisibleForTesting(visibility = PACKAGE)
@UnsupportedAppUsage
public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
@@ -101,7 +111,7 @@
* Set the lifecycle state in which the client should be after executing the transaction.
* @param stateRequest A lifecycle request initialized with right parameters.
*/
- public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
+ public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
mLifecycleStateRequest = stateRequest;
}
@@ -110,15 +120,15 @@
* @param clientTransactionHandler Handler on the client side that will executed all operations
* requested by transaction items.
*/
- public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
+ public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
if (mActivityCallbacks != null) {
final int size = mActivityCallbacks.size();
for (int i = 0; i < size; ++i) {
- mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
+ mActivityCallbacks.get(i).preExecute(clientTransactionHandler);
}
}
if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
+ mLifecycleStateRequest.preExecute(clientTransactionHandler);
}
}
@@ -141,14 +151,14 @@
private ClientTransaction() {}
- /** Obtain an instance initialized with provided params. */
- public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
+ /** Obtains an instance initialized with provided params. */
+ @NonNull
+ public static ClientTransaction obtain(@Nullable IApplicationThread client) {
ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
if (instance == null) {
instance = new ClientTransaction();
}
instance.mClient = client;
- instance.mActivityToken = activityToken;
return instance;
}
@@ -167,7 +177,6 @@
mLifecycleStateRequest = null;
}
mClient = null;
- mActivityToken = null;
ObjectPool.recycle(this);
}
@@ -175,12 +184,7 @@
/** Write to Parcel. */
@Override
- public void writeToParcel(Parcel dest, int flags) {
- final boolean writeActivityToken = mActivityToken != null;
- dest.writeBoolean(writeActivityToken);
- if (writeActivityToken) {
- dest.writeStrongBinder(mActivityToken);
- }
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mLifecycleStateRequest, flags);
final boolean writeActivityCallbacks = mActivityCallbacks != null;
dest.writeBoolean(writeActivityCallbacks);
@@ -190,11 +194,7 @@
}
/** Read from Parcel. */
- private ClientTransaction(Parcel in) {
- final boolean readActivityToken = in.readBoolean();
- if (readActivityToken) {
- mActivityToken = in.readStrongBinder();
- }
+ private ClientTransaction(@NonNull Parcel in) {
mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class);
final boolean readActivityCallbacks = in.readBoolean();
if (readActivityCallbacks) {
@@ -203,9 +203,8 @@
}
}
- public static final @android.annotation.NonNull Creator<ClientTransaction> CREATOR =
- new Creator<ClientTransaction>() {
- public ClientTransaction createFromParcel(Parcel in) {
+ public static final @NonNull Creator<ClientTransaction> CREATOR = new Creator<>() {
+ public ClientTransaction createFromParcel(@NonNull Parcel in) {
return new ClientTransaction(in);
}
@@ -230,8 +229,7 @@
final ClientTransaction other = (ClientTransaction) o;
return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
&& Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
- && mClient == other.mClient
- && mActivityToken == other.mActivityToken;
+ && mClient == other.mClient;
}
@Override
@@ -240,26 +238,32 @@
result = 31 * result + Objects.hashCode(mActivityCallbacks);
result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
result = 31 * result + Objects.hashCode(mClient);
- result = 31 * result + Objects.hashCode(mActivityToken);
return result;
}
/** Dump transaction items callback items and final lifecycle state request. */
- public void dump(String prefix, PrintWriter pw) {
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+ @NonNull ClientTransactionHandler transactionHandler) {
pw.append(prefix).println("ClientTransaction{");
pw.append(prefix).print(" callbacks=[");
+ final String itemPrefix = prefix + " ";
final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
if (size > 0) {
pw.println();
for (int i = 0; i < size; i++) {
- pw.append(prefix).append(" ").println(mActivityCallbacks.get(i).toString());
+ mActivityCallbacks.get(i).dump(itemPrefix, pw, transactionHandler);
}
pw.append(prefix).println(" ]");
} else {
pw.println("]");
}
- pw.append(prefix).append(" stateRequest=").println(mLifecycleStateRequest != null
- ? mLifecycleStateRequest.toString() : null);
+
+ pw.append(prefix).println(" stateRequest=");
+ if (mLifecycleStateRequest != null) {
+ mLifecycleStateRequest.dump(itemPrefix, pw, transactionHandler);
+ } else {
+ pw.append(itemPrefix).println("null");
+ }
pw.append(prefix).println("}");
}
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index 30fc104..07e5a7d 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -30,6 +30,8 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
+
/**
* A callback message to a client that can be scheduled and executed.
* Examples of these might be activity configuration change, multi-window mode change, activity
@@ -56,8 +58,7 @@
* it is updating; otherwise, returns {@code null}.
*/
@Nullable
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
- @NonNull IBinder token) {
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
return null;
}
@@ -71,6 +72,12 @@
return null;
}
+ /** Dumps this transaction item. */
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+ @NonNull ClientTransactionHandler transactionHandler) {
+ pw.append(prefix).println(this);
+ }
+
// Parcelable
@Override
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index f72e2e0..96961ace 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.os.IBinder;
import android.os.Parcel;
import java.util.Objects;
@@ -38,21 +37,20 @@
private int mDeviceId;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @Nullable IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
client.updatePendingConfiguration(mConfiguration);
}
@Override
- public void execute(@NonNull ClientTransactionHandler client, @Nullable IBinder token,
+ public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
client.handleConfigurationChanged(mConfiguration, mDeviceId);
}
@Nullable
@Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
- @Nullable IBinder token) {
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
return ActivityThread.currentApplication();
}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index a327a99..ddb6df1 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -36,7 +36,7 @@
private int mConfigChanges;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
client.getActivitiesToBeDestroyed().put(getActivityToken(), this);
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 9b37a35..a64c744 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -84,7 +84,7 @@
private IActivityClientController mActivityClientController;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
@@ -96,7 +96,7 @@
}
@Override
- public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
@@ -109,7 +109,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
client.countLaunchingActivities(-1);
}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index fb57bed..e56d3f8 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,7 +40,7 @@
private Configuration mConfiguration;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index a8e6772..8f1e90b 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -56,7 +56,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
if (mDontReport) {
return;
diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java
index 00128f0..368ed76 100644
--- a/core/java/android/app/servertransaction/RefreshCallbackItem.java
+++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java
@@ -51,7 +51,7 @@
@NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) {}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
final ActivityClientRecord r = getActivityClientRecord(client);
client.reportRefresh(r);
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index b11e73c..4a0ea98 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -44,7 +44,7 @@
private boolean mShouldSendCompatFakeFocus;
@Override
- public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+ public void preExecute(@NonNull ClientTransactionHandler client) {
if (mUpdateProcState) {
client.updateProcessState(mProcState, false);
}
@@ -60,7 +60,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
// TODO(lifecycler): Use interface callback instead of actual implementation.
ActivityClient.getInstance().activityResumed(getActivityToken(),
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index f432567..b8ce52d 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -46,7 +46,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
client.reportStop(pendingActions);
}
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 693599f..23d4505 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -43,7 +43,7 @@
}
@Override
- public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
if (mOnTop) {
return;
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index d080162..4433673 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -74,7 +74,7 @@
* Then the client will cycle to the final lifecycle state if provided. Otherwise, it will
* either remain in the initial state, or last state needed by a callback.
*/
- public void execute(ClientTransaction transaction) {
+ public void execute(@NonNull ClientTransaction transaction) {
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
final IBinder token = transaction.getActivityToken();
@@ -109,7 +109,7 @@
/** Cycle through all states requested by callbacks and execute them at proper times. */
@VisibleForTesting
- public void executeCallbacks(ClientTransaction transaction) {
+ public void executeCallbacks(@NonNull ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
if (callbacks == null || callbacks.isEmpty()) {
// No callbacks to execute, return early.
@@ -117,9 +117,6 @@
}
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
- final IBinder token = transaction.getActivityToken();
- ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
-
// In case when post-execution state of the last callback matches the final state requested
// for the activity in this transaction, we won't do the last transition here and do it when
// moving to final state instead (because it may contain additional parameters from server).
@@ -135,6 +132,9 @@
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
+ final IBinder token = item.getActivityToken();
+ ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
@@ -150,13 +150,13 @@
final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
&& syncWindowConfigUpdateFlag();
final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
- ? item.getContextToUpdate(mTransactionHandler, token)
+ ? item.getContextToUpdate(mTransactionHandler)
: null;
final Configuration preExecutedConfig = configUpdatedContext != null
? new Configuration(configUpdatedContext.getResources().getConfiguration())
: null;
- item.execute(mTransactionHandler, token, mPendingActions);
+ item.execute(mTransactionHandler, mPendingActions);
if (configUpdatedContext != null) {
final Configuration postExecutedConfig = configUpdatedContext.getResources()
@@ -169,7 +169,7 @@
}
}
- item.postExecute(mTransactionHandler, token, mPendingActions);
+ item.postExecute(mTransactionHandler, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.
r = mTransactionHandler.getActivityClient(token);
@@ -195,14 +195,14 @@
}
/** Transition to the final state if requested by the transaction. */
- private void executeLifecycleState(ClientTransaction transaction) {
+ private void executeLifecycleState(@NonNull ClientTransaction transaction) {
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
if (lifecycleItem == null) {
// No lifecycle request, return early.
return;
}
- final IBinder token = transaction.getActivityToken();
+ final IBinder token = lifecycleItem.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
if (DEBUG_RESOLVER) {
Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
@@ -219,8 +219,8 @@
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
// Execute the final transition with proper parameters.
- lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
- lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
+ lifecycleItem.execute(mTransactionHandler, mPendingActions);
+ lifecycleItem.postExecute(mTransactionHandler, mPendingActions);
}
/** Transition the client between states. */
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 0f9c517..7e89a5b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,6 +26,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -266,14 +267,12 @@
}
/** Dump transaction to string. */
- static String transactionToString(ClientTransaction transaction,
- ClientTransactionHandler transactionHandler) {
+ static String transactionToString(@NonNull ClientTransaction transaction,
+ @NonNull ClientTransactionHandler transactionHandler) {
final StringWriter stringWriter = new StringWriter();
final PrintWriter pw = new PrintWriter(stringWriter);
final String prefix = tId(transaction);
- transaction.dump(prefix, pw);
- pw.append(prefix + "Target activity: ")
- .println(getActivityName(transaction.getActivityToken(), transactionHandler));
+ transaction.dump(prefix, pw, transactionHandler);
return stringWriter.toString();
}
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index 99824b0..375d1bf 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -41,15 +41,14 @@
private WindowContextInfo mInfo;
@Override
- public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
client.handleWindowContextInfoChanged(mClientToken, mInfo);
}
@Nullable
@Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
- @Nullable IBinder token) {
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
return client.getWindowContext(mClientToken);
}
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
index ed52a64..1bea468 100644
--- a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -36,7 +36,7 @@
private IBinder mClientToken;
@Override
- public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
client.handleWindowContextWindowRemoval(mClientToken);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index b4c740ec..0fa78c8 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -229,6 +229,7 @@
@Nullable private final String mName;
// Mapping of @PolicyType to @DevicePolicy
@NonNull private final SparseIntArray mDevicePolicies;
+ @Nullable private final ComponentName mHomeComponent;
@NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs;
@Nullable private final IVirtualSensorCallback mVirtualSensorCallback;
private final int mAudioPlaybackSessionId;
@@ -243,6 +244,7 @@
@NonNull Set<ComponentName> activityPolicyExemptions,
@Nullable String name,
@NonNull SparseIntArray devicePolicies,
+ @Nullable ComponentName homeComponent,
@NonNull List<VirtualSensorConfig> virtualSensorConfigs,
@Nullable IVirtualSensorCallback virtualSensorCallback,
int audioPlaybackSessionId,
@@ -258,6 +260,7 @@
new ArraySet<>(Objects.requireNonNull(activityPolicyExemptions));
mName = name;
mDevicePolicies = Objects.requireNonNull(devicePolicies);
+ mHomeComponent = homeComponent;
mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs);
mVirtualSensorCallback = virtualSensorCallback;
mAudioPlaybackSessionId = audioPlaybackSessionId;
@@ -280,6 +283,7 @@
IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder());
mAudioPlaybackSessionId = parcel.readInt();
mAudioRecordingSessionId = parcel.readInt();
+ mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR);
}
/**
@@ -291,6 +295,19 @@
}
/**
+ * Returns the custom component used as home on all displays owned by this virtual device that
+ * support home activities.
+ *
+ * @see Builder#setHomeComponent
+ */
+ // TODO(b/297168328): Link to the relevant API for creating displays with home support.
+ @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
+ @Nullable
+ public ComponentName getHomeComponent() {
+ return mHomeComponent;
+ }
+
+ /**
* Returns the user handles with matching managed accounts on the remote device to which
* this virtual device is streaming.
*
@@ -468,6 +485,7 @@
mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null);
dest.writeInt(mAudioPlaybackSessionId);
dest.writeInt(mAudioRecordingSessionId);
+ dest.writeTypedObject(mHomeComponent, flags);
}
@Override
@@ -508,7 +526,7 @@
int hashCode = Objects.hash(
mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions,
mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName,
- mDevicePolicies, mAudioPlaybackSessionId, mAudioRecordingSessionId);
+ mDevicePolicies, mHomeComponent, mAudioPlaybackSessionId, mAudioRecordingSessionId);
for (int i = 0; i < mDevicePolicies.size(); i++) {
hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
@@ -528,6 +546,7 @@
+ " mActivityPolicyExemptions=" + mActivityPolicyExemptions
+ " mName=" + mName
+ " mDevicePolicies=" + mDevicePolicies
+ + " mHomeComponent=" + mHomeComponent
+ " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId
+ " mAudioRecordingSessionId=" + mAudioRecordingSessionId
+ ")";
@@ -588,6 +607,7 @@
@Nullable private VirtualSensorCallback mVirtualSensorCallback;
@Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor;
@Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback;
+ @Nullable private ComponentName mHomeComponent;
private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub {
@NonNull
@@ -665,6 +685,23 @@
}
/**
+ * Specifies a component to be used as home on all displays owned by this virtual device
+ * that support home activities.
+ * *
+ * <p>Note: Only relevant for virtual displays that support home activities.</p>
+ *
+ * @param homeComponent The component name to be used as home. If unset, then the system-
+ * default secondary home activity will be used.
+ */
+ // TODO(b/297168328): Link to the relevant API for creating displays with home support.
+ @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
+ @NonNull
+ public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
+ mHomeComponent = homeComponent;
+ return this;
+ }
+
+ /**
* Sets the user handles with matching managed accounts on the remote device to which
* this virtual device is streaming. The caller is responsible for verifying the presence
* and legitimacy of a matching managed account on the remote device.
@@ -1031,6 +1068,7 @@
mActivityPolicyExemptions,
mName,
mDevicePolicies,
+ mHomeComponent,
mVirtualSensorConfigs,
virtualSensorCallbackDelegate,
mAudioPlaybackSessionId,
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index ee36f18..3e96c96 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -15,6 +15,13 @@
}
flag {
+ name: "vdm_custom_home"
+ namespace: "virtual_devices"
+ description: "Enable custom home API"
+ bug: "297168328"
+}
+
+flag {
name: "vdm_public_apis"
namespace: "virtual_devices"
description: "Enable public VDM API for device capabilities"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d6dee93..b6a98a5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4089,6 +4089,7 @@
VIBRATOR_MANAGER_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
+ THREAD_NETWORK_SERVICE,
CONNECTIVITY_SERVICE,
PAC_PROXY_SERVICE,
VCN_MANAGEMENT_SERVICE,
@@ -4764,6 +4765,20 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.net.thread.ThreadNetworkManager}.
+ *
+ * <p>On devices without {@link PackageManager#FEATURE_THREAD_NETWORK} system feature
+ * the {@link #getSystemService(String)} will return {@code null}.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.thread.ThreadNetworkManager
+ * @hide
+ */
+ @SystemApi
+ public static final String THREAD_NETWORK_SERVICE = "thread_network";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.IpSecManager} for encrypting Sockets or Networks with
* IPSec.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e9bbed3..7579d99 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12516,6 +12516,7 @@
return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
}
+ // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal}
/** @hide */
public boolean isSandboxActivity(@NonNull Context context) {
if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index 01a9373..addede4 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/android/content/om/OWNERS b/core/java/android/content/om/OWNERS
index 3669817..72aed2d 100644
--- a/core/java/android/content/om/OWNERS
+++ b/core/java/android/content/om/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 568631
-toddke@android.com
-toddke@google.com
patb@google.com
zyy@google.com
+jakmcbane@google.com
\ No newline at end of file
diff --git a/core/java/android/content/pm/ArchivedActivityParcel.aidl b/core/java/android/content/pm/ArchivedActivityParcel.aidl
new file mode 100644
index 0000000..7ab7ed1
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedActivityParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.content.pm;
+
+/** @hide */
+parcelable ArchivedActivityParcel {
+ String title;
+ // PNG compressed bitmaps.
+ byte[] iconBitmap;
+ byte[] monochromeIconBitmap;
+}
diff --git a/core/java/android/content/pm/ArchivedPackageParcel.aidl b/core/java/android/content/pm/ArchivedPackageParcel.aidl
index 573e690..d3cd79e 100644
--- a/core/java/android/content/pm/ArchivedPackageParcel.aidl
+++ b/core/java/android/content/pm/ArchivedPackageParcel.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.ArchivedActivityParcel;
import android.content.pm.SigningDetails;
/**
@@ -29,9 +30,8 @@
int versionCode;
int versionCodeMajor;
int targetSdkVersion;
- String backupAllowed;
String defaultToDeviceProtectedStorage;
String requestLegacyExternalStorage;
String userDataFragile;
- String clearUserDataOnFailedRestoreAllowed;
+ ArchivedActivityParcel[] archivedActivities;
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 556c794..aca88d6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
@@ -59,7 +60,7 @@
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
-import android.content.IntentSender;
+import android.os.UserHandle;
import java.util.Map;
@@ -832,5 +833,7 @@
void unregisterPackageMonitorCallback(IRemoteCallback callback);
- ArchivedPackageParcel getArchivedPackage(in String apkPath);
+ ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);
+
+ Bitmap getArchivedAppIcon(String packageName, in UserHandle user);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 3165e29..dbaa4c9 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -75,7 +75,6 @@
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.function.pooled.PooledLambda;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1424,8 +1423,8 @@
}
try {
return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Icon file not found: " + uri);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to open icon file: " + uri, e);
return null;
}
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1fe1923..63c11b7 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -18,9 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.IntentSender;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -490,18 +488,6 @@
*/
public boolean isActiveApex;
- /**
- * Whether the package is currently in an archived state.
- *
- * <p>Packages can be archived through
- * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
- * on the device, but do keep the data directory.
- * @hide
- */
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
- public boolean isArchived;
-
public PackageInfo() {
}
@@ -589,7 +575,6 @@
}
dest.writeBoolean(isApex);
dest.writeBoolean(isActiveApex);
- dest.writeBoolean(isArchived);
dest.restoreAllowSquashing(prevAllowSquashing);
}
@@ -655,6 +640,5 @@
}
isApex = source.readBoolean();
isActiveApex = source.readBoolean();
- isArchived = source.readBoolean();
}
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 673a8a5..3a9e9bf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -371,6 +371,13 @@
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
/**
+ * A list of warnings that occurred during installation.
+ *
+ * @hide
+ */
+ public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index bb978e0..c7091ad 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -21,6 +21,7 @@
import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
import static android.text.TextUtils.makeSafeForPresentation;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -173,6 +174,18 @@
*/
public int showUserIcon;
+ /**
+ * Whether the package is currently in an archived state.
+ *
+ * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
+ * on the device, but do keep the data directory.
+ * @hide
+ */
+ // TODO(b/278553670) Unhide and update @links before launch.
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public boolean isArchived;
+
public PackageItemInfo() {
showUserIcon = UserHandle.USER_NULL;
}
@@ -189,6 +202,7 @@
logo = orig.logo;
metaData = orig.metaData;
showUserIcon = orig.showUserIcon;
+ isArchived = orig.isArchived;
}
/**
@@ -442,6 +456,7 @@
dest.writeBundle(metaData);
dest.writeInt(banner);
dest.writeInt(showUserIcon);
+ dest.writeBoolean(isArchived);
}
/**
@@ -459,6 +474,7 @@
}
proto.write(PackageItemInfoProto.ICON, icon);
proto.write(PackageItemInfoProto.BANNER, banner);
+ proto.write(PackageItemInfoProto.IS_ARCHIVED, isArchived);
proto.end(token);
}
@@ -473,6 +489,7 @@
metaData = source.readBundle();
banner = source.readInt();
showUserIcon = source.readInt();
+ isArchived = source.readBoolean();
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cdab431..3fc515d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2549,6 +2549,15 @@
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that the deletion is an archival. This
+ * flag is only for internal usage as part of
+ * {@link PackageInstaller#requestArchive(String, IntentSender)}.
+ *
+ * @hide
+ */
+ public static final int DELETE_ARCHIVE = 0x00000010;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
@@ -9868,6 +9877,18 @@
}
/**
+ * Query if an app is currently quarantined.
+ *
+ * @return {@code true} if the given package is quarantined, {@code false} otherwise
+ * @throws NameNotFoundException if the package could not be found.
+ *
+ * @hide
+ */
+ public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException("isPackageQuarantined not implemented");
+ }
+
+ /**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
* <p>
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 149de7e..0333942 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -834,4 +834,11 @@
public abstract V parseServiceAttributes(Resources res,
String packageName, AttributeSet attrs);
+
+ @VisibleForTesting
+ public void unregisterReceivers() {
+ mContext.unregisterReceiver(mPackageReceiver);
+ mContext.unregisterReceiver(mExternalReceiver);
+ mContext.unregisterReceiver(mUserRemovedReceiver);
+ }
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index be8b2a2..65f56f6 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -486,7 +486,7 @@
* Here is an example:
* <pre>
* <uses-permission
- * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
* />
* <service
* android:name=".MySpecialForegroundService"
@@ -506,7 +506,7 @@
* in both platforms.
* <pre>
* <uses-permission
- * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
* android:maxSdkVersion="last_sdk_version_without_type_foo"
* />
* <service
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 159b789..f3194be 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -23,11 +23,9 @@
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
-import android.os.Build;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
-import com.android.internal.util.XmlUtils;
import java.util.List;
import java.util.Set;
@@ -142,32 +140,9 @@
private final boolean mIsSdkLibrary;
/**
- * Set to <code>false</code> if the application does not wish to permit any OS-driven
- * backups of its data; <code>true</code> otherwise.
+ * Archival install info.
*/
- private final boolean mBackupAllowed;
-
- /**
- * When set, the default data storage directory for this app is pointed at
- * the device-protected location.
- */
- private final boolean mDefaultToDeviceProtectedStorage;
-
- /**
- * If {@code true} this app requests full external storage access.
- */
- private final boolean mRequestLegacyExternalStorage;
-
- /**
- * Indicates whether this application has declared its user data as fragile, causing the
- * system to prompt the user on whether to keep the user data on uninstall.
- */
- private final boolean mUserDataFragile;
-
- /**
- * Indicates whether this application's data will be cleared on a failed restore.
- */
- private final boolean mClearUserDataOnFailedRestoreAllowed;
+ private final @Nullable ArchivedPackageParcel mArchivedPackage;
public ApkLite(String path, String packageName, String splitName, boolean isFeatureSplit,
String configForSplit, String usesSplitName, boolean isSplitRequired, int versionCode,
@@ -179,10 +154,7 @@
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean clearUserDataAllowed,
- boolean backupAllowed, boolean defaultToDeviceProtectedStorage,
- boolean requestLegacyExternalStorage, boolean userDataFragile,
- boolean clearUserDataOnFailedRestoreAllowed) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -216,11 +188,7 @@
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
- mBackupAllowed = backupAllowed;
- mDefaultToDeviceProtectedStorage = defaultToDeviceProtectedStorage;
- mRequestLegacyExternalStorage = requestLegacyExternalStorage;
- mUserDataFragile = userDataFragile;
- mClearUserDataOnFailedRestoreAllowed = clearUserDataOnFailedRestoreAllowed;
+ mArchivedPackage = null;
}
public ApkLite(String path, ArchivedPackageParcel archivedPackage) {
@@ -257,16 +225,7 @@
mRollbackDataPolicy = 0;
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
- // @see ParsingPackageUtils#parseBaseAppBasicFlags
- mBackupAllowed = XmlUtils.convertValueToBoolean(archivedPackage.backupAllowed, true);
- mDefaultToDeviceProtectedStorage = XmlUtils.convertValueToBoolean(
- archivedPackage.defaultToDeviceProtectedStorage, false);
- mRequestLegacyExternalStorage = XmlUtils.convertValueToBoolean(
- archivedPackage.requestLegacyExternalStorage,
- mTargetSdkVersion < Build.VERSION_CODES.Q);
- mUserDataFragile = XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false);
- mClearUserDataOnFailedRestoreAllowed = XmlUtils.convertValueToBoolean(
- archivedPackage.clearUserDataOnFailedRestoreAllowed, true);
+ mArchivedPackage = archivedPackage;
}
/**
@@ -576,53 +535,18 @@
}
/**
- * Set to <code>false</code> if the application does not wish to permit any OS-driven
- * backups of its data; <code>true</code> otherwise.
+ * Archival install info.
*/
@DataClass.Generated.Member
- public boolean isBackupAllowed() {
- return mBackupAllowed;
- }
-
- /**
- * When set, the default data storage directory for this app is pointed at
- * the device-protected location.
- */
- @DataClass.Generated.Member
- public boolean isDefaultToDeviceProtectedStorage() {
- return mDefaultToDeviceProtectedStorage;
- }
-
- /**
- * If {@code true} this app requests full external storage access.
- */
- @DataClass.Generated.Member
- public boolean isRequestLegacyExternalStorage() {
- return mRequestLegacyExternalStorage;
- }
-
- /**
- * Indicates whether this application has declared its user data as fragile, causing the
- * system to prompt the user on whether to keep the user data on uninstall.
- */
- @DataClass.Generated.Member
- public boolean isUserDataFragile() {
- return mUserDataFragile;
- }
-
- /**
- * Indicates whether this application's data will be cleared on a failed restore.
- */
- @DataClass.Generated.Member
- public boolean isClearUserDataOnFailedRestoreAllowed() {
- return mClearUserDataOnFailedRestoreAllowed;
+ public @Nullable ArchivedPackageParcel getArchivedPackage() {
+ return mArchivedPackage;
}
@DataClass.Generated(
- time = 1693513509013L,
+ time = 1694792109463L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mBackupAllowed\nprivate final boolean mDefaultToDeviceProtectedStorage\nprivate final boolean mRequestLegacyExternalStorage\nprivate final boolean mUserDataFragile\nprivate final boolean mClearUserDataOnFailedRestoreAllowed\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 066ff689..5f86742 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -40,7 +40,6 @@
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
@@ -447,13 +446,6 @@
int overlayPriority = 0;
int rollbackDataPolicy = 0;
- boolean clearUserDataAllowed = true;
- boolean backupAllowed = true;
- boolean defaultToDeviceProtectedStorage = false;
- String requestLegacyExternalStorage = null;
- boolean userDataFragile = false;
- boolean clearUserDataOnFailedRestoreAllowed = true;
-
String requiredSystemPropertyName = null;
String requiredSystemPropertyValue = null;
@@ -493,22 +485,6 @@
useEmbeddedDex = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"useEmbeddedDex", false);
- clearUserDataAllowed = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
- "allowClearUserDataOnFailedRestore", true);
- backupAllowed = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
- "allowBackup", true);
- defaultToDeviceProtectedStorage = parser.getAttributeBooleanValue(
- ANDROID_RES_NAMESPACE,
- "defaultToDeviceProtectedStorage", false);
- userDataFragile = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
- "hasFragileUserData", false);
- clearUserDataOnFailedRestoreAllowed = parser.getAttributeBooleanValue(
- ANDROID_RES_NAMESPACE,
- "allowClearUserDataOnFailedRestore", true);
-
- requestLegacyExternalStorage = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
- "requestLegacyExternalStorage");
-
rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
"rollbackDataPolicy", 0);
String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
@@ -629,9 +605,6 @@
return input.skip(message);
}
- boolean isRequestLegacyExternalStorage = XmlUtils.convertValueToBoolean(
- requestLegacyExternalStorage, targetSdkVersion < Build.VERSION_CODES.Q);
-
return input.success(
new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode,
@@ -641,9 +614,7 @@
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary, clearUserDataAllowed, backupAllowed,
- defaultToDeviceProtectedStorage, isRequestLegacyExternalStorage,
- userDataFragile, clearUserDataOnFailedRestoreAllowed));
+ hasDeviceAdminReceiver, isSdkLibrary));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index ccef9de..116dd1f 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.PackageInfo;
import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
@@ -112,29 +113,11 @@
* Indicates if this package is a sdk.
*/
private final boolean mIsSdkLibrary;
+
/**
- * Set to <code>false</code> if the application does not wish to permit any OS-driven
- * backups of its data; <code>true</code> otherwise.
+ * Archival install info.
*/
- private final boolean mBackupAllowed;
- /**
- * When set, the default data storage directory for this app is pointed at
- * the device-protected location.
- */
- private final boolean mDefaultToDeviceProtectedStorage;
- /**
- * If {@code true} this app requests full external storage access.
- */
- private final boolean mRequestLegacyExternalStorage;
- /**
- * Indicates whether this application has declared its user data as fragile, causing the
- * system to prompt the user on whether to keep the user data on uninstall.
- */
- private final boolean mUserDataFragile;
- /**
- * Indicates whether this application's data will be cleared on a failed restore.
- */
- private final boolean mClearUserDataOnFailedRestoreAllowed;
+ private final @Nullable ArchivedPackageParcel mArchivedPackage;
public PackageLite(String path, String baseApkPath, ApkLite baseApk,
String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
@@ -171,11 +154,7 @@
mSplitApkPaths = splitApkPaths;
mSplitRevisionCodes = splitRevisionCodes;
mTargetSdk = targetSdk;
- mBackupAllowed = baseApk.isBackupAllowed();
- mDefaultToDeviceProtectedStorage = baseApk.isDefaultToDeviceProtectedStorage();
- mRequestLegacyExternalStorage = baseApk.isRequestLegacyExternalStorage();
- mUserDataFragile = baseApk.isUserDataFragile();
- mClearUserDataOnFailedRestoreAllowed = baseApk.isClearUserDataOnFailedRestoreAllowed();
+ mArchivedPackage = baseApk.getArchivedPackage();
}
/**
@@ -455,53 +434,18 @@
}
/**
- * Set to <code>false</code> if the application does not wish to permit any OS-driven
- * backups of its data; <code>true</code> otherwise.
+ * Archival install info.
*/
@DataClass.Generated.Member
- public boolean isBackupAllowed() {
- return mBackupAllowed;
- }
-
- /**
- * When set, the default data storage directory for this app is pointed at
- * the device-protected location.
- */
- @DataClass.Generated.Member
- public boolean isDefaultToDeviceProtectedStorage() {
- return mDefaultToDeviceProtectedStorage;
- }
-
- /**
- * If {@code true} this app requests full external storage access.
- */
- @DataClass.Generated.Member
- public boolean isRequestLegacyExternalStorage() {
- return mRequestLegacyExternalStorage;
- }
-
- /**
- * Indicates whether this application has declared its user data as fragile, causing the
- * system to prompt the user on whether to keep the user data on uninstall.
- */
- @DataClass.Generated.Member
- public boolean isUserDataFragile() {
- return mUserDataFragile;
- }
-
- /**
- * Indicates whether this application's data will be cleared on a failed restore.
- */
- @DataClass.Generated.Member
- public boolean isClearUserDataOnFailedRestoreAllowed() {
- return mClearUserDataOnFailedRestoreAllowed;
+ public @Nullable ArchivedPackageParcel getArchivedPackage() {
+ return mArchivedPackage;
}
@DataClass.Generated(
- time = 1693513525097L,
+ time = 1694792176268L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final boolean mBackupAllowed\nprivate final boolean mDefaultToDeviceProtectedStorage\nprivate final boolean mRequestLegacyExternalStorage\nprivate final boolean mUserDataFragile\nprivate final boolean mClearUserDataOnFailedRestoreAllowed\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS
index a7bce12..141d58d 100644
--- a/core/java/android/content/res/OWNERS
+++ b/core/java/android/content/res/OWNERS
@@ -1,8 +1,7 @@
# Bug component: 568761
-toddke@android.com
-toddke@google.com
patb@google.com
zyy@google.com
+branliu@google.com
per-file FontScaleConverter*=fuego@google.com
\ No newline at end of file
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index eedb25b..20771af 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
+import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -117,6 +118,52 @@
}
/**
+ * Returns a list of candidate credentials returned from credential manager providers
+ *
+ * @param request the request specifying type(s) of credentials to get from the
+ * credential providers
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param callback the callback invoked when the request succeeds or fails
+ *
+ * @hide
+ */
+ @Hide
+ public void getCandidateCredentials(
+ @NonNull GetCredentialRequest request,
+ @NonNull String callingPackage,
+ @Nullable CancellationSignal cancellationSignal,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> callback
+ ) {
+ requireNonNull(request, "request must not be null");
+ requireNonNull(callingPackage, "callingPackage must not be null");
+ requireNonNull(executor, "executor must not be null");
+ requireNonNull(callback, "callback must not be null");
+
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ Log.w(TAG, "getCandidateCredentials already canceled");
+ return;
+ }
+
+ ICancellationSignal cancelRemote = null;
+ try {
+ cancelRemote =
+ mService.getCandidateCredentials(
+ request,
+ new GetCandidateCredentialsTransport(executor, callback),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ if (cancellationSignal != null && cancelRemote != null) {
+ cancellationSignal.setRemote(cancelRemote);
+ }
+ }
+
+ /**
* Launches the necessary flows to retrieve an app credential from the user.
*
* <p>The execution can potentially launch UI flows to collect user consent to using a
@@ -641,6 +688,44 @@
}
}
+ private static class GetCandidateCredentialsTransport
+ extends IGetCandidateCredentialsCallback.Stub {
+
+ private final Executor mExecutor;
+ private final OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> mCallback;
+
+ private GetCandidateCredentialsTransport(
+ Executor executor,
+ OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResponse(GetCandidateCredentialsResponse response) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onResult(response));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onError(String errorType, String message) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallback.onError(new GetCandidateCredentialsException(
+ errorType, message)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
// TODO: listen for cancellation to release callback.
diff --git a/core/java/android/credentials/GetCandidateCredentialsException.java b/core/java/android/credentials/GetCandidateCredentialsException.java
new file mode 100644
index 0000000..40650d0
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsException.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents an error encountered during the
+ * {@link CredentialManager#getCandidateCredentials} operation.
+ *
+ * @hide
+ */
+@Hide
+public class GetCandidateCredentialsException extends Exception {
+ /**
+ * The error type value for when the given operation failed due to an unknown reason.
+ */
+ @NonNull
+ public static final String TYPE_UNKNOWN =
+ "android.credentials.GetCandidateCredentialsException.TYPE_UNKNOWN";
+
+ /**
+ * The error type value for when no credential is found available for the given {@link
+ * CredentialManager#getCandidateCredentials} request.
+ */
+ @NonNull
+ public static final String TYPE_NO_CREDENTIAL =
+ "android.credentials.GetCandidateCredentialsException.TYPE_NO_CREDENTIAL";
+
+ @NonNull
+ private final String mType;
+
+ /** Returns the specific exception type. */
+ @NonNull
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type, @Nullable String message) {
+ this(type, message, null);
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(
+ @NonNull String type, @Nullable String message, @Nullable Throwable cause) {
+ super(message, cause);
+ this.mType = Preconditions.checkStringNotEmpty(type,
+ "type must not be empty");
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type, @Nullable Throwable cause) {
+ this(type, null, cause);
+ }
+
+ /**
+ * Constructs a {@link GetCandidateCredentialsException}.
+ *
+ * @throws IllegalArgumentException If type is empty.
+ */
+ public GetCandidateCredentialsException(@NonNull String type) {
+ this(type, null, null);
+ }
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.aidl b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
new file mode 100644
index 0000000..d361089
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 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.credentials;
+
+parcelable GetCandidateCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.java b/core/java/android/credentials/GetCandidateCredentialsRequest.java
new file mode 100644
index 0000000..7f0dcaf
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A request to retrieve a list of candidate credentials against the list of credential
+ * options
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsRequest implements Parcelable {
+
+ /**
+ * The list of credential requests.
+ */
+ @NonNull
+ private final List<CredentialOption> mCredentialOptions;
+
+ /**
+ * The top request level data.
+ */
+ @NonNull
+ private final Bundle mData;
+
+ /**
+ * The origin of the calling app. Callers of this special API (e.g. browsers)
+ * can set this origin for an app different from their own, to be able to get credentials
+ * on behalf of that app.
+ */
+ @Nullable
+ private String mOrigin;
+
+ /**
+ * Returns the list of credential options to be requested.
+ */
+ @NonNull
+ public List<CredentialOption> getCredentialOptions() {
+ return mCredentialOptions;
+ }
+
+ /**
+ * Returns the top request level data.
+ */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the origin of the calling app if set otherwise returns null.
+ */
+ @Nullable
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mCredentialOptions, flags);
+ dest.writeBundle(mData);
+ dest.writeString8(mOrigin);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "GetCandidateCredentialsRequest {credentialOption=" + mCredentialOptions
+ + ", data=" + mData
+ + ", origin=" + mOrigin
+ + "}";
+ }
+
+ private GetCandidateCredentialsRequest(@NonNull List<CredentialOption> credentialOptions,
+ @NonNull Bundle data, String origin) {
+ Preconditions.checkCollectionNotEmpty(
+ credentialOptions,
+ /*valueName=*/ "credentialOptions");
+ Preconditions.checkCollectionElementsNotNull(
+ credentialOptions,
+ /*valueName=*/ "credentialOptions");
+ mCredentialOptions = credentialOptions;
+ mData = requireNonNull(data,
+ "data must not be null");
+ mOrigin = origin;
+ }
+
+ private GetCandidateCredentialsRequest(@NonNull Parcel in) {
+ List<CredentialOption> credentialOptions = new ArrayList<CredentialOption>();
+ in.readTypedList(credentialOptions, CredentialOption.CREATOR);
+ mCredentialOptions = credentialOptions;
+ AnnotationValidations.validate(NonNull.class, null, mCredentialOptions);
+
+ Bundle data = in.readBundle();
+ mData = data;
+ AnnotationValidations.validate(NonNull.class, null, mData);
+
+ mOrigin = in.readString8();
+ }
+
+ @NonNull
+ public static final Creator<GetCandidateCredentialsRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public GetCandidateCredentialsRequest[] newArray(int size) {
+ return new GetCandidateCredentialsRequest[size];
+ }
+
+ @Override
+ public GetCandidateCredentialsRequest createFromParcel(@NonNull Parcel in) {
+ return new GetCandidateCredentialsRequest(in);
+ }
+ };
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.aidl b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
new file mode 100644
index 0000000..ffcd3e7
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 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.credentials;
+
+parcelable GetCandidateCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
new file mode 100644
index 0000000..231e4bc
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.credentials.ui.GetCredentialProviderData;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of candidate credentials.
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsResponse implements Parcelable {
+ // TODO(b/299321990): Add members
+
+ @NonNull
+ private final List<GetCredentialProviderData> mCandidateProviderDataList;
+
+ /**
+ * @hide
+ */
+ @Hide
+ public GetCandidateCredentialsResponse(
+ List<GetCredentialProviderData> candidateProviderDataList
+ ) {
+ Preconditions.checkCollectionNotEmpty(
+ candidateProviderDataList,
+ /*valueName=*/ "candidateProviderDataList");
+ mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+ }
+
+ protected GetCandidateCredentialsResponse(Parcel in) {
+ List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+ in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
+ mCandidateProviderDataList = candidateProviderDataList;
+
+ AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mCandidateProviderDataList);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GetCandidateCredentialsResponse> CREATOR =
+ new Creator<>() {
+ @Override
+ public GetCandidateCredentialsResponse createFromParcel(Parcel in) {
+ return new GetCandidateCredentialsResponse(in);
+ }
+
+ @Override
+ public GetCandidateCredentialsResponse[] newArray(int size) {
+ return new GetCandidateCredentialsResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index dec729f..d081576 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -21,12 +21,14 @@
import android.credentials.CredentialProviderInfo;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
+import android.credentials.GetCandidateCredentialsRequest;
import android.credentials.GetCredentialRequest;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
+import android.credentials.IGetCandidateCredentialsCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.content.ComponentName;
@@ -45,6 +47,8 @@
@nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
+ @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
void setEnabledProviders(in List<String> primaryProviders, in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
diff --git a/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
new file mode 100644
index 0000000..729176a
--- /dev/null
+++ b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.app.PendingIntent;
+import android.credentials.GetCandidateCredentialsResponse;
+
+/**
+ * Listener for a getCandidateCredentials request.
+ *
+ * @hide
+ */
+interface IGetCandidateCredentialsCallback {
+ oneway void onResponse(in GetCandidateCredentialsResponse response);
+ oneway void onError(String errorType, String message);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index 1c2cbbc..b1d6ac4 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -64,6 +64,7 @@
private ArrayList<CameraStreamStats> mStreamStats;
private String mUserTag;
private int mVideoStabilizationMode;
+ private boolean mUsedUltraWide;
private int mSessionIndex;
private CameraExtensionSessionStats mCameraExtensionSessionStats;
@@ -82,6 +83,7 @@
mDeviceError = false;
mStreamStats = new ArrayList<CameraStreamStats>();
mVideoStabilizationMode = -1;
+ mUsedUltraWide = false;
mSessionIndex = 0;
mCameraExtensionSessionStats = new CameraExtensionSessionStats();
}
@@ -102,6 +104,8 @@
mSessionType = sessionType;
mInternalReconfigure = internalReconfigure;
mStreamStats = new ArrayList<CameraStreamStats>();
+ mVideoStabilizationMode = -1;
+ mUsedUltraWide = false;
mSessionIndex = sessionIdx;
mCameraExtensionSessionStats = new CameraExtensionSessionStats();
}
@@ -147,6 +151,7 @@
dest.writeTypedList(mStreamStats);
dest.writeString(mUserTag);
dest.writeInt(mVideoStabilizationMode);
+ dest.writeBoolean(mUsedUltraWide);
dest.writeInt(mSessionIndex);
mCameraExtensionSessionStats.writeToParcel(dest, 0);
}
@@ -173,6 +178,9 @@
mUserTag = in.readString();
mVideoStabilizationMode = in.readInt();
+
+ mUsedUltraWide = in.readBoolean();
+
mSessionIndex = in.readInt();
mCameraExtensionSessionStats = CameraExtensionSessionStats.CREATOR.createFromParcel(in);
}
@@ -245,6 +253,10 @@
return mVideoStabilizationMode;
}
+ public boolean getUsedUltraWide() {
+ return mUsedUltraWide;
+ }
+
public int getSessionIndex() {
return mSessionIndex;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 257ad71..5b24fb6 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -218,7 +218,8 @@
FINGERPRINT_ACQUIRED_UNKNOWN,
FINGERPRINT_ACQUIRED_IMMOBILE,
FINGERPRINT_ACQUIRED_TOO_BRIGHT,
- FINGERPRINT_ACQUIRED_POWER_PRESSED})
+ FINGERPRINT_ACQUIRED_POWER_PRESSED,
+ FINGERPRINT_ACQUIRED_RE_ENROLL})
@Retention(RetentionPolicy.SOURCE)
@interface FingerprintAcquired {}
@@ -310,6 +311,12 @@
int FINGERPRINT_ACQUIRED_POWER_PRESSED = 11;
/**
+ * This message is sent to encourage the user to re-enroll their fingerprints.
+ * @hide
+ */
+ int FINGERPRINT_ACQUIRED_RE_ENROLL = 12;
+
+ /**
* @hide
*/
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
new file mode 100644
index 0000000..66429e5
--- /dev/null
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.hardware.biometrics"
+
+flag {
+ name: "add_key_agreement_crypto_object"
+ namespace: "biometrics"
+ description: "Feature flag for adding KeyAgreement api to CryptoObject."
+ bug: "282058146"
+}
+
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c3fae55..88d7231 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,6 +41,7 @@
import android.view.InputEvent;
import android.view.InputMonitor;
import android.view.PointerIcon;
+import android.view.KeyCharacterMap;
import android.view.VerifiedInputEvent;
/** @hide */
@@ -63,6 +64,8 @@
// active keyboard layout.
int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode);
+ KeyCharacterMap getKeyCharacterMap(String layoutDescriptor);
+
// Temporarily changes the pointer speed.
void tryPointerSpeed(int speed);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a0cceae..ff1a6ac 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,8 @@
package android.hardware.input;
+import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
+
import android.Manifest;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -31,6 +33,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.hardware.BatteryState;
import android.os.Build;
import android.os.Handler;
@@ -931,6 +934,31 @@
}
/**
+ * Provides a Keyboard layout preview of a particular dimension.
+ *
+ * @param keyboardLayout Layout whose preview is requested. If null, will return preview of
+ * the default Keyboard layout defined by {@code Generic.kl}.
+ * @param width Expected width of the drawable
+ * @param height Expected height of the drawable
+ *
+ * NOTE: Width and height will auto-adjust to the width and height of the ImageView that
+ * shows the drawable but this allows the caller to provide an intrinsic width and height of
+ * the drawable allowing the ImageView to properly wrap the drawable content.
+ *
+ * @hide
+ */
+ @Nullable
+ public Drawable getKeyboardLayoutPreview(@Nullable KeyboardLayout keyboardLayout, int width,
+ int height) {
+ if (!keyboardLayoutPreviewFlag()) {
+ return null;
+ }
+ PhysicalKeyLayout keyLayout = new PhysicalKeyLayout(
+ mGlobal.getKeyCharacterMap(keyboardLayout), keyboardLayout);
+ return new KeyboardLayoutPreviewDrawable(mContext, keyLayout, width, height);
+ }
+
+ /**
* Injects an input event into the event system, targeting windows owned by the provided uid.
*
* If a valid targetUid is provided, the system will only consider injecting the input event
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index e886f68..8c598ae 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -51,6 +51,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
import android.view.PointerIcon;
import com.android.internal.annotations.GuardedBy;
@@ -1206,6 +1207,21 @@
}
/**
+ * Returns KeyCharacterMap for the provided Keyboard layout. If provided layout is null it will
+ * return KeyCharacter map for the default layout {@code Generic.kl}.
+ */
+ public KeyCharacterMap getKeyCharacterMap(@Nullable KeyboardLayout keyboardLayout) {
+ if (keyboardLayout == null) {
+ return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ }
+ try {
+ return mIm.getKeyCharacterMap(keyboardLayout.getDescriptor());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @see InputManager#injectInputEvent(InputEvent, int, int)
*/
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 4403251..bbfed24 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -22,6 +22,7 @@
import android.os.Parcelable;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -230,6 +231,33 @@
return mProductId;
}
+ /**
+ * Returns if the Keyboard layout follows the ANSI Physical key layout.
+ */
+ public boolean isAnsiLayout() {
+ for (int i = 0; i < mLocales.size(); i++) {
+ Locale locale = mLocales.get(i);
+ if (locale != null && locale.getCountry().equalsIgnoreCase("us")
+ && mLayoutType != LayoutType.EXTENDED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns if the Keyboard layout follows the JIS Physical key layout.
+ */
+ public boolean isJisLayout() {
+ for (int i = 0; i < mLocales.size(); i++) {
+ Locale locale = mLocales.get(i);
+ if (locale != null && locale.getCountry().equalsIgnoreCase("jp")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
new file mode 100644
index 0000000..d943c37
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright 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.hardware.input;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom drawable class that draws preview of a Physical keyboard layout.
+ */
+final class KeyboardLayoutPreviewDrawable extends Drawable {
+
+ private static final String TAG = "KeyboardLayoutPreview";
+ private static final int GRAVITY_LEFT = 0x1;
+ private static final int GRAVITY_RIGHT = 0x2;
+ private static final int GRAVITY_TOP = 0x4;
+ private static final int GRAVITY_BOTTOM = 0x8;
+ private static final int GRAVITY_CENTER =
+ GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
+ private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+ private static final int KEY_PADDING_IN_DP = 3;
+ private static final int KEYBOARD_PADDING_IN_DP = 10;
+ private static final int KEY_RADIUS_IN_DP = 5;
+ private static final int KEYBOARD_RADIUS_IN_DP = 10;
+ private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+
+ private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
+
+ private final int mWidth;
+ private final int mHeight;
+ private final RectF mKeyboardBackground = new RectF();
+ private final ResourceProvider mResourceProvider;
+ private final PhysicalKeyLayout mKeyLayout;
+
+ public KeyboardLayoutPreviewDrawable(Context context, PhysicalKeyLayout keyLayout, int width,
+ int height) {
+ mWidth = width;
+ mHeight = height;
+ mResourceProvider = new ResourceProvider(context);
+ mKeyLayout = keyLayout;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mHeight;
+ }
+
+ @Override
+ protected void onBoundsChange(@NonNull Rect bounds) {
+ super.onBoundsChange(bounds);
+ mKeyDrawables.clear();
+ final PhysicalKeyLayout.LayoutKey[][] keys = mKeyLayout.getKeys();
+ if (keys == null) {
+ return;
+ }
+ final PhysicalKeyLayout.EnterKey enterKey = mKeyLayout.getEnterKey();
+ int width = bounds.width();
+ int height = bounds.height();
+ final int keyboardPadding = mResourceProvider.getKeyboardPadding();
+ final int keyPadding = mResourceProvider.getKeyPadding();
+ final float keyRadius = mResourceProvider.getKeyRadius();
+ mKeyboardBackground.set(0, 0, width, height);
+ width -= keyboardPadding * 2;
+ height -= keyboardPadding * 2;
+ if (width <= 0 || height <= 0) {
+ Slog.e(TAG, "Invalid width and height to draw layout preview, width = " + width
+ + ", height = " + height);
+ return;
+ }
+ int rowCount = keys.length;
+ float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+ float isoEnterKeyLeft = 0;
+ float isoEnterKeyTop = 0;
+ float isoEnterWidthUnit = 0;
+ for (int i = 0; i < rowCount; i++) {
+ PhysicalKeyLayout.LayoutKey[] row = keys[i];
+ float totalRowWeight = 0;
+ int keysInRow = row.length;
+ for (PhysicalKeyLayout.LayoutKey layoutKey : row) {
+ totalRowWeight += layoutKey.keyWeight();
+ }
+ float keyWidthInPx = (width - keysInRow * 2 * keyPadding) / totalRowWeight;
+ float rowWeightOnLeft = 0;
+ float top = keyboardPadding + keyPadding * (2 * i + 1) + i * keyHeight;
+ for (int j = 0; j < keysInRow; j++) {
+ float left =
+ keyboardPadding + keyPadding * (2 * j + 1) + rowWeightOnLeft * keyWidthInPx;
+ rowWeightOnLeft += row[j].keyWeight();
+ RectF keyRect = new RectF(left, top, left + keyWidthInPx * row[j].keyWeight(),
+ top + keyHeight);
+ if (enterKey != null && row[j].keyCode() == KeyEvent.KEYCODE_ENTER) {
+ if (enterKey.row() == i && enterKey.column() == j) {
+ isoEnterKeyLeft = keyRect.left;
+ isoEnterKeyTop = keyRect.top;
+ isoEnterWidthUnit = keyWidthInPx;
+ }
+ continue;
+ }
+ if (PhysicalKeyLayout.isSpecialKey(row[j])) {
+ mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+ mResourceProvider.getSpecialKeyPaint(),
+ mResourceProvider.getSpecialKeyPaint(),
+ mResourceProvider.getSpecialKeyPaint()));
+ } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
+ mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
+ keyRadius, mResourceProvider.getTypingKeyPaint(),
+ mResourceProvider.getPrimaryGlyphPaint(),
+ mResourceProvider.getSecondaryGlyphPaint()));
+ } else {
+ mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+ mResourceProvider.getTypingKeyPaint(),
+ mResourceProvider.getPrimaryGlyphPaint(),
+ mResourceProvider.getSecondaryGlyphPaint()));
+ }
+ }
+ }
+ if (enterKey != null) {
+ IsoEnterKey.Builder isoEnterKeyBuilder = new IsoEnterKey.Builder(keyRadius,
+ mResourceProvider.getSpecialKeyPaint());
+ isoEnterKeyBuilder.setTopWidth(enterKey.topKeyWeight() * isoEnterWidthUnit)
+ .setStartPoint(isoEnterKeyLeft, isoEnterKeyTop)
+ .setVerticalEdges(keyHeight, 2 * (keyHeight + keyPadding))
+ .setBottomWidth(enterKey.bottomKeyWeight() * isoEnterWidthUnit);
+ mKeyDrawables.add(isoEnterKeyBuilder.build());
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final float keyboardRadius = mResourceProvider.getBackgroundRadius();
+ canvas.drawRoundRect(mKeyboardBackground, keyboardRadius, keyboardRadius,
+ mResourceProvider.getBackgroundPaint());
+ for (KeyDrawable key : mKeyDrawables) {
+ key.draw(canvas);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // Do nothing
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ // Do nothing
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ private static class TypingKey implements KeyDrawable {
+
+ private final RectF mKeyRect;
+ private final float mKeyRadius;
+ private final Paint mKeyPaint;
+ private final Paint mBaseTextPaint;
+ private final Paint mModifierTextPaint;
+ private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
+
+ private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
+ float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+ mKeyRect = keyRect;
+ mKeyRadius = keyRadius;
+ mKeyPaint = keyPaint;
+ mBaseTextPaint = baseTextPaint;
+ mModifierTextPaint = modifierTextPaint;
+ initGlyphs(glyphData);
+ }
+
+ private void initGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+ createGlyphs(glyphData);
+ measureGlyphs();
+ }
+
+ private void createGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+ if (glyphData == null) {
+ return;
+ }
+ if (!glyphData.hasBaseText()) {
+ return;
+ }
+ if (glyphData.hasValidShiftText() && glyphData.hasValidAltGrText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+ GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+ } else if (glyphData.hasValidShiftText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_CENTER_HORIZONTAL, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+ GRAVITY_TOP | GRAVITY_CENTER_HORIZONTAL, mModifierTextPaint));
+ } else if (glyphData.hasValidAltGrText()) {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+ GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+ } else {
+ mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+ GRAVITY_CENTER, mBaseTextPaint));
+ }
+ }
+
+ private void measureGlyphs() {
+ float keyWidth = mKeyRect.width();
+ float keyHeight = mKeyRect.height();
+ for (GlyphDrawable glyph : mGlyphDrawables) {
+ float centerX = keyWidth / 2;
+ float centerY = keyHeight / 2;
+ if ((glyph.gravity & GRAVITY_LEFT) != 0) {
+ centerX -= keyWidth / 4;
+ }
+ if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
+ centerX += keyWidth / 4;
+ }
+ if ((glyph.gravity & GRAVITY_TOP) != 0) {
+ centerY -= keyHeight / 4;
+ }
+ if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
+ centerY += keyHeight / 4;
+ }
+ Rect textBounds = new Rect();
+ glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
+ float textWidth = textBounds.width();
+ float textHeight = textBounds.height();
+ glyph.rect.set(centerX - textWidth / 2, centerY - textHeight / 2 - textBounds.top,
+ centerX + textWidth / 2, centerY + textHeight / 2 - textBounds.top);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawRoundRect(mKeyRect, mKeyRadius, mKeyRadius, mKeyPaint);
+ for (GlyphDrawable glyph : mGlyphDrawables) {
+ float textWidth = glyph.rect.width();
+ float textHeight = glyph.rect.height();
+ float keyWidth = mKeyRect.width();
+ float keyHeight = mKeyRect.height();
+ if (textWidth == 0 || textHeight == 0 || keyWidth == 0 || keyHeight == 0) {
+ return;
+ }
+ canvas.drawText(glyph.text, 0, glyph.text.length(), mKeyRect.left + glyph.rect.left,
+ mKeyRect.top + glyph.rect.top, glyph.paint);
+ }
+ }
+ }
+
+ private static class UnsureTypingKey extends TypingKey {
+
+ private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
+ RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
+ Paint modifierTextPaint) {
+ super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+ createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
+ }
+ }
+
+ private static class IsoEnterKey implements KeyDrawable {
+
+ private final Paint mKeyPaint;
+ private final Path mPath;
+
+ private IsoEnterKey(Paint keyPaint, @NonNull Path path) {
+ mKeyPaint = keyPaint;
+ mPath = path;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawPath(mPath, mKeyPaint);
+ }
+
+ private static class Builder {
+ private final float mKeyRadius;
+ private final Paint mKeyPaint;
+ private float mLeft;
+ private float mTop;
+ private float mTopWidth;
+ private float mBottomWidth;
+ private float mLeftHeight;
+ private float mRightHeight;
+
+ private Builder(float keyRadius, Paint keyPaint) {
+ mKeyRadius = keyRadius;
+ mKeyPaint = keyPaint;
+ }
+
+ private Builder setStartPoint(float left, float top) {
+ mLeft = left;
+ mTop = top;
+ return this;
+ }
+
+ private Builder setTopWidth(float width) {
+ mTopWidth = width;
+ return this;
+ }
+
+ private Builder setBottomWidth(float width) {
+ mBottomWidth = width;
+ return this;
+ }
+
+ private Builder setVerticalEdges(float leftHeight, float rightHeight) {
+ mLeftHeight = leftHeight;
+ mRightHeight = rightHeight;
+ return this;
+ }
+
+ private IsoEnterKey build() {
+ Path enterKey = new Path();
+ RectF oval = new RectF(-mKeyRadius, -mKeyRadius, mKeyRadius, mKeyRadius);
+ // Horizontal top line
+ enterKey.moveTo(mLeft + mKeyRadius, mTop);
+ enterKey.lineTo(mLeft + mTopWidth - mKeyRadius, mTop);
+ // Rounded top right corner
+ oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius, mTop);
+ enterKey.arcTo(oval, 270, 90);
+ // Vertical right line
+ enterKey.lineTo(mLeft + mTopWidth, mTop + mRightHeight - mKeyRadius);
+ // Rounded bottom right corner
+ oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius,
+ mTop + mRightHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 0, 90);
+ // Horizontal bottom line
+ enterKey.lineTo(mLeft + mTopWidth - mBottomWidth + mKeyRadius, mTop + mRightHeight);
+ // Rounded bottom left corner
+ oval.offsetTo(mLeft + mTopWidth - mBottomWidth,
+ mTop + mRightHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 90, 90);
+ // Vertical left line (bottom half)
+ enterKey.lineTo(mLeft + mTopWidth - mBottomWidth, mTop + mLeftHeight - mKeyRadius);
+ // Rounded corner
+ oval.offsetTo(mLeft + mTopWidth - mBottomWidth - 2 * mKeyRadius,
+ mTop + mLeftHeight);
+ enterKey.arcTo(oval, 0, -90);
+ // Horizontal line in the mid part
+ enterKey.lineTo(mLeft + mKeyRadius, mTop + mLeftHeight);
+ // Rounded corner
+ oval.offsetTo(mLeft, mTop + mLeftHeight - 2 * mKeyRadius);
+ enterKey.arcTo(oval, 90, 90);
+ // Vertical left line (top half)
+ enterKey.lineTo(mLeft, mTop + mKeyRadius);
+ // Rounded top left corner
+ oval.offsetTo(mLeft, mTop);
+ enterKey.arcTo(oval, 180, 90);
+ enterKey.close();
+ return new IsoEnterKey(mKeyPaint, enterKey);
+ }
+ }
+ }
+
+ private record GlyphDrawable(String text, RectF rect, int gravity, Paint paint) {}
+
+ private interface KeyDrawable {
+ void draw(Canvas canvas);
+ }
+
+ private static class ResourceProvider {
+ // Resources
+ private final Paint mBackgroundPaint;
+ private final Paint mTypingKeyPaint;
+ private final Paint mSpecialKeyPaint;
+ private final Paint mPrimaryGlyphPaint;
+ private final Paint mSecondaryGlyphPaint;
+ private final int mKeyPadding;
+ private final int mKeyboardPadding;
+ private final float mKeyRadius;
+ private final float mBackgroundRadius;
+
+ private ResourceProvider(Context context) {
+ mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEY_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+ mKeyboardPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEYBOARD_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+ mKeyRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+ mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+ int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+ boolean isDark = (context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ int typingKeyColor = context.getColor(
+ isDark ? android.R.color.system_outline_variant_dark
+ : android.R.color.system_surface_container_lowest_light);
+ int specialKeyColor = context.getColor(isDark ? android.R.color.system_neutral1_800
+ : android.R.color.system_secondary_container_light);
+ int primaryGlyphColor = context.getColor(isDark ? android.R.color.system_on_surface_dark
+ : android.R.color.system_on_surface_light);
+ int secondaryGlyphColor = context.getColor(isDark ? android.R.color.system_outline_dark
+ : android.R.color.system_outline_light);
+ int backgroundColor = context.getColor(
+ isDark ? android.R.color.system_surface_container_dark
+ : android.R.color.system_surface_container_light);
+ mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+ Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+ mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+ Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+ mTypingKeyPaint = createFillPaint(typingKeyColor);
+ mSpecialKeyPaint = createFillPaint(specialKeyColor);
+ mBackgroundPaint = createFillPaint(backgroundColor);
+ }
+
+ private Paint getBackgroundPaint() {
+ return mBackgroundPaint;
+ }
+
+ private Paint getTypingKeyPaint() {
+ return mTypingKeyPaint;
+ }
+
+ private Paint getSpecialKeyPaint() {
+ return mSpecialKeyPaint;
+ }
+
+ private Paint getPrimaryGlyphPaint() {
+ return mPrimaryGlyphPaint;
+ }
+
+ private Paint getSecondaryGlyphPaint() {
+ return mSecondaryGlyphPaint;
+ }
+
+ private int getKeyPadding() {
+ return mKeyPadding;
+ }
+
+ private int getKeyboardPadding() {
+ return mKeyboardPadding;
+ }
+
+ private float getKeyRadius() {
+ return mKeyRadius;
+ }
+
+ private float getBackgroundRadius() {
+ return mBackgroundRadius;
+ }
+ }
+
+ private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+ Paint paint = new Paint();
+ paint.setColor(textColor);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setTextSize(textSize);
+ paint.setTypeface(typeface);
+ return paint;
+ }
+
+ private static Paint createFillPaint(@ColorInt int color) {
+ Paint paint = new Paint();
+ paint.setColor(color);
+ paint.setStyle(Paint.Style.FILL);
+ return paint;
+ }
+
+ private static Paint createGreyedOutPaint(Paint paint) {
+ Paint result = new Paint(paint);
+ result.setAlpha(100);
+ return result;
+ }
+}
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
new file mode 100644
index 0000000..241c452
--- /dev/null
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright 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.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+import java.util.Locale;
+
+/**
+ * A complimentary class to {@link KeyboardLayoutPreviewDrawable} describing the physical key layout
+ * of a Physical keyboard and provides information regarding the scan codes produced by the physical
+ * keys.
+ */
+final class PhysicalKeyLayout {
+
+ private static final String TAG = "KeyboardLayoutPreview";
+ private static final int SCANCODE_1 = 2;
+ private static final int SCANCODE_2 = 3;
+ private static final int SCANCODE_3 = 4;
+ private static final int SCANCODE_4 = 5;
+ private static final int SCANCODE_5 = 6;
+ private static final int SCANCODE_6 = 7;
+ private static final int SCANCODE_7 = 8;
+ private static final int SCANCODE_8 = 9;
+ private static final int SCANCODE_9 = 10;
+ private static final int SCANCODE_0 = 11;
+ private static final int SCANCODE_MINUS = 12;
+ private static final int SCANCODE_EQUALS = 13;
+ private static final int SCANCODE_Q = 16;
+ private static final int SCANCODE_W = 17;
+ private static final int SCANCODE_E = 18;
+ private static final int SCANCODE_R = 19;
+ private static final int SCANCODE_T = 20;
+ private static final int SCANCODE_Y = 21;
+ private static final int SCANCODE_U = 22;
+ private static final int SCANCODE_I = 23;
+ private static final int SCANCODE_O = 24;
+ private static final int SCANCODE_P = 25;
+ private static final int SCANCODE_LEFT_BRACKET = 26;
+ private static final int SCANCODE_RIGHT_BRACKET = 27;
+ private static final int SCANCODE_A = 30;
+ private static final int SCANCODE_S = 31;
+ private static final int SCANCODE_D = 32;
+ private static final int SCANCODE_F = 33;
+ private static final int SCANCODE_G = 34;
+ private static final int SCANCODE_H = 35;
+ private static final int SCANCODE_J = 36;
+ private static final int SCANCODE_K = 37;
+ private static final int SCANCODE_L = 38;
+ private static final int SCANCODE_SEMICOLON = 39;
+ private static final int SCANCODE_APOSTROPHE = 40;
+ private static final int SCANCODE_GRAVE = 41;
+ private static final int SCANCODE_BACKSLASH1 = 43;
+ private static final int SCANCODE_Z = 44;
+ private static final int SCANCODE_X = 45;
+ private static final int SCANCODE_C = 46;
+ private static final int SCANCODE_V = 47;
+ private static final int SCANCODE_B = 48;
+ private static final int SCANCODE_N = 49;
+ private static final int SCANCODE_M = 50;
+ private static final int SCANCODE_COMMA = 51;
+ private static final int SCANCODE_PERIOD = 52;
+ private static final int SCANCODE_SLASH = 53;
+ private static final int SCANCODE_BACKSLASH2 = 86;
+ private static final int SCANCODE_YEN = 124;
+
+ private static final SparseIntArray DEFAULT_KEYCODE_FOR_SCANCODE = new SparseIntArray();
+
+ static {
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_1, KeyEvent.KEYCODE_1);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_2, KeyEvent.KEYCODE_2);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_3, KeyEvent.KEYCODE_3);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_4, KeyEvent.KEYCODE_4);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_5, KeyEvent.KEYCODE_5);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_6, KeyEvent.KEYCODE_6);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_7, KeyEvent.KEYCODE_7);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_8, KeyEvent.KEYCODE_8);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_9, KeyEvent.KEYCODE_9);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_0, KeyEvent.KEYCODE_0);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_MINUS, KeyEvent.KEYCODE_MINUS);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_EQUALS, KeyEvent.KEYCODE_EQUALS);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Q, KeyEvent.KEYCODE_Q);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_W, KeyEvent.KEYCODE_W);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_E, KeyEvent.KEYCODE_E);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_R, KeyEvent.KEYCODE_R);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_T, KeyEvent.KEYCODE_T);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Y, KeyEvent.KEYCODE_Y);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_U, KeyEvent.KEYCODE_U);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_I, KeyEvent.KEYCODE_I);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_O, KeyEvent.KEYCODE_O);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_P, KeyEvent.KEYCODE_P);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_LEFT_BRACKET, KeyEvent.KEYCODE_LEFT_BRACKET);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_RIGHT_BRACKET, KeyEvent.KEYCODE_RIGHT_BRACKET);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_A, KeyEvent.KEYCODE_A);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_S, KeyEvent.KEYCODE_S);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_D, KeyEvent.KEYCODE_D);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_F, KeyEvent.KEYCODE_F);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_G, KeyEvent.KEYCODE_G);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_H, KeyEvent.KEYCODE_H);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_J, KeyEvent.KEYCODE_J);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_K, KeyEvent.KEYCODE_K);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_L, KeyEvent.KEYCODE_L);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SEMICOLON, KeyEvent.KEYCODE_SEMICOLON);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_APOSTROPHE, KeyEvent.KEYCODE_APOSTROPHE);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_GRAVE, KeyEvent.KEYCODE_GRAVE);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH1, KeyEvent.KEYCODE_BACKSLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Z, KeyEvent.KEYCODE_Z);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_X, KeyEvent.KEYCODE_X);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_C, KeyEvent.KEYCODE_C);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_V, KeyEvent.KEYCODE_V);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_B, KeyEvent.KEYCODE_B);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_N, KeyEvent.KEYCODE_N);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_M, KeyEvent.KEYCODE_M);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_COMMA, KeyEvent.KEYCODE_COMMA);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_PERIOD, KeyEvent.KEYCODE_PERIOD);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SLASH, KeyEvent.KEYCODE_SLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH2, KeyEvent.KEYCODE_BACKSLASH);
+ DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_YEN, KeyEvent.KEYCODE_YEN);
+ }
+
+ private LayoutKey[][] mKeys = null;
+ private EnterKey mEnterKey = null;
+
+ public PhysicalKeyLayout(@NonNull KeyCharacterMap kcm, @Nullable KeyboardLayout layout) {
+ initLayoutKeys(kcm, layout);
+ }
+
+ private void initLayoutKeys(KeyCharacterMap kcm, KeyboardLayout layout) {
+ if (layout == null) {
+ createIsoLayout(kcm);
+ return;
+ }
+ if (layout.isAnsiLayout()) {
+ createAnsiLayout(kcm);
+ } else if (layout.isJisLayout()) {
+ createJisLayout(kcm);
+ } else {
+ createIsoLayout(kcm);
+ }
+ }
+
+ public LayoutKey[][] getKeys() {
+ return mKeys;
+ }
+
+ /**
+ * @return Special enter key (if required) that can span multiple rows like ISO enter key.
+ */
+ @Nullable
+ public EnterKey getEnterKey() {
+ return mEnterKey;
+ }
+
+ private void createAnsiLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+ getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(kcm, SCANCODE_BACKSLASH1)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CAPS_LOCK, 1.75F),
+ getKey(kcm, SCANCODE_A), getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D),
+ getKey(kcm, SCANCODE_F), getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H),
+ getKey(kcm, SCANCODE_J), getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.75F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 2.5F),
+ getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+ getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+ getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+ getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.5F),
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ }
+
+ private void createIsoLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+ getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+ getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+ getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+ getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(kcm, SCANCODE_BACKSLASH1),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+ getKey(kcm, SCANCODE_BACKSLASH2), getKey(kcm, SCANCODE_Z),
+ getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C), getKey(kcm, SCANCODE_V),
+ getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N), getKey(kcm, SCANCODE_M),
+ getKey(kcm, SCANCODE_COMMA), getKey(kcm, SCANCODE_PERIOD),
+ getKey(kcm, SCANCODE_SLASH),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+ }
+
+ private void createJisLayout(KeyCharacterMap kcm) {
+ mKeys = new LayoutKey[][]{
+ {
+ getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+ getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+ getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+ getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+ getKey(kcm, SCANCODE_MINUS, 0.8F), getKey(kcm, SCANCODE_EQUALS, 0.8f),
+ getKey(kcm, SCANCODE_YEN, 0.8f), getKey(KeyEvent.KEYCODE_DEL, 1.1F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+ getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+ getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+ getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+ getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+ getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+ getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+ getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+ getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+ getKey(kcm, SCANCODE_BACKSLASH2),
+ getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+ getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+ getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+ getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+ getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+ getKey(kcm, SCANCODE_BACKSLASH1),
+ getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+ },
+ {
+ getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_SPACE, 3.5F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+ getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+ getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+ getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+ }
+ };
+ mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+ }
+
+ private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode, float keyWeight) {
+ int keyCode = kcm.getMappedKeyOrDefault(scanCode,
+ DEFAULT_KEYCODE_FOR_SCANCODE.get(scanCode, KeyEvent.KEYCODE_UNKNOWN));
+ return new LayoutKey(keyCode, scanCode, keyWeight, new KeyGlyph(kcm, keyCode));
+ }
+
+ private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode) {
+ return getKey(kcm, scanCode, 1.0F);
+ }
+
+ private static String getKeyText(KeyCharacterMap kcm, int keyCode, int modifierState) {
+ if (isSpecialKey(keyCode)) {
+ return "";
+ }
+ int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ if (Character.isValidCodePoint(utf8Char)) {
+ return String.valueOf(Character.toChars(utf8Char)).toUpperCase(Locale.getDefault());
+ } else {
+ return String.valueOf(kcm.getDisplayLabel(keyCode)).toUpperCase(Locale.getDefault());
+ }
+ }
+
+ private static LayoutKey getKey(int keyCode, float keyWeight) {
+ return new LayoutKey(keyCode, keyCode, keyWeight, null);
+ }
+
+ /**
+ * Util function that tells if a key corresponds to a special key which are keys on a Physical
+ * layout that perform some special action like modifier keys, enter key, space key, character
+ * set changing keys, etc.
+ */
+ private static boolean isSpecialKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_TAB:
+ case KeyEvent.KEYCODE_CAPS_LOCK:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ case KeyEvent.KEYCODE_CTRL_LEFT:
+ case KeyEvent.KEYCODE_CTRL_RIGHT:
+ case KeyEvent.KEYCODE_FUNCTION:
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ case KeyEvent.KEYCODE_META_LEFT:
+ case KeyEvent.KEYCODE_META_RIGHT:
+ case KeyEvent.KEYCODE_SPACE:
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_UNKNOWN:
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isSpecialKey(LayoutKey key) {
+ return isSpecialKey(key.keyCode);
+ }
+
+ public static boolean isKeyPositionUnsure(LayoutKey key) {
+ switch (key.scanCode) {
+ case SCANCODE_GRAVE:
+ case SCANCODE_BACKSLASH1:
+ case SCANCODE_BACKSLASH2:
+ return true;
+ }
+ return false;
+ }
+
+ public record LayoutKey(int keyCode, int scanCode, float keyWeight, KeyGlyph glyph) {}
+ public record EnterKey(int row, int column, float topKeyWeight, float bottomKeyWeight) {}
+
+ public static class KeyGlyph {
+ private final String mBaseText;
+ private final String mShiftText;
+ private final String mAltGrText;
+
+ public KeyGlyph(KeyCharacterMap kcm, int keyCode) {
+ mBaseText = getKeyText(kcm, keyCode, 0);
+ mShiftText = getKeyText(kcm, keyCode,
+ KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON);
+ mAltGrText = getKeyText(kcm, keyCode,
+ KeyEvent.META_ALT_ON | KeyEvent.META_ALT_RIGHT_ON);
+ }
+
+ public String getBaseText() {
+ return mBaseText;
+ }
+
+ public String getShiftText() {
+ return mShiftText;
+ }
+
+ public String getAltGrText() {
+ return mAltGrText;
+ }
+
+ public boolean hasBaseText() {
+ return !TextUtils.isEmpty(mBaseText);
+ }
+
+ public boolean hasValidShiftText() {
+ return !TextUtils.isEmpty(mShiftText) && !TextUtils.equals(mBaseText, mShiftText);
+ }
+
+ public boolean hasValidAltGrText() {
+ return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 24d1c95..1b8d925 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -104,7 +104,11 @@
*/
void hide(boolean remove) {
if (getDecorView() != null) {
- getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
+ if (remove) {
+ mWindowManager.removeViewImmediate(getDecorView());
+ } else {
+ getDecorView().setVisibility(View.INVISIBLE);
+ }
}
}
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 7f6ec58..cf47786 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -16,14 +16,12 @@
package android.inputmethodservice;
-import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.app.Dialog;
-import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.util.Log;
@@ -45,7 +43,6 @@
private static final String TAG = "SoftInputWindow";
private final KeyEvent.DispatcherState mDispatcherState;
- private final Rect mBounds = new Rect();
private final InputMethodService mService;
@Retention(SOURCE)
@@ -150,22 +147,6 @@
}
@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- getWindow().getDecorView().getHitRect(mBounds);
-
- if (ev.isWithinBoundsNoHistory(mBounds.left, mBounds.top,
- mBounds.right - 1, mBounds.bottom - 1)) {
- return super.dispatchTouchEvent(ev);
- } else {
- MotionEvent temp = ev.clampNoHistory(mBounds.left, mBounds.top,
- mBounds.right - 1, mBounds.bottom - 1);
- boolean handled = super.dispatchTouchEvent(temp);
- temp.recycle();
- return handled;
- }
- }
-
- @Override
public void show() {
switch (mWindowState) {
case WindowState.TOKEN_PENDING:
@@ -273,7 +254,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- mBounds.dumpDebug(proto, BOUNDS);
proto.write(WINDOW_STATE, mWindowState);
proto.end(token);
}
diff --git a/core/java/android/nfc/NfcAntennaInfo.java b/core/java/android/nfc/NfcAntennaInfo.java
index d54fcd2..b002ca2 100644
--- a/core/java/android/nfc/NfcAntennaInfo.java
+++ b/core/java/android/nfc/NfcAntennaInfo.java
@@ -85,8 +85,8 @@
this.mDeviceHeight = in.readInt();
this.mDeviceFoldable = in.readByte() != 0;
this.mAvailableNfcAntennas = new ArrayList<>();
- in.readParcelableList(this.mAvailableNfcAntennas,
- AvailableNfcAntenna.class.getClassLoader());
+ in.readTypedList(this.mAvailableNfcAntennas,
+ AvailableNfcAntenna.CREATOR);
}
public static final @NonNull Parcelable.Creator<NfcAntennaInfo> CREATOR =
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 2436e57..958669e 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -16,7 +16,11 @@
package android.nfc.cardemulation;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.nfc.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -29,6 +33,11 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline module *
+ * *******************************************************************/
/**
* The AidGroup class represents a group of Application Identifiers (AIDs).
@@ -39,28 +48,30 @@
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public final class AidGroup implements Parcelable {
/**
* The maximum number of AIDs that can be present in any one group.
*/
- public static final int MAX_NUM_AIDS = 256;
+ private static final int MAX_NUM_AIDS = 256;
- static final String TAG = "AidGroup";
+ private static final String TAG = "AidGroup";
- @UnsupportedAppUsage
- final List<String> aids;
- @UnsupportedAppUsage
- final String category;
- @UnsupportedAppUsage
- final String description;
+
+ private final List<String> mAids;
+ private final String mCategory;
+ @SuppressWarnings("unused") // Unused as of now, but part of the XML input.
+ private final String mDescription;
/**
* Creates a new AidGroup object.
*
- * @param aids The list of AIDs present in the group
- * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
+ * @param aids list of AIDs present in the group
+ * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
*/
- public AidGroup(List<String> aids, String category) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public AidGroup(@NonNull List<String> aids, @Nullable String category) {
if (aids == null || aids.size() == 0) {
throw new IllegalArgumentException("No AIDS in AID group.");
}
@@ -73,45 +84,55 @@
}
}
if (isValidCategory(category)) {
- this.category = category;
+ this.mCategory = category;
} else {
- this.category = CardEmulation.CATEGORY_OTHER;
+ this.mCategory = CardEmulation.CATEGORY_OTHER;
}
- this.aids = new ArrayList<String>(aids.size());
+ this.mAids = new ArrayList<String>(aids.size());
for (String aid : aids) {
- this.aids.add(aid.toUpperCase());
+ this.mAids.add(aid.toUpperCase(Locale.US));
}
- this.description = null;
- }
-
- @UnsupportedAppUsage
- AidGroup(String category, String description) {
- this.aids = new ArrayList<String>();
- this.category = category;
- this.description = description;
+ this.mDescription = null;
}
/**
+ * Creates a new AidGroup object.
+ *
+ * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
+ * @param description description of this group
+ */
+ AidGroup(@NonNull String category, @NonNull String description) {
+ this.mAids = new ArrayList<String>();
+ this.mCategory = category;
+ this.mDescription = description;
+ }
+
+ /**
+ * Returns the category of this group.
* @return the category of this AID group
*/
- @UnsupportedAppUsage
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getCategory() {
- return category;
+ return mCategory;
}
/**
+ * Returns the list of AIDs in this group.
+ *
* @return the list of AIDs in this group
*/
- @UnsupportedAppUsage
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public List<String> getAids() {
- return aids;
+ return mAids;
}
@Override
public String toString() {
- StringBuilder out = new StringBuilder("Category: " + category +
- ", AIDs:");
- for (String aid : aids) {
+ StringBuilder out = new StringBuilder("Category: " + mCategory
+ + ", AIDs:");
+ for (String aid : mAids) {
out.append(aid);
out.append(", ");
}
@@ -119,41 +140,44 @@
}
/**
- * Dump debugging info as AidGroupProto
+ * Dump debugging info as AidGroupProto.
*
* If the output belongs to a sub message, the caller is responsible for wrapping this function
* between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
*
* @param proto the ProtoOutputStream to write to
*/
- public void dump(ProtoOutputStream proto) {
- proto.write(AidGroupProto.CATEGORY, category);
- for (String aid : aids) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void dump(@NonNull ProtoOutputStream proto) {
+ proto.write(AidGroupProto.CATEGORY, mCategory);
+ for (String aid : mAids) {
proto.write(AidGroupProto.AIDS, aid);
}
}
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
public int describeContents() {
return 0;
}
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(category);
- dest.writeInt(aids.size());
- if (aids.size() > 0) {
- dest.writeStringList(aids);
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mCategory);
+ dest.writeInt(mAids.size());
+ if (mAids.size() > 0) {
+ dest.writeStringList(mAids);
}
}
- @UnsupportedAppUsage
- public static final @android.annotation.NonNull Parcelable.Creator<AidGroup> CREATOR =
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final @NonNull Parcelable.Creator<AidGroup> CREATOR =
new Parcelable.Creator<AidGroup>() {
@Override
public AidGroup createFromParcel(Parcel source) {
- String category = source.readString();
+ String category = source.readString8();
int listSize = source.readInt();
ArrayList<String> aidList = new ArrayList<String>();
if (listSize > 0) {
@@ -168,8 +192,17 @@
}
};
- @UnsupportedAppUsage
- static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+ /**
+ * Create an instance of AID group from XML file.
+ *
+ * @param parser input xml parser stream
+ * @throws XmlPullParserException If an error occurs parsing the element.
+ * @throws IOException If an error occurs reading the element.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @Nullable
+ public static AidGroup createFromXml(@NonNull XmlPullParser parser)
+ throws XmlPullParserException, IOException {
String category = null;
ArrayList<String> aids = new ArrayList<String>();
AidGroup group = null;
@@ -210,11 +243,16 @@
return group;
}
- @UnsupportedAppUsage
- public void writeAsXml(XmlSerializer out) throws IOException {
+ /**
+ * Serialize instance of AID group to XML file.
+ * @param out XML serializer stream
+ * @throws IOException If an error occurs reading the element.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void writeAsXml(@NonNull XmlSerializer out) throws IOException {
out.startTag(null, "aid-group");
- out.attribute(null, "category", category);
- for (String aid : aids) {
+ out.attribute(null, "category", mCategory);
+ for (String aid : mAids) {
out.startTag(null, "aid");
out.attribute(null, "value", aid);
out.endTag(null, "aid");
@@ -222,7 +260,7 @@
out.endTag(null, "aid-group");
}
- static boolean isValidCategory(String category) {
+ private static boolean isValidCategory(String category) {
return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
CardEmulation.CATEGORY_OTHER.equals(category);
}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 793a70e..18ec914 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -14,10 +14,16 @@
* limitations under the License.
*/
+/**********************************************************************
+ * This file is not a part of the NFC mainline module *
+ * *******************************************************************/
+
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -28,7 +34,9 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.nfc.Flags;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
@@ -38,7 +46,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -47,82 +54,82 @@
import java.util.Map;
/**
+ * Class holding APDU service info.
+ *
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public final class ApduServiceInfo implements Parcelable {
- static final String TAG = "ApduServiceInfo";
+ private static final String TAG = "ApduServiceInfo";
/**
* The service that implements this
*/
- @UnsupportedAppUsage
- final ResolveInfo mService;
+ private final ResolveInfo mService;
/**
* Description of the service
*/
- final String mDescription;
+ private final String mDescription;
/**
* Whether this service represents AIDs running on the host CPU
*/
- final boolean mOnHost;
+ private final boolean mOnHost;
/**
* Offhost reader name.
* eg: SIM, eSE etc
*/
- String mOffHostName;
+ private String mOffHostName;
/**
* Offhost reader name from manifest file.
- * Used for unsetOffHostSecureElement()
+ * Used for resetOffHostSecureElement()
*/
- final String mStaticOffHostName;
+ private final String mStaticOffHostName;
/**
* Mapping from category to static AID group
*/
- @UnsupportedAppUsage
- final HashMap<String, AidGroup> mStaticAidGroups;
+ private final HashMap<String, AidGroup> mStaticAidGroups;
/**
* Mapping from category to dynamic AID group
*/
- @UnsupportedAppUsage
- final HashMap<String, AidGroup> mDynamicAidGroups;
+ private final HashMap<String, AidGroup> mDynamicAidGroups;
/**
* Whether this service should only be started when the device is unlocked.
*/
- final boolean mRequiresDeviceUnlock;
+ private final boolean mRequiresDeviceUnlock;
/**
* Whether this service should only be started when the device is screen on.
*/
- final boolean mRequiresDeviceScreenOn;
+ private final boolean mRequiresDeviceScreenOn;
/**
* The id of the service banner specified in XML.
*/
- final int mBannerResourceId;
+ private final int mBannerResourceId;
/**
* The uid of the package the service belongs to
*/
- final int mUid;
+ private final int mUid;
/**
* Settings Activity for this service
*/
- final String mSettingsActivityName;
+ private final String mSettingsActivityName;
/**
* @hide
*/
- @UnsupportedAppUsage
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
- ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+ List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
@@ -134,7 +141,7 @@
* @hide
*/
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
- ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+ List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
this.mService = info;
@@ -147,19 +154,28 @@
this.mRequiresDeviceUnlock = requiresUnlock;
this.mRequiresDeviceScreenOn = requiresScreenOn;
for (AidGroup aidGroup : staticAidGroups) {
- this.mStaticAidGroups.put(aidGroup.category, aidGroup);
+ this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup);
}
for (AidGroup aidGroup : dynamicAidGroups) {
- this.mDynamicAidGroups.put(aidGroup.category, aidGroup);
+ this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
}
this.mBannerResourceId = bannerResource;
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
}
- @UnsupportedAppUsage
- public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
- XmlPullParserException, IOException {
+ /**
+ * Creates a new ApduServiceInfo object.
+ *
+ * @param pm packageManager instance
+ * @param info app component info
+ * @param onHost whether service is on host or not (secure element)
+ * @throws XmlPullParserException If an error occurs parsing the element.
+ * @throws IOException If an error occurs reading the element.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost)
+ throws XmlPullParserException, IOException {
ServiceInfo si = info.serviceInfo;
XmlResourceParser parser = null;
try {
@@ -277,9 +293,9 @@
groupAttrs.recycle();
} else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) &&
currentGroup != null) {
- if (currentGroup.aids.size() > 0) {
- if (!mStaticAidGroups.containsKey(currentGroup.category)) {
- mStaticAidGroups.put(currentGroup.category, currentGroup);
+ if (currentGroup.getAids().size() > 0) {
+ if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) {
+ mStaticAidGroups.put(currentGroup.getCategory(), currentGroup);
}
} else {
Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs");
@@ -291,8 +307,8 @@
com.android.internal.R.styleable.AidFilter);
String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
toUpperCase();
- if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) {
- currentGroup.aids.add(aid);
+ if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
}
@@ -305,8 +321,8 @@
toUpperCase();
// Add wildcard char to indicate prefix
aid = aid.concat("*");
- if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) {
- currentGroup.aids.add(aid);
+ if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
}
@@ -319,8 +335,8 @@
toUpperCase();
// Add wildcard char to indicate suffix
aid = aid.concat("#");
- if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) {
- currentGroup.aids.add(aid);
+ if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
}
@@ -336,11 +352,25 @@
mUid = si.applicationInfo.uid;
}
+ /**
+ * Returns the app component corresponding to this APDU service.
+ *
+ * @return app component for this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public ComponentName getComponent() {
return new ComponentName(mService.serviceInfo.packageName,
mService.serviceInfo.name);
}
+ /**
+ * Returns the offhost secure element name (if the service is offhost).
+ *
+ * @return offhost secure element name for offhost services
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @Nullable
public String getOffHostSecureElement() {
return mOffHostName;
}
@@ -353,18 +383,30 @@
* for that category.
* @return List of AIDs registered by the service
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public List<String> getAids() {
final ArrayList<String> aids = new ArrayList<String>();
for (AidGroup group : getAidGroups()) {
- aids.addAll(group.aids);
+ aids.addAll(group.getAids());
}
return aids;
}
+ /**
+ * Returns a consolidated list of AIDs with prefixes from the AID groups
+ * registered by this service. Note that if a service has both
+ * a static (manifest-based) AID group for a category and a dynamic
+ * AID group, only the dynamically registered AIDs will be returned
+ * for that category.
+ * @return List of prefix AIDs registered by the service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public List<String> getPrefixAids() {
final ArrayList<String> prefixAids = new ArrayList<String>();
for (AidGroup group : getAidGroups()) {
- for (String aid : group.aids) {
+ for (String aid : group.getAids()) {
if (aid.endsWith("*")) {
prefixAids.add(aid);
}
@@ -373,10 +415,20 @@
return prefixAids;
}
+ /**
+ * Returns a consolidated list of AIDs with subsets from the AID groups
+ * registered by this service. Note that if a service has both
+ * a static (manifest-based) AID group for a category and a dynamic
+ * AID group, only the dynamically registered AIDs will be returned
+ * for that category.
+ * @return List of prefix AIDs registered by the service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public List<String> getSubsetAids() {
final ArrayList<String> subsetAids = new ArrayList<String>();
for (AidGroup group : getAidGroups()) {
- for (String aid : group.aids) {
+ for (String aid : group.getAids()) {
if (aid.endsWith("#")) {
subsetAids.add(aid);
}
@@ -384,14 +436,28 @@
}
return subsetAids;
}
+
/**
* Returns the registered AID group for this category.
+ *
+ * @param category category name
+ * @return {@link AidGroup} instance for the provided category
*/
- public AidGroup getDynamicAidGroupForCategory(String category) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public AidGroup getDynamicAidGroupForCategory(@NonNull String category) {
return mDynamicAidGroups.get(category);
}
- public boolean removeDynamicAidGroupForCategory(String category) {
+ /**
+ * Removes the registered AID group for this category.
+ *
+ * @param category category name
+ * @return {@code true} if an AID group existed
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public boolean removeDynamicAidGroupForCategory(@NonNull String category) {
return (mDynamicAidGroups.remove(category) != null);
}
@@ -403,7 +469,9 @@
* for that category.
* @return List of AIDs registered by the service
*/
- public ArrayList<AidGroup> getAidGroups() {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public List<AidGroup> getAidGroups() {
final ArrayList<AidGroup> groups = new ArrayList<AidGroup>();
for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) {
groups.add(entry.getValue());
@@ -421,49 +489,83 @@
/**
* Returns the category to which this service has attributed the AID that is passed in,
* or null if we don't know this AID.
+ * @param aid AID to lookup for
+ * @return category name corresponding to this AID
*/
- public String getCategoryForAid(String aid) {
- ArrayList<AidGroup> groups = getAidGroups();
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public String getCategoryForAid(@NonNull String aid) {
+ List<AidGroup> groups = getAidGroups();
for (AidGroup group : groups) {
- if (group.aids.contains(aid.toUpperCase())) {
- return group.category;
+ if (group.getAids().contains(aid.toUpperCase())) {
+ return group.getCategory();
}
}
return null;
}
- public boolean hasCategory(String category) {
+ /**
+ * Returns whether there is any AID group for this category.
+ * @param category category name
+ * @return {@code true} if an AID group exists
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public boolean hasCategory(@NonNull String category) {
return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
}
- @UnsupportedAppUsage
+ /**
+ * Returns whether the service is on host or not.
+ * @return true if the service is on host (not secure element)
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public boolean isOnHost() {
return mOnHost;
}
- @UnsupportedAppUsage
+ /**
+ * Returns whether the service requires device unlock.
+ * @return whether the service requires device unlock
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public boolean requiresUnlock() {
return mRequiresDeviceUnlock;
}
/**
* Returns whether this service should only be started when the device is screen on.
+ * @return whether the service requires screen on
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public boolean requiresScreenOn() {
return mRequiresDeviceScreenOn;
}
- @UnsupportedAppUsage
+ /**
+ * Returns description of service.
+ * @return user readable description of service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getDescription() {
return mDescription;
}
- @UnsupportedAppUsage
+ /**
+ * Returns uid of service.
+ * @return uid of the service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public int getUid() {
return mUid;
}
- public void setOrReplaceDynamicAidGroup(AidGroup aidGroup) {
+ /**
+ * Add or replace an AID group to this service.
+ * @param aidGroup instance of aid group to set or replace
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setDynamicAidGroup(@NonNull AidGroup aidGroup) {
mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
}
@@ -476,7 +578,8 @@
* TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number]
* (e.g. eSE/eSE1, eSE2, etc.).
*/
- public void setOffHostSecureElement(String offHost) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setOffHostSecureElement(@NonNull String offHost) {
mOffHostName = offHost;
}
@@ -484,15 +587,30 @@
* Resets the off host Secure Element to statically defined
* by the service in the manifest file.
*/
- public void unsetOffHostSecureElement() {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void resetOffHostSecureElement() {
mOffHostName = mStaticOffHostName;
}
- public CharSequence loadLabel(PackageManager pm) {
+ /**
+ * Load label for this service.
+ * @param pm packagemanager instance
+ * @return label name corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public CharSequence loadLabel(@NonNull PackageManager pm) {
return mService.loadLabel(pm);
}
- public CharSequence loadAppLabel(PackageManager pm) {
+ /**
+ * Load application label for this service.
+ * @param pm packagemanager instance
+ * @return app label name corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public CharSequence loadAppLabel(@NonNull PackageManager pm) {
try {
return pm.getApplicationLabel(pm.getApplicationInfo(
mService.resolvePackageName, PackageManager.GET_META_DATA));
@@ -501,12 +619,25 @@
}
}
- public Drawable loadIcon(PackageManager pm) {
+ /**
+ * Load application icon for this service.
+ * @param pm packagemanager instance
+ * @return app icon corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public Drawable loadIcon(@NonNull PackageManager pm) {
return mService.loadIcon(pm);
}
- @UnsupportedAppUsage
- public Drawable loadBanner(PackageManager pm) {
+ /**
+ * Load application banner for this service.
+ * @param pm packagemanager instance
+ * @return app banner corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public Drawable loadBanner(@NonNull PackageManager pm) {
Resources res;
try {
res = pm.getResourcesForApplication(mService.serviceInfo.packageName);
@@ -521,7 +652,12 @@
}
}
- @UnsupportedAppUsage
+ /**
+ * Load activity name for this service.
+ * @return activity name for this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getSettingsActivityName() { return mSettingsActivityName; }
@Override
@@ -556,14 +692,15 @@
return getComponent().hashCode();
}
-
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
public int describeContents() {
return 0;
}
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
mService.writeToParcel(dest, flags);
dest.writeString(mDescription);
dest.writeInt(mOnHost ? 1 : 0);
@@ -584,8 +721,8 @@
dest.writeString(mSettingsActivityName);
};
- @UnsupportedAppUsage
- public static final @android.annotation.NonNull Parcelable.Creator<ApduServiceInfo> CREATOR =
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR =
new Parcelable.Creator<ApduServiceInfo>() {
@Override
public ApduServiceInfo createFromParcel(Parcel source) {
@@ -620,7 +757,15 @@
}
};
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ /**
+ * Dump contents for debugging.
+ * @param fd parcelfiledescriptor instance
+ * @param pw printwriter instance
+ * @param args args for dumping
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw,
+ @NonNull String[] args) {
pw.println(" " + getComponent()
+ " (Description: " + getDescription() + ")"
+ " (UID: " + getUid() + ")");
@@ -633,15 +778,15 @@
}
pw.println(" Static AID groups:");
for (AidGroup group : mStaticAidGroups.values()) {
- pw.println(" Category: " + group.category);
- for (String aid : group.aids) {
+ pw.println(" Category: " + group.getCategory());
+ for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
}
pw.println(" Dynamic AID groups:");
for (AidGroup group : mDynamicAidGroups.values()) {
- pw.println(" Category: " + group.category);
- for (String aid : group.aids) {
+ pw.println(" Category: " + group.getCategory());
+ for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
}
@@ -651,7 +796,7 @@
}
/**
- * Dump debugging info as ApduServiceInfoProto
+ * Dump debugging info as ApduServiceInfoProto.
*
* If the output belongs to a sub message, the caller is responsible for wrapping this function
* between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
@@ -659,7 +804,8 @@
*
* @param proto the ProtoOutputStream to write to
*/
- public void dumpDebug(ProtoOutputStream proto) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void dumpDebug(@NonNull ProtoOutputStream proto) {
Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME);
proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription());
proto.write(ApduServiceInfoProto.ON_HOST, mOnHost);
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index 7a36b26..ec919e4 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -14,9 +14,16 @@
* limitations under the License.
*/
+/**********************************************************************
+ * This file is not a part of the NFC mainline module *
+ * *******************************************************************/
+
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -26,7 +33,9 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.nfc.Flags;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
@@ -36,13 +45,16 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
/**
+ * Class to hold NfcF service info.
+ *
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public final class NfcFServiceInfo implements Parcelable {
static final String TAG = "NfcFServiceInfo";
@@ -51,42 +63,42 @@
/**
* The service that implements this
*/
- final ResolveInfo mService;
+ private final ResolveInfo mService;
/**
* Description of the service
*/
- final String mDescription;
+ private final String mDescription;
/**
* System Code of the service
*/
- final String mSystemCode;
+ private final String mSystemCode;
/**
* System Code of the service registered by API
*/
- String mDynamicSystemCode;
+ private String mDynamicSystemCode;
/**
* NFCID2 of the service
*/
- final String mNfcid2;
+ private final String mNfcid2;
/**
* NFCID2 of the service registered by API
*/
- String mDynamicNfcid2;
+ private String mDynamicNfcid2;
/**
* The uid of the package the service belongs to
*/
- final int mUid;
+ private final int mUid;
/**
* LF_T3T_PMM of the service
*/
- final String mT3tPmm;
+ private final String mT3tPmm;
/**
* @hide
@@ -104,7 +116,16 @@
this.mT3tPmm = t3tPmm;
}
- public NfcFServiceInfo(PackageManager pm, ResolveInfo info)
+ /**
+ * Creates a new NfcFServiceInfo object.
+ *
+ * @param pm packageManager instance
+ * @param info app component info
+ * @throws XmlPullParserException If an error occurs parsing the element.
+ * @throws IOException If an error occurs reading the element.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info)
throws XmlPullParserException, IOException {
ServiceInfo si = info.serviceInfo;
XmlResourceParser parser = null;
@@ -192,44 +213,107 @@
mUid = si.applicationInfo.uid;
}
+ /**
+ * Returns the app component corresponding to this NFCF service.
+ *
+ * @return app component for this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public ComponentName getComponent() {
return new ComponentName(mService.serviceInfo.packageName,
mService.serviceInfo.name);
}
+ /**
+ * Returns the system code corresponding to this service.
+ *
+ * @return system code for this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getSystemCode() {
return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode);
}
- public void setOrReplaceDynamicSystemCode(String systemCode) {
+ /**
+ * Add or replace a system code to this service.
+ * @param systemCode system code to set or replace
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setDynamicSystemCode(@NonNull String systemCode) {
mDynamicSystemCode = systemCode;
}
+ /**
+ * Returns NFC ID2.
+ *
+ * @return nfc id2 to return
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getNfcid2() {
return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2);
}
- public void setOrReplaceDynamicNfcid2(String nfcid2) {
+ /**
+ * Set or replace NFC ID2
+ *
+ * @param nfcid2 NFC ID2 string
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setDynamicNfcid2(@NonNull String nfcid2) {
mDynamicNfcid2 = nfcid2;
}
+ /**
+ * Returns description of service.
+ * @return user readable description of service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getDescription() {
return mDescription;
}
+ /**
+ * Returns uid of service.
+ * @return uid of the service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public int getUid() {
return mUid;
}
+ /**
+ * Returns LF_T3T_PMM of the service
+ * @return returns LF_T3T_PMM of the service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
public String getT3tPmm() {
return mT3tPmm;
}
- public CharSequence loadLabel(PackageManager pm) {
+ /**
+ * Load application label for this service.
+ * @param pm packagemanager instance
+ * @return label name corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public CharSequence loadLabel(@NonNull PackageManager pm) {
return mService.loadLabel(pm);
}
- public Drawable loadIcon(PackageManager pm) {
+ /**
+ * Load application icon for this service.
+ * @param pm packagemanager instance
+ * @return app icon corresponding to service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public Drawable loadIcon(@NonNull PackageManager pm) {
return mService.loadIcon(pm);
}
@@ -270,13 +354,15 @@
return getComponent().hashCode();
}
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
public int describeContents() {
return 0;
}
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
mService.writeToParcel(dest, flags);
dest.writeString(mDescription);
dest.writeString(mSystemCode);
@@ -293,7 +379,8 @@
dest.writeString(mT3tPmm);
};
- public static final @android.annotation.NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR =
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final @NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR =
new Parcelable.Creator<NfcFServiceInfo>() {
@Override
public NfcFServiceInfo createFromParcel(Parcel source) {
@@ -322,7 +409,15 @@
}
};
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ /**
+ * Dump contents of the service for debugging.
+ * @param fd parcelfiledescriptor instance
+ * @param pw printwriter instance
+ * @param args args for dumping
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw,
+ @NonNull String[] args) {
pw.println(" " + getComponent()
+ " (Description: " + getDescription() + ")"
+ " (UID: " + getUid() + ")");
@@ -332,14 +427,15 @@
}
/**
- * Dump debugging info as NfcFServiceInfoProto
+ * Dump debugging info as NfcFServiceInfoProto.
*
* If the output belongs to a sub message, the caller is responsible for wrapping this function
* between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
*
* @param proto the ProtoOutputStream to write to
*/
- public void dumpDebug(ProtoOutputStream proto) {
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void dumpDebug(@NonNull ProtoOutputStream proto) {
Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME);
proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription());
proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode());
@@ -347,4 +443,3 @@
proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm());
}
}
-
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
new file mode 100644
index 0000000..e3faf39
--- /dev/null
+++ b/core/java/android/nfc/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.nfc"
+
+flag {
+ name: "enable_nfc_mainline"
+ namespace: "nfc"
+ description: "Flag for NFC mainline changes"
+ bug: "292140387"
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f40efc8..218d4bb 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -186,6 +186,8 @@
/**
* Get the binder transaction observer for this process.
*
+ * TODO(b/299356196): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setObserver(@Nullable BinderInternal.Observer observer) {
@@ -202,6 +204,8 @@
* that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls
* which deliver results through a callback interface.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setWarnOnBlocking(boolean warnOnBlocking) {
@@ -218,6 +222,8 @@
* interfaces hosted by package that could be upgraded or replaced,
* otherwise you risk system instability if that remote interface wedges.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static IBinder allowBlocking(IBinder binder) {
@@ -1307,6 +1313,8 @@
int callingUid) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
+
+ // TODO(b/299356196): observer should also observe transactions in native code
final CallSession callSession =
observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
// Theoretically, we should call transact, which will call onTransact,
@@ -1329,7 +1337,7 @@
final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
try {
- // TODO - this logic should not be in Java - it should be in native
+ // TODO(b/299356201) - this logic should not be in Java - it should be in native
// code in libbinder so that it works for all binder users.
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null && callingUid != -1) {
@@ -1340,9 +1348,9 @@
Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
}
- // TODO - this logic should not be in Java - it should be in native
- // code in libbinder so that it works for all binder users. Further,
- // this should not re-use flags.
+ // TODO(b/299353919) - this logic should not be in Java - it should be
+ // in native code in libbinder so that it works for all binder users.
+ // Further, this should not re-use flags.
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
AppOpsManager.startNotedAppOpsCollection(callingUid);
try {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 62d9c69..c527cb5 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
@@ -1953,6 +1954,23 @@
*/
public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
+ /**
+ * Retrieves the RSS memory used by the process as given by the status file.
+ */
+ @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ public static native long getRss();
+
+ /**
+ * Retrieves the RSS memory used by the process as given by the status file. Optionally supply a
+ * long array of up to 4 entries to retrieve the total memtrack reported size, memtrack
+ * graphics, memtrack gl, and memtrack other.
+ *
+ * @return The RSS memory usage, or 0 if retrieval failed (i.e. the PID is gone).
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ public static native long getRss(int pid, long[] outMemtrack);
+
/** @hide */
public static final int MEMINFO_TOTAL = 0;
/** @hide */
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 60622f18..ad3abd9 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -7,7 +7,7 @@
],
"name": "FrameworksVibratorCoreTests",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
@@ -20,7 +20,7 @@
],
"name": "FrameworksVibratorServicesTests",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
@@ -33,7 +33,7 @@
],
"name": "CtsVibratorTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index febe6f7..a95e66d 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -16,7 +16,7 @@
flag {
name: "allow_private_profile"
- namespace: "private_profile"
+ namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
}
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 2044502..38d885f 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -254,7 +254,10 @@
this.signingInfos = signingInfos;
}
- private static V4Signature readFrom(InputStream stream) throws IOException {
+ /**
+ * Constructs a V4Signature from an InputStream.
+ */
+ public static V4Signature readFrom(InputStream stream) throws IOException {
final int version = readIntLE(stream);
int maxSize = INCFS_MAX_SIGNATURE_SIZE;
final byte[] hashingInfo = readBytes(stream, maxSize);
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index e3f02e7..1cb7930 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -43,7 +43,7 @@
void setRuntimePermissionGrantStateByDeviceAdminFromParams(String callerPackageName,
in AdminPermissionControlParams params, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
- void notifyOneTimePermissionSessionTimeout(String packageName);
+ void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId);
void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
void getPrivilegesDescriptionStringForProfile(
in String deviceProfileName,
@@ -60,5 +60,5 @@
in String packageName,
in AndroidFuture callback);
void revokeSelfPermissionsOnKill(in String packageName, in List<String> permissions,
- in AndroidFuture callback);
+ int deviceId, in AndroidFuture callback);
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 18ede44d..7cecfdc 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -78,7 +78,7 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
@EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
- void startOneTimePermissionSession(String packageName, int userId, long timeout,
+ void startOneTimePermissionSession(String packageName, int deviceId, int userId, long timeout,
long revokeAfterKilledDelay);
@EnforcePermission("MANAGE_ONE_TIME_PERMISSION_SESSIONS")
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 84a197a..ba75918 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -764,13 +764,14 @@
* inactive.
*
* @param packageName The package which became inactive
- *
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
* @hide
*/
@RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
- public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName) {
- mRemoteService.run(
- service -> service.notifyOneTimePermissionSessionTimeout(packageName));
+ public void notifyOneTimePermissionSessionTimeout(@NonNull String packageName, int deviceId) {
+ mRemoteService.run(service -> service.notifyOneTimePermissionSessionTimeout(
+ packageName, deviceId));
}
/**
@@ -930,12 +931,14 @@
@NonNull List<String> permissions) {
mRemoteService.postAsync(service -> {
AndroidFuture<Void> callback = new AndroidFuture<>();
- service.revokeSelfPermissionsOnKill(packageName, permissions, callback);
+ service.revokeSelfPermissionsOnKill(packageName, permissions, mContext.getDeviceId(),
+ callback);
return callback;
}).whenComplete((result, err) -> {
if (err != null) {
Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
- + " for package " + packageName, err);
+ + " for package " + packageName + ", and device " + mContext.getDeviceId(),
+ err);
}
});
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 11005a6..9fe2599 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -30,6 +30,7 @@
import android.Manifest;
import android.annotation.BinderThread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -37,6 +38,7 @@
import android.app.admin.DevicePolicyManager.PermissionGrantState;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -46,6 +48,7 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.permission.PermissionControllerManager.CountPermissionAppsFlag;
+import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.Log;
@@ -296,13 +299,32 @@
* This method is called at the end of a one-time permission session
*
* @param packageName The package that has been inactive
+ *
+ * @deprecated Implement {@link #onOneTimePermissionSessionTimeout(String, int)} instead.
*/
+ @Deprecated
@BinderThread
public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
/**
+ * Called when a package is considered inactive based on the criteria given by
+ * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}.
+ * This method is called at the end of a one-time permission session
+ *
+ * @param packageName The package that has been inactive
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+ */
+ @BinderThread
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ public void onOneTimePermissionSessionTimeout(@NonNull String packageName,
+ int deviceId) {
+ onOneTimePermissionSessionTimeout(packageName);
+ }
+
+ /**
* Get the platform permissions which belong to a particular permission group
*
* @param permissionGroupName The permission group whose permissions are desired
@@ -341,13 +363,42 @@
* @param callback Callback waiting for operation to be complete.
*
* @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+ *
+ * @deprecated Implement {@link #onRevokeSelfPermissionsOnKill(String, List, int, Runnable)}
+ * instead.
*/
+ @Deprecated
@BinderThread
public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions, @NonNull Runnable callback) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
+ /**
+ * Triggers the revocation of one or more permissions for a package and device.
+ * This should only be called at the request of {@code packageName}.
+ * <p>
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * This revocation happens asynchronously and kills all processes running in the same UID as
+ * {@code packageName}. It will be triggered once it is safe to do so.
+ *
+ * @param packageName The name of the package for which the permissions will be revoked.
+ * @param permissions List of permissions to be revoked.
+ * @param deviceId The device ID refers either the primary device i.e. the phone or
+ * a virtual device. See {@link Context#DEVICE_ID_DEFAULT}
+ * @param callback Callback waiting for operation to be complete.
+ *
+ * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
+ */
+ @BinderThread
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
+ @NonNull List<String> permissions, int deviceId, @NonNull Runnable callback) {
+ onRevokeSelfPermissionsOnKill(packageName, permissions, callback);
+ }
+
// TODO(b/272129940): Remove this API and device profile role description when we drop T
// support.
/**
@@ -613,12 +664,12 @@
}
@Override
- public void notifyOneTimePermissionSessionTimeout(String packageName) {
+ public void notifyOneTimePermissionSessionTimeout(String packageName, int deviceId) {
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
packageName = Preconditions.checkNotNull(packageName,
"packageName cannot be null");
- onOneTimePermissionSessionTimeout(packageName);
+ onOneTimePermissionSessionTimeout(packageName, deviceId);
}
@Override
@@ -710,7 +761,8 @@
@Override
public void revokeSelfPermissionsOnKill(@NonNull String packageName,
- @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
+ @NonNull List<String> permissions, int deviceId,
+ @NonNull AndroidFuture callback) {
try {
Objects.requireNonNull(callback);
@@ -721,7 +773,7 @@
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
- onRevokeSelfPermissionsOnKill(packageName, permissions,
+ onRevokeSelfPermissionsOnKill(packageName, permissions, deviceId,
() -> callback.complete(null));
} catch (Throwable t) {
callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7d39210..e10ea10 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -214,6 +214,12 @@
public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
/**
+ * Additional debug log for virtual device permissions.
+ * @hide
+ */
+ public static final boolean DEBUG_DEVICE_PERMISSIONS = false;
+
+ /**
* Intent extra: List of PermissionGroupUsages
* <p>
* Type: {@code List<PermissionGroupUsage>}
@@ -1392,8 +1398,8 @@
@ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
try {
- mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
- timeoutMillis, revokeAfterKilledDelayMillis);
+ mPermissionManager.startOneTimePermissionSession(packageName, mContext.getDeviceId(),
+ mContext.getUserId(), timeoutMillis, revokeAfterKilledDelayMillis);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
new file mode 100644
index 0000000..77be5d4
--- /dev/null
+++ b/core/java/android/permission/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.permission.flags"
+
+flag {
+ name: "device_aware_permission_apis"
+ namespace: "permissions"
+ description: "enable device aware permission APIs"
+ bug: "274852670"
+}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index ecf1770..dc02470 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -178,7 +178,7 @@
* <pre> <print-service
* android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
- * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
+ * android:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
* /></pre>
* <p>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7e71134f..c19c20c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3596,12 +3596,9 @@
}
Bundle b;
- // b/252663068: if we're in system server and the caller did not call
+ // If we're in system server and the caller did not call
// clearCallingIdentity, the read would fail due to mismatched AttributionSources.
- // TODO(b/256013480): remove this bypass after fixing the callers in system server.
- if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
- && Settings.isInSystemServer()
- && Binder.getCallingUid() != Process.myUid()) {
+ if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
// Fetch all flags for the namespace at once for caching purposes
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index cf3707b..a391571 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4915,6 +4915,14 @@
public static final String COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER =
"satellite_attach_enabled_for_carrier";
+ /**
+ * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network.
+ * By default, it's disabled.
+ *
+ * @hide
+ */
+ public static final String COLUMN_IS_NTN = "is_ntn";
+
/** All columns in {@link SimInfo} table. */
private static final List<String> ALL_COLUMNS = List.of(
COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -4985,7 +4993,8 @@
COLUMN_TP_MESSAGE_REF,
COLUMN_USER_HANDLE,
COLUMN_SATELLITE_ENABLED,
- COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
+ COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+ COLUMN_IS_NTN
);
/**
diff --git a/core/java/android/security/TEST_MAPPING b/core/java/android/security/TEST_MAPPING
index 7e43381..5a679b1 100644
--- a/core/java/android/security/TEST_MAPPING
+++ b/core/java/android/security/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSecurityTestCases",
"options": [
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index cfc6f48..800149c 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -20,3 +20,11 @@
description: "Feature flag for deprecating .fsv_sig"
bug: "277916185"
}
+
+flag {
+ name: "extend_vb_chain_to_updated_apk"
+ namespace: "hardware_backed_security"
+ description: "Use v4 signature and fs-verity to chain verification of allowlisted APKs to Verified Boot"
+ bug: "277916185"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 00c30b1..83f9662 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -63,6 +63,10 @@
private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
+ private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.credentialmanager",
+ "com.android.credentialmanager.autofill.CredentialAutofillService");
+
private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
try {
@@ -307,6 +311,11 @@
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
try {
+ if (serviceInfo != null && isCredentialManagerAutofillService(
+ serviceInfo.getComponentName())) {
+ // Skip this service as it is for internal use only
+ continue;
+ }
services.add(new AutofillServiceInfo(context, serviceInfo));
} catch (SecurityException e) {
// Service does not declare the proper permission, ignore it.
@@ -316,6 +325,13 @@
return services;
}
+ private static boolean isCredentialManagerAutofillService(ComponentName componentName) {
+ if (componentName == null) {
+ return false;
+ }
+ return componentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index 59b2bc1..7b8d52f 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -13,9 +13,6 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
- },
- {
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
@@ -33,9 +30,6 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
- },
- {
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b48b7ec..3f41c56 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -46,6 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SharedMemory;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
@@ -131,6 +132,9 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long MULTIPLE_ACTIVE_HOTWORD_DETECTORS = 193232191L;
+ private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+ SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
+
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
@Override
public void ready() {
@@ -947,6 +951,10 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
+ if (!SYSPROP_VISUAL_QUERY_SERVICE_ENABLED) {
+ throw new IllegalStateException("VisualQueryDetectionService is not enabled on this "
+ + "system. Please set ro.hotword.visual_query_service_enabled to true.");
+ }
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index ece069f..c4660c4 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -111,7 +111,7 @@
/**
* Regular expression to match all IANA top-level domains.
*
- * List accurate as of 2015/11/24. List taken from:
+ * List accurate as of 2023/09/11. List taken from:
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
* This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
*
@@ -119,121 +119,167 @@
*/
static final String IANA_TOP_LEVEL_DOMAINS =
"(?:"
- + "(?:aaa|aarp|abb|abbott|abogado|academy|accenture|accountant|accountants|aco|active"
- + "|actor|ads|adult|aeg|aero|afl|agency|aig|airforce|airtel|allfinanz|alsace|amica|amsterdam"
- + "|android|apartments|app|apple|aquarelle|aramco|archi|army|arpa|arte|asia|associates"
- + "|attorney|auction|audio|auto|autos|axa|azure|a[cdefgilmoqrstuwxz])"
- + "|(?:band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bbc|bbva"
- + "|bcn|beats|beer|bentley|berlin|best|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
- + "|blackfriday|bloomberg|blue|bms|bmw|bnl|bnpparibas|boats|bom|bond|boo|boots|boutique"
- + "|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|build|builders|business"
- + "|buzz|bzh|b[abdefghijmnorstvwyz])"
- + "|(?:cab|cafe|cal|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards"
- + "|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|ceb|center|ceo"
- + "|cern|cfa|cfd|chanel|channel|chat|cheap|chloe|christmas|chrome|church|cipriani|cisco"
- + "|citic|city|cityeats|claims|cleaning|click|clinic|clothing|cloud|club|clubmed|coach"
- + "|codes|coffee|college|cologne|com|commbank|community|company|computer|comsec|condos"
- + "|construction|consulting|contractors|cooking|cool|coop|corsica|country|coupons|courses"
- + "|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
- + "|(?:dabur|dad|dance|date|dating|datsun|day|dclk|deals|degree|delivery|dell|delta"
- + "|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount"
- + "|dnp|docs|dog|doha|domains|doosan|download|drive|durban|dvag|d[ejkmoz])"
- + "|(?:earth|eat|edu|education|email|emerck|energy|engineer|engineering|enterprises"
- + "|epson|equipment|erni|esq|estate|eurovision|eus|events|everbank|exchange|expert|exposed"
- + "|express|e[cegrstu])"
- + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|feedback|ferrero|film"
- + "|final|finance|financial|firmdale|fish|fishing|fit|fitness|flights|florist|flowers|flsmidth"
- + "|fly|foo|football|forex|forsale|forum|foundation|frl|frogans|fund|furniture|futbol|fyi"
- + "|f[ijkmor])"
- + "|(?:gal|gallery|game|garden|gbiz|gdn|gea|gent|genting|ggee|gift|gifts|gives|giving"
- + "|glass|gle|global|globo|gmail|gmo|gmx|gold|goldpoint|golf|goo|goog|google|gop|gov|grainger"
- + "|graphics|gratis|green|gripe|group|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
- + "|(?:hamburg|hangout|haus|healthcare|help|here|hermes|hiphop|hitachi|hiv|hockey|holdings"
- + "|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hsbc|hyundai"
- + "|h[kmnrtu])"
- + "|(?:ibm|icbc|ice|icu|ifm|iinet|immo|immobilien|industries|infiniti|info|ing|ink|institute"
- + "|insure|int|international|investments|ipiranga|irish|ist|istanbul|itau|iwc|i[delmnoqrst])"
- + "|(?:jaguar|java|jcb|jetzt|jewelry|jlc|jll|jobs|joburg|jprs|juegos|j[emop])"
- + "|(?:kaufen|kddi|kia|kim|kinder|kitchen|kiwi|koeln|komatsu|krd|kred|kyoto|k[eghimnprwyz])"
- + "|(?:lacaixa|lancaster|land|landrover|lasalle|lat|latrobe|law|lawyer|lds|lease|leclerc"
- + "|legal|lexus|lgbt|liaison|lidl|life|lifestyle|lighting|limited|limo|linde|link|live"
- + "|lixil|loan|loans|lol|london|lotte|lotto|love|ltd|ltda|lupin|luxe|luxury|l[abcikrstuvy])"
- + "|(?:madrid|maif|maison|man|management|mango|market|marketing|markets|marriott|mba"
- + "|media|meet|melbourne|meme|memorial|men|menu|meo|miami|microsoft|mil|mini|mma|mobi|moda"
- + "|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar"
- + "|mtn|mtpc|mtr|museum|mutuelle|m[acdeghklmnopqrstuvwxyz])"
- + "|(?:nadex|nagoya|name|navy|nec|net|netbank|network|neustar|new|news|nexus|ngo|nhk"
- + "|nico|ninja|nissan|nokia|nra|nrw|ntt|nyc|n[acefgilopruz])"
- + "|(?:obi|office|okinawa|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|osaka"
- + "|otsuka|ovh|om)"
- + "|(?:page|panerai|paris|partners|parts|party|pet|pharmacy|philips|photo|photography"
- + "|photos|physio|piaget|pics|pictet|pictures|ping|pink|pizza|place|play|playstation|plumbing"
- + "|plus|pohl|poker|porn|post|praxi|press|pro|prod|productions|prof|properties|property"
- + "|protection|pub|p[aefghklmnrstwy])"
- + "|(?:qpon|quebec|qa)"
- + "|(?:racing|realtor|realty|recipes|red|redstone|rehab|reise|reisen|reit|ren|rent|rentals"
- + "|repair|report|republican|rest|restaurant|review|reviews|rich|ricoh|rio|rip|rocher|rocks"
- + "|rodeo|rsvp|ruhr|run|rwe|ryukyu|r[eosuw])"
- + "|(?:saarland|sakura|sale|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|saxo"
- + "|sbs|sca|scb|schmidt|scholarships|school|schule|schwarz|science|scor|scot|seat|security"
- + "|seek|sener|services|seven|sew|sex|sexy|shiksha|shoes|show|shriram|singles|site|ski"
- + "|sky|skype|sncf|soccer|social|software|sohu|solar|solutions|sony|soy|space|spiegel|spreadbetting"
- + "|srl|stada|starhub|statoil|stc|stcgroup|stockholm|studio|study|style|sucks|supplies"
- + "|supply|support|surf|surgery|suzuki|swatch|swiss|sydney|systems|s[abcdeghijklmnortuvxyz])"
- + "|(?:tab|taipei|tatamotors|tatar|tattoo|tax|taxi|team|tech|technology|tel|telefonica"
- + "|temasek|tennis|thd|theater|theatre|tickets|tienda|tips|tires|tirol|today|tokyo|tools"
- + "|top|toray|toshiba|tours|town|toyota|toys|trade|trading|training|travel|trust|tui|t[cdfghjklmnortvwz])"
- + "|(?:ubs|university|uno|uol|u[agksyz])"
- + "|(?:vacations|vana|vegas|ventures|versicherung|vet|viajes|video|villas|vin|virgin"
- + "|vision|vista|vistaprint|viva|vlaanderen|vodka|vote|voting|voto|voyage|v[aceginu])"
- + "|(?:wales|walter|wang|watch|webcam|website|wed|wedding|weir|whoswho|wien|wiki|williamhill"
- + "|win|windows|wine|wme|work|works|world|wtc|wtf|w[fs])"
- + "|(?:\u03b5\u03bb|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438|\u043a\u043e\u043c|\u043c\u043a\u0434"
+ + "(?:aaa|aarp|abb|abbott|abbvie|abc|able|abogado|abudhabi|academy|accenture|accountant"
+ + "|accountants|aco|actor|ads|adult|aeg|aero|aetna|afl|africa|agakhan|agency|aig|airbus"
+ + "|airforce|airtel|akdn|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|amazon|americanexpress"
+ + "|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|aol|apartments"
+ + "|app|apple|aquarelle|arab|aramco|archi|army|arpa|art|arte|asda|asia|associates|athleta"
+ + "|attorney|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aws|axa|azure"
+ + "|a[cdefgilmoqrstuwxz])"
+ + "|(?:baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays"
+ + "|barefoot|bargains|baseball|basketball|bauhaus|bayern|bbc|bbt|bbva|bcg|bcn|beats|beauty"
+ + "|beer|bentley|berlin|best|bestbuy|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
+ + "|blackfriday|blockbuster|blog|bloomberg|blue|bms|bmw|bnpparibas|boats|boehringer|bofa"
+ + "|bom|bond|boo|book|booking|bosch|bostik|boston|bot|boutique|box|bradesco|bridgestone"
+ + "|broadway|broker|brother|brussels|build|builders|business|buy|buzz|bzh|b[abdefghijmnorstvwyz])"
+ + "|(?:cab|cafe|cal|call|calvinklein|cam|camera|camp|canon|capetown|capital|capitalone"
+ + "|car|caravan|cards|care|career|careers|cars|casa|case|cash|casino|cat|catering|catholic"
+ + "|cba|cbn|cbre|cbs|center|ceo|cern|cfa|cfd|chanel|channel|charity|chase|chat|cheap|chintai"
+ + "|christmas|chrome|church|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|claims"
+ + "|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|coach|codes|coffee|college"
+ + "|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction"
+ + "|consulting|contact|contractors|cooking|cool|coop|corsica|country|coupon|coupons|courses"
+ + "|cpa|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|cuisinella|cymru"
+ + "|cyou|c[acdfghiklmnoruvwxyz])"
+ + "|(?:dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|deal|dealer|deals|degree"
+ + "|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet"
+ + "|digital|direct|directory|discount|discover|dish|diy|dnp|docs|doctor|dog|domains|dot"
+ + "|download|drive|dtv|dubai|dunlop|dupont|durban|dvag|dvr|d[ejkmoz])"
+ + "|(?:earth|eat|eco|edeka|edu|education|email|emerck|energy|engineer|engineering|enterprises"
+ + "|epson|equipment|ericsson|erni|esq|estate|etisalat|eurovision|eus|events|exchange|expert"
+ + "|exposed|express|extraspace|e[cegrstu])"
+ + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback"
+ + "|ferrari|ferrero|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale"
+ + "|fish|fishing|fit|fitness|flickr|flights|flir|florist|flowers|fly|foo|food|football"
+ + "|ford|forex|forsale|forum|foundation|fox|free|fresenius|frl|frogans|frontdoor|frontier"
+ + "|ftr|fujitsu|fun|fund|furniture|futbol|fyi|f[ijkmor])"
+ + "|(?:gal|gallery|gallo|gallup|game|games|gap|garden|gay|gbiz|gdn|gea|gent|genting"
+ + "|george|ggee|gift|gifts|gives|giving|glass|gle|global|globo|gmail|gmbh|gmo|gmx|godaddy"
+ + "|gold|goldpoint|golf|goo|goodyear|goog|google|gop|got|gov|grainger|graphics|gratis|green"
+ + "|gripe|grocery|group|guardian|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
+ + "|(?:hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here"
+ + "|hermes|hiphop|hisamitsu|hitachi|hiv|hkt|hockey|holdings|holiday|homedepot|homegoods"
+ + "|homes|homesense|honda|horse|hospital|host|hosting|hot|hotels|hotmail|house|how|hsbc"
+ + "|hughes|hyatt|hyundai|h[kmnrtu])"
+ + "|(?:ibm|icbc|ice|icu|ieee|ifm|ikano|imamat|imdb|immo|immobilien|inc|industries|infiniti"
+ + "|info|ing|ink|institute|insurance|insure|int|international|intuit|investments|ipiranga"
+ + "|irish|ismaili|ist|istanbul|itau|itv|i[delmnoqrst])"
+ + "|(?:jaguar|java|jcb|jeep|jetzt|jewelry|jio|jll|jmp|jnj|jobs|joburg|jot|joy|jpmorgan"
+ + "|jprs|juegos|juniper|j[emop])"
+ + "|(?:kaufen|kddi|kerryhotels|kerrylogistics|kerryproperties|kfh|kia|kids|kim|kinder"
+ + "|kindle|kitchen|kiwi|koeln|komatsu|kosher|kpmg|kpn|krd|kred|kuokgroup|kyoto|k[eghimnprwyz])"
+ + "|(?:lacaixa|lamborghini|lamer|lancaster|land|landrover|lanxess|lasalle|lat|latino"
+ + "|latrobe|law|lawyer|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|lidl|life|lifeinsurance"
+ + "|lifestyle|lighting|like|lilly|limited|limo|lincoln|link|lipsy|live|living|llc|llp|loan"
+ + "|loans|locker|locus|lol|london|lotte|lotto|love|lpl|lplfinancial|ltd|ltda|lundbeck|luxe"
+ + "|luxury|l[abcikrstuvy])"
+ + "|(?:madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott"
+ + "|marshalls|mattel|mba|mckinsey|med|media|meet|melbourne|meme|memorial|men|menu|merckmsd"
+ + "|miami|microsoft|mil|mini|mint|mit|mitsubishi|mlb|mls|mma|mobi|mobile|moda|moe|moi|mom"
+ + "|monash|money|monster|mormon|mortgage|moscow|moto|motorcycles|mov|movie|msd|mtn|mtr"
+ + "|museum|music|m[acdeghklmnopqrstuvwxyz])"
+ + "|(?:nab|nagoya|name|natura|navy|nba|nec|net|netbank|netflix|network|neustar|new|news"
+ + "|next|nextdirect|nexus|nfl|ngo|nhk|nico|nike|nikon|ninja|nissan|nissay|nokia|norton"
+ + "|now|nowruz|nowtv|nra|nrw|ntt|nyc|n[acefgilopruz])"
+ + "|(?:obi|observer|office|okinawa|olayan|olayangroup|oldnavy|ollo|omega|one|ong|onl"
+ + "|online|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|om)"
+ + "|(?:page|panasonic|paris|pars|partners|parts|party|pay|pccw|pet|pfizer|pharmacy|phd"
+ + "|philips|phone|photo|photography|photos|physio|pics|pictet|pictures|pid|pin|ping|pink"
+ + "|pioneer|pizza|place|play|playstation|plumbing|plus|pnc|pohl|poker|politie|porn|post"
+ + "|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties"
+ + "|property|protection|pru|prudential|pub|pwc|p[aefghklmnrstwy])"
+ + "|(?:qpon|quebec|quest|qa)"
+ + "|(?:racing|radio|read|realestate|realtor|realty|recipes|red|redstone|redumbrella"
+ + "|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant"
+ + "|review|reviews|rexroth|rich|richardli|ricoh|ril|rio|rip|rocher|rocks|rodeo|rogers|room"
+ + "|rsvp|rugby|ruhr|run|rwe|ryukyu|r[eosuw])"
+ + "|(?:saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant"
+ + "|sanofi|sap|sarl|sas|save|saxo|sbi|sbs|sca|scb|schaeffler|schmidt|scholarships|school"
+ + "|schule|schwarz|science|scot|search|seat|secure|security|seek|select|sener|services"
+ + "|seven|sew|sex|sexy|sfr|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping"
+ + "|shouji|show|showtime|silk|sina|singles|site|ski|skin|sky|skype|sling|smart|smile|sncf"
+ + "|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|spa|space|sport"
+ + "|spot|srl|stada|staples|star|statebank|statefarm|stc|stcgroup|stockholm|storage|store"
+ + "|stream|studio|study|style|sucks|supplies|supply|support|surf|surgery|suzuki|swatch"
+ + "|swiss|sydney|systems|s[abcdeghijklmnorstuvxyz])"
+ + "|(?:tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tci|tdk|team|tech"
+ + "|technology|tel|temasek|tennis|teva|thd|theater|theatre|tiaa|tickets|tienda|tips|tires"
+ + "|tirol|tjmaxx|tjx|tkmaxx|tmall|today|tokyo|tools|top|toray|toshiba|total|tours|town"
+ + "|toyota|toys|trade|trading|training|travel|travelers|travelersinsurance|trust|trv|tube"
+ + "|tui|tunes|tushu|tvs|t[cdfghjklmnortvwz])"
+ + "|(?:ubank|ubs|unicom|university|uno|uol|ups|u[agksyz])"
+ + "|(?:vacations|vana|vanguard|vegas|ventures|verisign|versicherung|vet|viajes|video"
+ + "|vig|viking|villas|vin|vip|virgin|visa|vision|viva|vivo|vlaanderen|vodka|volkswagen"
+ + "|volvo|vote|voting|voto|voyage|v[aceginu])"
+ + "|(?:wales|walmart|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam"
+ + "|weber|website|wed|wedding|weibo|weir|whoswho|wien|wiki|williamhill|win|windows|wine"
+ + "|winners|wme|wolterskluwer|woodside|work|works|world|wow|wtc|wtf|w[fs])"
+ + "|(?:\u03b5\u03bb|\u03b5\u03c5|\u0431\u0433|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438"
+ + "|\u0435\u044e|\u043a\u0430\u0442\u043e\u043b\u0438\u043a|\u043a\u043e\u043c|\u043c\u043a\u0434"
+ "|\u043c\u043e\u043d|\u043c\u043e\u0441\u043a\u0432\u0430|\u043e\u043d\u043b\u0430\u0439\u043d"
+ "|\u043e\u0440\u0433|\u0440\u0443\u0441|\u0440\u0444|\u0441\u0430\u0439\u0442|\u0441\u0440\u0431"
- + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05e7\u05d5\u05dd|\u0627\u0631\u0627\u0645\u0643\u0648"
- + "|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
- + "|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0627\u06cc\u0631\u0627\u0646"
- + "|\u0628\u0627\u0632\u0627\u0631|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633"
- + "|\u0633\u0648\u062f\u0627\u0646|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629"
- + "|\u0639\u0631\u0627\u0642|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646"
- + "|\u0642\u0637\u0631|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627"
- + "|\u0645\u0648\u0642\u0639|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
- + "|\u0938\u0902\u0917\u0920\u0928|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4"
- + "|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
- + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22"
- + "|\u10d2\u10d4|\u307f\u3093\u306a|\u30b0\u30fc\u30b0\u30eb|\u30b3\u30e0|\u4e16\u754c"
- + "|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51|\u4f01\u4e1a|\u4f5b\u5c71"
- + "|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063"
- + "|\u5546\u57ce|\u5546\u5e97|\u5546\u6807|\u5728\u7ebf|\u5927\u62ff|\u5a31\u4e50|\u5de5\u884c"
- + "|\u5e7f\u4e1c|\u6148\u5584|\u6211\u7231\u4f60|\u624b\u673a|\u653f\u52a1|\u653f\u5e9c"
- + "|\u65b0\u52a0\u5761|\u65b0\u95fb|\u65f6\u5c1a|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f"
- + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7edc"
- + "|\u8c37\u6b4c|\u96c6\u56e2|\u98de\u5229\u6d66|\u9910\u5385|\u9999\u6e2f|\ub2f7\ub137"
- + "|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d|xbox"
- + "|xerox|xin|xn\\-\\-11b4c3d|xn\\-\\-1qqw23a|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g"
- + "|xn\\-\\-3e0b707e|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim"
- + "|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks"
- + "|xn\\-\\-80ao21a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-90a3ac|xn\\-\\-90ais|xn\\-\\-9dbq2a"
- + "|xn\\-\\-9et52u|xn\\-\\-b4w605ferd|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
- + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-efvy88h"
- + "|xn\\-\\-estv75g|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s"
- + "|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-gecrj9c"
- + "|xn\\-\\-h2brj9c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i"
- + "|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+ + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05d9\u05e9\u05e8\u05d0\u05dc"
+ + "|\u05e7\u05d5\u05dd|\u0627\u0628\u0648\u0638\u0628\u064a|\u0627\u062a\u0635\u0627\u0644\u0627\u062a"
+ + "|\u0627\u0631\u0627\u0645\u0643\u0648|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u0628\u062d\u0631\u064a\u0646"
+ + "|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
+ + "|\u0627\u0644\u0639\u0644\u064a\u0627\u0646|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a"
+ + "|\u0627\u06cc\u0631\u0627\u0646|\u0628\u0627\u0631\u062a|\u0628\u0627\u0632\u0627\u0631"
+ + "|\u0628\u064a\u062a\u0643|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u062f\u0627\u0646"
+ + "|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629|\u0639\u0631\u0627\u0642|\u0639\u0631\u0628"
+ + "|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0643\u0627\u062b\u0648\u0644\u064a\u0643"
+ + "|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627|\u0645\u0648\u0631\u064a\u062a\u0627\u0646\u064a\u0627"
+ + "|\u0645\u0648\u0642\u0639|\u0647\u0645\u0631\u0627\u0647|\u067e\u0627\u06a9\u0633\u062a\u0627\u0646"
+ + "|\u0680\u0627\u0631\u062a|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
+ + "|\u092d\u093e\u0930\u0924\u092e\u094d|\u092d\u093e\u0930\u094b\u0924|\u0938\u0902\u0917\u0920\u0928"
+ + "|\u09ac\u09be\u0982\u09b2\u09be|\u09ad\u09be\u09b0\u09a4|\u09ad\u09be\u09f0\u09a4|\u0a2d\u0a3e\u0a30\u0a24"
+ + "|\u0aad\u0abe\u0ab0\u0aa4|\u0b2d\u0b3e\u0b30\u0b24|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe"
+ + "|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
+ + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0cad\u0cbe\u0cb0\u0ca4|\u0d2d\u0d3e\u0d30\u0d24\u0d02"
+ + "|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22|\u0ea5\u0eb2\u0ea7|\u10d2\u10d4"
+ + "|\u307f\u3093\u306a|\u30a2\u30de\u30be\u30f3|\u30af\u30e9\u30a6\u30c9|\u30b0\u30fc\u30b0\u30eb"
+ + "|\u30b3\u30e0|\u30b9\u30c8\u30a2|\u30bb\u30fc\u30eb|\u30d5\u30a1\u30c3\u30b7\u30e7\u30f3"
+ + "|\u30dd\u30a4\u30f3\u30c8|\u4e16\u754c|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51"
+ + "|\u4e9a\u9a6c\u900a|\u4f01\u4e1a|\u4f5b\u5c71|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366"
+ + "|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063|\u5546\u57ce|\u5546\u5e97|\u5546\u6807"
+ + "|\u5609\u91cc|\u5609\u91cc\u5927\u9152\u5e97|\u5728\u7ebf|\u5927\u62ff|\u5929\u4e3b\u6559"
+ + "|\u5a31\u4e50|\u5bb6\u96fb|\u5e7f\u4e1c|\u5fae\u535a|\u6148\u5584|\u6211\u7231\u4f60"
+ + "|\u624b\u673a|\u62db\u8058|\u653f\u52a1|\u653f\u5e9c|\u65b0\u52a0\u5761|\u65b0\u95fb"
+ + "|\u65f6\u5c1a|\u66f8\u7c4d|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f|\u6fb3\u9580"
+ + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7ad9"
+ + "|\u7f51\u7edc|\u8054\u901a|\u8c37\u6b4c|\u8d2d\u7269|\u901a\u8ca9|\u96c6\u56e2|\u96fb\u8a0a\u76c8\u79d1"
+ + "|\u98de\u5229\u6d66|\u98df\u54c1|\u9910\u5385|\u9999\u683c\u91cc\u62c9|\u9999\u6e2f"
+ + "|\ub2f7\ub137|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d"
+ + "|xbox|xerox|xfinity|xihuan|xin|xn\\-\\-11b4c3d|xn\\-\\-1ck2e1b|xn\\-\\-1qqw23a|xn\\-\\-2scrj9c"
+ + "|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g|xn\\-\\-3e0b707e|xn\\-\\-3hcrj9c|xn\\-\\-3pxu8k"
+ + "|xn\\-\\-42c2d9a|xn\\-\\-45br5cyl|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4dbrk0ce|xn\\-\\-4gbrim"
+ + "|xn\\-\\-54b7fta0cc|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-5su34j936bgsg|xn\\-\\-5tzm5g"
+ + "|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks|xn\\-\\-80ao21a|xn\\-\\-80aqecdr1a"
+ + "|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-8y0a063a|xn\\-\\-90a3ac|xn\\-\\-90ae|xn\\-\\-90ais"
+ + "|xn\\-\\-9dbq2a|xn\\-\\-9et52u|xn\\-\\-9krt00a|xn\\-\\-b4w605ferd|xn\\-\\-bck1b9a5dre4c"
+ + "|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cck2b3b|xn\\-\\-cckwcxetd|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
+ + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-e1a4c"
+ + "|xn\\-\\-eckvdtc9d|xn\\-\\-efvy88h|xn\\-\\-fct429k|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs"
+ + "|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d"
+ + "|xn\\-\\-fzc2c9e2c|xn\\-\\-fzys8d69uvgm|xn\\-\\-g2xx48c|xn\\-\\-gckr3f0f|xn\\-\\-gecrj9c"
+ + "|xn\\-\\-gk3at1e|xn\\-\\-h2breg3eve|xn\\-\\-h2brj9c|xn\\-\\-h2brj9c8c|xn\\-\\-hxt814e"
+ + "|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g"
+ + "|xn\\-\\-jlq480n2rg|xn\\-\\-jvr189m|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+ "|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt"
- + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e"
- + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbpl2fh|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
- + "|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema"
- + "|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh"
- + "|xn\\-\\-pssy2u|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-s9brj9c"
- + "|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb"
- + "|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+ + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgba7c0bbn0a|xn\\-\\-mgbaakc7dvf|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd"
+ + "|xn\\-\\-mgbah1a3hjkrd|xn\\-\\-mgbai9azgqp6j|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a|xn\\-\\-mgbbh1a71e"
+ + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgbca7dzdo|xn\\-\\-mgbcpq6gpa1a|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbgu82a"
+ + "|xn\\-\\-mgbi4ecexp|xn\\-\\-mgbpl2fh|xn\\-\\-mgbt3dhd|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
+ + "|xn\\-\\-mix891f|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-ngbe9e0a|xn\\-\\-ngbrx"
+ + "|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl"
+ + "|xn\\-\\-otu796d|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-pssy2u|xn\\-\\-q7ce6a"
+ + "|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxa6a|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-rovu88b"
+ + "|xn\\-\\-rvc1e0am3e|xn\\-\\-s9brj9c|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-tiq49xqyj"
+ + "|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv"
+ + "|xn\\-\\-vuq861b|xn\\-\\-w4r85el8fhu5dnra|xn\\-\\-w4rs40l|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+ "|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o"
- + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xperia|xxx|xyz)"
- + "|(?:yachts|yamaxun|yandex|yodobashi|yoga|yokohama|youtube|y[et])"
- + "|(?:zara|zip|zone|zuerich|z[amw]))";
-
+ + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xxx|xyz)"
+ + "|(?:yachts|yahoo|yamaxun|yandex|yodobashi|yoga|yokohama|you|youtube|yun|y[et])"
+ + "|(?:zappos|zara|zero|zip|zone|zuerich|z[amw]))";
/**
* Kept for backward compatibility reasons.
*
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 6b26155..5210271 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -27,9 +27,14 @@
import android.util.ArrayMap;
import android.util.Pair;
+import com.android.internal.security.VerityUtils;
+
import java.io.ByteArrayInputStream;
+import java.io.EOFException;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -60,7 +65,7 @@
* certificates associated with each signer.
*/
public static VerifiedSigner extractCertificates(String apkFile)
- throws SignatureNotFoundException, SecurityException {
+ throws SignatureNotFoundException, SignatureException, SecurityException {
Pair<V4Signature.HashingInfo, V4Signature.SigningInfos> pair = extractSignature(apkFile);
return verify(apkFile, pair.first, pair.second, APK_SIGNATURE_SCHEME_DEFAULT);
}
@@ -69,15 +74,37 @@
* Extracts APK Signature Scheme v4 signature of the provided APK.
*/
public static Pair<V4Signature.HashingInfo, V4Signature.SigningInfos> extractSignature(
- String apkFile) throws SignatureNotFoundException {
- final File apk = new File(apkFile);
- final byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature(
- apk.getAbsolutePath());
- if (signatureBytes == null || signatureBytes.length == 0) {
- throw new SignatureNotFoundException("Failed to obtain signature bytes from IncFS.");
- }
+ String apkFile) throws SignatureNotFoundException, SignatureException {
try {
- final V4Signature signature = V4Signature.readFrom(signatureBytes);
+ final File apk = new File(apkFile);
+ boolean needsConsistencyCheck;
+
+ // 1. Try IncFS first. IncFS verifies the file according to the integrity metadata
+ // (including the root hash of Merkle tree) it keeps track of with signature check. No
+ // further consistentcy check is needed.
+ byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature(
+ apk.getAbsolutePath());
+ V4Signature signature;
+ if (signatureBytes != null && signatureBytes.length > 0) {
+ needsConsistencyCheck = false;
+ signature = V4Signature.readFrom(signatureBytes);
+ } else if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ // 2. Try fs-verity next. fs-verity checks against the Merkle tree, but the
+ // v4 signature file (including a raw root hash) is managed separately. We need to
+ // ensure the signed data from the file is consistent with the actual file.
+ needsConsistencyCheck = true;
+
+ final File idsig = new File(apk.getAbsolutePath() + V4Signature.EXT);
+ try (var fis = new FileInputStream(idsig.getAbsolutePath())) {
+ signature = V4Signature.readFrom(fis);
+ } catch (IOException e) {
+ throw new SignatureNotFoundException(
+ "Failed to obtain signature bytes from .idsig");
+ }
+ } else {
+ throw new SignatureNotFoundException(
+ "Failed to obtain signature bytes from IncFS.");
+ }
if (!signature.isVersionSupported()) {
throw new SecurityException(
"v4 signature version " + signature.version + " is not supported");
@@ -86,9 +113,26 @@
signature.hashingInfo);
final V4Signature.SigningInfos signingInfos = V4Signature.SigningInfos.fromByteArray(
signature.signingInfos);
+
+ if (needsConsistencyCheck) {
+ final byte[] actualDigest = VerityUtils.getFsverityDigest(apk.getAbsolutePath());
+ if (actualDigest == null) {
+ throw new SecurityException("The APK does not have fs-verity");
+ }
+ final byte[] computedDigest =
+ VerityUtils.generateFsVerityDigest(apk.length(), hashingInfo);
+ if (!Arrays.equals(computedDigest, actualDigest)) {
+ throw new SignatureException("Actual digest does not match the v4 signature");
+ }
+ }
+
return Pair.create(hashingInfo, signingInfos);
+ } catch (EOFException e) {
+ throw new SignatureException("V4 signature is invalid.", e);
} catch (IOException e) {
throw new SignatureNotFoundException("Failed to read V4 signature.", e);
+ } catch (DigestException | NoSuchAlgorithmException e) {
+ throw new SecurityException("Failed to calculate the digest", e);
}
}
@@ -107,7 +151,7 @@
signingInfo);
final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);
- // Populate digests enforced by IncFS driver.
+ // Populate digests enforced by IncFS driver and fs-verity.
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
hashingInfo.rawRootHash);
@@ -217,7 +261,7 @@
public final byte[] apkDigest;
// Algorithm -> digest map of signed digests in the signature.
- // These are continuously enforced by the IncFS driver.
+ // These are continuously enforced by the IncFS driver and fs-verity.
public final Map<Integer, byte[]> contentDigests;
public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
diff --git a/core/java/android/view/HapticScrollFeedbackProvider.java b/core/java/android/view/HapticScrollFeedbackProvider.java
index a2f1d37..1310b0c 100644
--- a/core/java/android/view/HapticScrollFeedbackProvider.java
+++ b/core/java/android/view/HapticScrollFeedbackProvider.java
@@ -47,9 +47,17 @@
public @interface HapticScrollFeedbackAxis {}
private static final int TICK_INTERVAL_NO_TICK = 0;
+ private static final boolean INITIAL_END_OF_LIST_HAPTICS_ENABLED = false;
private final View mView;
private final ViewConfiguration mViewConfig;
+ /**
+ * Flag to disable the logic in this class if the View-based scroll haptics implementation is
+ * enabled. If {@code false}, this class will continue to run despite the View's scroll
+ * haptics implementation being enabled. This value should be set to {@code true} when this
+ * class is directly used by the View class.
+ */
+ private final boolean mDisabledIfViewPlaysScrollHaptics;
// Info about the cause of the latest scroll event.
@@ -63,18 +71,21 @@
/** The tick interval corresponding to the current InputDevice/source/axis. */
private int mTickIntervalPixels = TICK_INTERVAL_NO_TICK;
private int mTotalScrollPixels = 0;
- private boolean mCanPlayLimitFeedback = true;
+ private boolean mCanPlayLimitFeedback = INITIAL_END_OF_LIST_HAPTICS_ENABLED;
private boolean mHapticScrollFeedbackEnabled = false;
public HapticScrollFeedbackProvider(@NonNull View view) {
- this(view, ViewConfiguration.get(view.getContext()));
+ this(view, ViewConfiguration.get(view.getContext()),
+ /* disabledIfViewPlaysScrollHaptics= */ true);
}
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public HapticScrollFeedbackProvider(View view, ViewConfiguration viewConfig) {
+ public HapticScrollFeedbackProvider(
+ View view, ViewConfiguration viewConfig, boolean disabledIfViewPlaysScrollHaptics) {
mView = view;
mViewConfig = viewConfig;
+ mDisabledIfViewPlaysScrollHaptics = disabledIfViewPlaysScrollHaptics;
}
@Override
@@ -136,13 +147,19 @@
private void maybeUpdateCurrentConfig(int deviceId, int source, int axis) {
if (mAxis != axis || mSource != source || mDeviceId != deviceId) {
+ if (mDisabledIfViewPlaysScrollHaptics
+ && (source == InputDevice.SOURCE_ROTARY_ENCODER)
+ && mViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) {
+ mHapticScrollFeedbackEnabled = false;
+ return;
+ }
mSource = source;
mAxis = axis;
mDeviceId = deviceId;
mHapticScrollFeedbackEnabled =
mViewConfig.isHapticScrollFeedbackEnabled(deviceId, axis, source);
- mCanPlayLimitFeedback = true;
+ mCanPlayLimitFeedback = INITIAL_END_OF_LIST_HAPTICS_ENABLED;
mTotalScrollPixels = 0;
updateTickIntervals(deviceId, source, axis);
}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index bc83750..2761aae 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -35,8 +35,6 @@
* @hide
*/
public final class InputWindowHandle {
- // TODO (b/300094445): Convert to use correct flagging infrastructure
- public static final boolean USE_SURFACE_TRUSTED_OVERLAY = true;
/**
* An internal annotation for all the {@link android.os.InputConfig} flags that can be
@@ -61,6 +59,7 @@
InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
InputConfig.IS_WALLPAPER,
InputConfig.PAUSE_DISPATCHING,
+ InputConfig.TRUSTED_OVERLAY,
InputConfig.WATCH_OUTSIDE_TOUCH,
InputConfig.SLIPPERY,
InputConfig.DISABLE_USER_ACTIVITY,
@@ -273,13 +272,4 @@
}
this.inputConfig &= ~inputConfig;
}
-
- public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
- boolean isTrusted) {
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- t.setTrustedOverlay(sc, isTrusted);
- } else if (isTrusted) {
- inputConfig |= InputConfig.TRUSTED_OVERLAY;
- }
- }
}
diff --git a/core/java/android/view/KeyCharacterMap.aidl b/core/java/android/view/KeyCharacterMap.aidl
new file mode 100644
index 0000000..1a761a67
--- /dev/null
+++ b/core/java/android/view/KeyCharacterMap.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable KeyCharacterMap;
\ No newline at end of file
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index d8221a6..4fe53c2 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManagerGlobal;
@@ -309,6 +310,10 @@
private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
private static native boolean nativeEquals(long ptr1, long ptr2);
+ private static native void nativeApplyOverlay(long ptr, String layoutDescriptor,
+ String overlay);
+ private static native int nativeGetMappedKey(long ptr, int scanCode);
+
private KeyCharacterMap(Parcel in) {
if (in == null) {
throw new IllegalArgumentException("parcel must not be null");
@@ -368,6 +373,38 @@
}
/**
+ * Loads the key character map with applied KCM overlay.
+ *
+ * @param layoutDescriptor descriptor of the applied overlay KCM
+ * @param overlay string describing the overlay KCM
+ * @return The resultant key character map.
+ * @throws {@link UnavailableException} if the key character map
+ * could not be loaded because it was malformed or the default key character map
+ * is missing from the system.
+ * @hide
+ */
+ public static KeyCharacterMap load(@NonNull String layoutDescriptor, @NonNull String overlay) {
+ KeyCharacterMap kcm = KeyCharacterMap.load(VIRTUAL_KEYBOARD);
+ kcm.applyOverlay(layoutDescriptor, overlay);
+ return kcm;
+ }
+
+ private void applyOverlay(@NonNull String layoutDescriptor, @NonNull String overlay) {
+ nativeApplyOverlay(mPtr, layoutDescriptor, overlay);
+ }
+
+ /**
+ * Gets the mapped key for the provided scan code. Returns the provided default if no mapping
+ * found in the KeyCharacterMap.
+ *
+ * @hide
+ */
+ public int getMappedKeyOrDefault(int scanCode, int defaultKeyCode) {
+ int keyCode = nativeGetMappedKey(mPtr, scanCode);
+ return keyCode == KeyEvent.KEYCODE_UNKNOWN ? defaultKeyCode : keyCode;
+ }
+
+ /**
* Gets the Unicode character generated by the specified key and meta
* key state combination.
* <p>
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index bb4cc8f..b17d2d1 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1990,8 +1990,23 @@
}
/**
- * Returns whether this key will be sent to the
- * {@link android.media.session.MediaSession.Callback} if not handled.
+ * Returns whether this key will be sent to the {@link
+ * android.media.session.MediaSession.Callback} if not handled.
+ *
+ * <p>The following key codes are considered {@link android.media.session.MediaSession} keys:
+ *
+ * <ul>
+ * <li>{@link #KEYCODE_MEDIA_PLAY}
+ * <li>{@link #KEYCODE_MEDIA_PAUSE}
+ * <li>{@link #KEYCODE_MEDIA_PLAY_PAUSE}
+ * <li>{@link #KEYCODE_HEADSETHOOK}
+ * <li>{@link #KEYCODE_MEDIA_STOP}
+ * <li>{@link #KEYCODE_MEDIA_NEXT}
+ * <li>{@link #KEYCODE_MEDIA_PREVIOUS}
+ * <li>{@link #KEYCODE_MEDIA_REWIND}
+ * <li>{@link #KEYCODE_MEDIA_RECORD}
+ * <li>{@link #KEYCODE_MEDIA_FAST_FORWARD}
+ * </ul>
*/
public static final boolean isMediaSessionKey(int keyCode) {
switch (keyCode) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 776eeda..cdf5eec3 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -18,6 +18,7 @@
import static android.os.IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
@@ -4360,17 +4361,6 @@
public boolean isResampled;
/**
- * Returns true if this pointer coords object was generated by resampling, rather than from
- * an actual input event from the device at this time.
- *
- * @hide
- */
- @TestApi
- public boolean isResampled() {
- return isResampled;
- }
-
- /**
* Clears the contents of this object.
* Resets all axes to zero.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 052c2832..be6fb31 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -263,6 +263,8 @@
long nativeObject, int compatibility);
private static native void nativeSetFrameRateCategory(
long transactionObj, long nativeObject, int category);
+ private static native void nativeSetFrameRateSelectionStrategy(
+ long transactionObj, long nativeObject, int strategy);
private static native long nativeGetHandle(long nativeObject);
private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
@@ -850,6 +852,35 @@
*/
public static final int METADATA_GAME_MODE = 8;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"FRAME_RATE_SELECTION_STRATEGY_"},
+ value = {FRAME_RATE_SELECTION_STRATEGY_SELF,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN})
+ public @interface FrameRateSelectionStrategy {}
+
+ // From window.h. Keep these in sync.
+ /**
+ * Default value. The layer uses its own frame rate specifications, assuming it has any
+ * specifications, instead of its parent's.
+ * However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor layer
+ * supersedes this behavior, meaning that this layer will inherit the frame rate specifications
+ * of that ancestor layer.
+ * @hide
+ */
+ public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 0;
+
+ /**
+ * The layer's frame rate specifications will propagate to and override those of its descendant
+ * layers.
+ * The layer with this strategy has the {@link #FRAME_RATE_SELECTION_STRATEGY_SELF} behavior
+ * for itself. This does mean that any parent or ancestor layer that also has the strategy
+ * {@link FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's
+ * frame rate specifications.
+ * @hide
+ */
+ public static final int FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1;
+
/**
* Builder class for {@link SurfaceControl} objects.
*
@@ -3669,6 +3700,31 @@
}
/**
+ * Sets the frame rate selection strategy for the {@link SurfaceControl}.
+ *
+ * This instructs the system on how to choose a display refresh rate, following the
+ * strategy for the layer's frame rate specifications relative to other layers'.
+ *
+ * @param sc The SurfaceControl to specify the frame rate category of.
+ * @param strategy The frame rate selection strategy.
+ *
+ * @return This transaction object.
+ *
+ * @see #setFrameRate(SurfaceControl, float, int, int)
+ * @see #setFrameRateCategory(SurfaceControl, int)
+ * @see #setDefaultFrameRateCompatibility(SurfaceControl, int)
+ *
+ * @hide
+ */
+ @NonNull
+ public Transaction setFrameRateSelectionStrategy(
+ @NonNull SurfaceControl sc, @FrameRateSelectionStrategy int strategy) {
+ checkPreconditions(sc);
+ nativeSetFrameRateSelectionStrategy(mNativeObject, sc.mNativeObject, strategy);
+ return this;
+ }
+
+ /**
* Sets focus on the window identified by the input {@code token} if the window is focusable
* otherwise the request is dropped.
*
diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING
index 1e39716..db35908 100644
--- a/core/java/android/view/TEST_MAPPING
+++ b/core/java/android/view/TEST_MAPPING
@@ -10,7 +10,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 30fd2cf..55374b9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -919,6 +919,9 @@
*/
private static boolean sCompatibilityDone = false;
+ /** @hide */
+ public HapticScrollFeedbackProvider mScrollFeedbackProvider = null;
+
/**
* Use the old (broken) way of building MeasureSpecs.
*/
@@ -1584,11 +1587,11 @@
*/
static final int DISABLED = 0x00000020;
- /**
- * Mask for use with setFlags indicating bits used for indicating whether
- * this view is enabled
- * {@hide}
- */
+ /**
+ * Mask for use with setFlags indicating bits used for indicating whether
+ * this view is enabled
+ * {@hide}
+ */
static final int ENABLED_MASK = 0x00000020;
/**
@@ -3605,6 +3608,10 @@
* 1 PFLAG4_IMPORTANT_FOR_CREDENTIAL_MANAGER
* 1 PFLAG4_TRAVERSAL_TRACING_ENABLED
* 1 PFLAG4_RELAYOUT_TRACING_ENABLED
+ * 1 PFLAG4_ROTARY_HAPTICS_DETERMINED
+ * 1 PFLAG4_ROTARY_HAPTICS_ENABLED
+ * 1 PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT
+ * 1 PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT
* |-------|-------|-------|-------|
*/
@@ -3703,6 +3710,24 @@
*/
private static final int PFLAG4_RELAYOUT_TRACING_ENABLED = 0x000080000;
+ /** Indicates if rotary scroll haptics support for the view has been determined. */
+ private static final int PFLAG4_ROTARY_HAPTICS_DETERMINED = 0x100000;
+
+ /**
+ * Indicates if rotary scroll haptics is enabled for this view.
+ * The source of truth for this info is a ViewConfiguration API; this bit only caches the value.
+ */
+ private static final int PFLAG4_ROTARY_HAPTICS_ENABLED = 0x200000;
+
+ /** Indicates if there has been a scroll event since the last rotary input. */
+ private static final int PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT = 0x400000;
+
+ /**
+ * Indicates if there has been a rotary input that may generate a scroll event.
+ * This flag is important so that a scroll event can be properly attributed to a rotary input.
+ */
+ private static final int PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT = 0x800000;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -15251,13 +15276,13 @@
}
}
- /**
- * @see #performAccessibilityAction(int, Bundle)
- *
- * Note: Called from the default {@link AccessibilityDelegate}.
- *
- * @hide
- */
+ /**
+ * @see #performAccessibilityAction(int, Bundle)
+ *
+ * Note: Called from the default {@link AccessibilityDelegate}.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) {
if (isNestedScrollingEnabled()
@@ -15894,6 +15919,28 @@
}
private boolean dispatchGenericMotionEventInternal(MotionEvent event) {
+ final boolean isRotaryEncoderEvent = event.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER);
+ if (isRotaryEncoderEvent) {
+ // Determine and cache rotary scroll haptics support if it's not yet determined.
+ // Caching the support is important for two reasons:
+ // 1) Limits call to `ViewConfiguration#get`, which we should avoid if possible.
+ // 2) Limits latency from the `ViewConfiguration` API, which may be slow due to feature
+ // flag querying.
+ if ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_DETERMINED) == 0) {
+ if (ViewConfiguration.get(mContext)
+ .isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) {
+ mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_ENABLED;
+ }
+ mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED;
+ }
+ }
+ final boolean processForRotaryScrollHaptics =
+ isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0);
+ if (processForRotaryScrollHaptics) {
+ mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT;
+ mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT;
+ }
+
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnGenericMotionListener != null
@@ -15902,7 +15949,18 @@
return true;
}
- if (onGenericMotionEvent(event)) {
+ final boolean onGenericMotionEventResult = onGenericMotionEvent(event);
+ // Process scroll haptics after `onGenericMotionEvent`, since that's where scrolling usually
+ // happens. Some views may return false from `onGenericMotionEvent` even if they have done
+ // scrolling, so disregard the return value when processing for scroll haptics.
+ if (processForRotaryScrollHaptics) {
+ if ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT) != 0) {
+ doRotaryProgressForScrollHaptics(event);
+ } else {
+ doRotaryLimitForScrollHaptics(event);
+ }
+ }
+ if (onGenericMotionEventResult) {
return true;
}
@@ -17391,7 +17449,7 @@
return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
}
- /**
+ /**
* Remove the pending click action
*/
@UnsupportedAppUsage
@@ -17783,6 +17841,38 @@
}
}
+ private HapticScrollFeedbackProvider getScrollFeedbackProvider() {
+ if (mScrollFeedbackProvider == null) {
+ mScrollFeedbackProvider = new HapticScrollFeedbackProvider(this,
+ ViewConfiguration.get(mContext), /* disabledIfViewPlaysScrollHaptics= */ false);
+ }
+ return mScrollFeedbackProvider;
+ }
+
+ private void doRotaryProgressForScrollHaptics(MotionEvent rotaryEvent) {
+ final float axisScrollValue = rotaryEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
+ final float verticalScrollFactor =
+ ViewConfiguration.get(mContext).getScaledVerticalScrollFactor();
+ final int scrollAmount = -Math.round(axisScrollValue * verticalScrollFactor);
+ getScrollFeedbackProvider().onScrollProgress(
+ rotaryEvent.getDeviceId(), InputDevice.SOURCE_ROTARY_ENCODER,
+ MotionEvent.AXIS_SCROLL, scrollAmount);
+ }
+
+ private void doRotaryLimitForScrollHaptics(MotionEvent rotaryEvent) {
+ final boolean isStart = rotaryEvent.getAxisValue(MotionEvent.AXIS_SCROLL) > 0;
+ getScrollFeedbackProvider().onScrollLimit(
+ rotaryEvent.getDeviceId(), InputDevice.SOURCE_ROTARY_ENCODER,
+ MotionEvent.AXIS_SCROLL, isStart);
+ }
+
+ private void processScrollEventForRotaryEncoderHaptics() {
+ if ((mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT) != 0) {
+ mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT;
+ mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT;
+ }
+ }
+
/**
* This is called in response to an internal scroll in this view (i.e., the
* view scrolled its own contents). This is typically as a result of
@@ -17798,6 +17888,8 @@
notifySubtreeAccessibilityStateChangedIfNeeded();
postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);
+ processScrollEventForRotaryEncoderHaptics();
+
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index a3ae6cf..2cf5d5d 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -40,6 +40,8 @@
import android.util.TypedValue;
import android.view.flags.Flags;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
*/
@@ -375,6 +377,7 @@
private final int mSmartSelectionInitializedTimeout;
private final int mSmartSelectionInitializingTimeout;
private final boolean mPreferKeepClearForFocusEnabled;
+ private final boolean mViewBasedRotaryEncoderScrollHapticsEnabledConfig;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915)
private boolean sHasPermanentMenuKey;
@@ -399,6 +402,7 @@
mMaximumRotaryEncoderFlingVelocity = MAXIMUM_FLING_VELOCITY;
mRotaryEncoderHapticScrollFeedbackEnabled = false;
mRotaryEncoderHapticScrollFeedbackTickIntervalPixels = NO_HAPTIC_SCROLL_TICK_INTERVAL;
+ mViewBasedRotaryEncoderScrollHapticsEnabledConfig = false;
mScrollbarSize = SCROLL_BAR_SIZE;
mTouchSlop = TOUCH_SLOP;
mHandwritingSlop = HANDWRITING_SLOP;
@@ -575,6 +579,9 @@
com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis);
mPreferKeepClearForFocusEnabled = res.getBoolean(
com.android.internal.R.bool.config_preferKeepClearForFocus);
+ mViewBasedRotaryEncoderScrollHapticsEnabledConfig =
+ res.getBoolean(
+ com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
}
/**
@@ -590,8 +597,7 @@
public static ViewConfiguration get(@NonNull @UiContext Context context) {
StrictMode.assertConfigurationContext(context, "ViewConfiguration");
- final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- final int density = (int) (100.0f * metrics.density);
+ final int density = getDisplayDensity(context);
ViewConfiguration configuration = sConfigurations.get(density);
if (configuration == null) {
@@ -603,6 +609,28 @@
}
/**
+ * Removes cached ViewConfiguration instances, so that we can ensure `get` constructs a new
+ * ViewConfiguration instance. This is useful for testing the behavior and performance of
+ * creating ViewConfiguration the first time.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void resetCacheForTesting() {
+ sConfigurations.clear();
+ }
+
+ /**
+ * Sets the ViewConfiguration cached instanc for a given Context for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void setInstanceForTesting(Context context, ViewConfiguration instance) {
+ sConfigurations.put(getDisplayDensity(context), instance);
+ }
+
+ /**
* @return The width of the horizontal scrollbar and the height of the vertical
* scrollbar in dips
*
@@ -1311,6 +1339,20 @@
return NO_HAPTIC_SCROLL_TICK_INTERVAL;
}
+ /**
+ * Checks if the View-based haptic scroll feedback implementation is enabled for
+ * {@link InputDevice#SOURCE_ROTARY_ENCODER}s.
+ *
+ * <p>If this method returns {@code true}, the {@link HapticScrollFeedbackProvider} will be
+ * muted for rotary encoders in favor of View's scroll haptics implementation.
+ *
+ * @hide
+ */
+ public boolean isViewBasedRotaryEncoderHapticScrollFeedbackEnabled() {
+ return mViewBasedRotaryEncoderScrollHapticsEnabledConfig
+ && Flags.useViewBasedRotaryEncoderScrollHaptics();
+ }
+
private static boolean isInputDeviceInfoValid(int id, int axis, int source) {
InputDevice device = InputManagerGlobal.getInstance().getInputDevice(id);
return device != null && device.getMotionRange(axis, source) != null;
@@ -1420,4 +1462,9 @@
public static int getHoverTooltipHideShortTimeout() {
return HOVER_TOOLTIP_HIDE_SHORT_TIMEOUT;
}
+
+ private static final int getDisplayDensity(Context context) {
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ return (int) (100.0f * metrics.density);
+ }
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 85d7c10..5720fc0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3752,7 +3752,15 @@
&& !child.isActivityDeniedForAutofillForUnimportantView())
|| (shouldIncludeAllChildrenViewWithAutofillTypeNotNone(afm)
&& child.getAutofillType() != AUTOFILL_TYPE_NONE)
- || shouldIncludeAllChildrenViews(afm)){
+ || shouldIncludeAllChildrenViews(afm)
+ || (child instanceof ViewGroup && child.getVisibility() != View.VISIBLE)) {
+ // If the child is a ViewGroup object and its visibility is not visible, include
+ // it as part of the assist structure. The children of these invisible ViewGroup
+ // objects are parsed and included in the assist structure. When the Autofill
+ // Provider determines the visibility of these children, it looks at their
+ // visibility as well as their parent's visibility. Omitting invisible parents
+ // will lead to the Autofill Provider incorrectly assuming that these children
+ // of invisible parents are actually visible.
list.add(child);
} else if (child instanceof ViewGroup) {
((ViewGroup) child).populateChildrenForAutofill(list, flags);
@@ -4437,7 +4445,7 @@
* @param drawingPosition the drawing order position.
* @return the container position of a child for this drawing order position.
*
- * @see #getChildDrawingOrder(int, int)}
+ * @see #getChildDrawingOrder(int, int)
*/
public final int getChildDrawingOrder(int drawingPosition) {
return getChildDrawingOrder(getChildCount(), drawingPosition);
@@ -7404,19 +7412,26 @@
}
target = next;
}
- if (!childIsHit) {
+ if (!childIsHit && mFirstHoverTarget != null) {
target = mFirstHoverTarget;
+ final ArrayList<View> preorderedList = buildTouchDispatchChildList();
while (notEmpty && target != null) {
final HoverTarget next = target.next;
final View hoveredView = target.child;
- rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
- hoveredView.mBottom);
- matrix.mapRect(rect);
- notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
- Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ if (!isOnTop(child, hoveredView, preorderedList)) {
+ rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
+ hoveredView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom),
+ Region.Op.DIFFERENCE);
+ }
target = next;
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
} else {
TouchTarget target = mFirstTouchTarget;
@@ -7429,19 +7444,26 @@
}
target = next;
}
- if (!childIsHit) {
+ if (!childIsHit && mFirstTouchTarget != null) {
target = mFirstTouchTarget;
+ final ArrayList<View> preorderedList = buildOrderedChildList();
while (notEmpty && target != null) {
final TouchTarget next = target.next;
final View touchedView = target.child;
- rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
- touchedView.mBottom);
- matrix.mapRect(rect);
- notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
- Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ if (!isOnTop(child, touchedView, preorderedList)) {
+ rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
+ touchedView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom),
+ Region.Op.DIFFERENCE);
+ }
target = next;
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
}
@@ -7451,6 +7473,28 @@
return notEmpty;
}
+ /**
+ * Return true if the given {@code view} is drawn on top of the {@code otherView}.
+ * Both the {@code view} and {@code otherView} must be children of this ViewGroup.
+ * Otherwise, the returned value is meaningless.
+ */
+ private boolean isOnTop(View view, View otherView, ArrayList<View> preorderedList) {
+ final int childrenCount = mChildrenCount;
+ final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ if (child == view) {
+ return true;
+ }
+ if (child == otherView) {
+ return false;
+ }
+ }
+ // Can't find the view and otherView in the children list. Return value is meaningless.
+ return false;
+ }
private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
final int[] locationInWindow = new int[2];
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d191ccd..0ba5d06 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,9 @@
@NonNull Display mDisplay;
final String mBasePackageName;
+ // If we would like to keep a particular eye on the corresponding package.
+ final boolean mExtraDisplayListenerLogging;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -1010,6 +1013,8 @@
mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -1146,14 +1151,10 @@
}
private boolean isInTouchMode() {
- IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
- if (windowManager != null) {
- try {
- return windowManager.isInTouchMode(getDisplayId());
- } catch (RemoteException e) {
- }
+ if (mAttachInfo == null) {
+ return mContext.getResources().getBoolean(R.bool.config_defaultInTouchMode);
}
- return false;
+ return mAttachInfo.mInTouchMode;
}
/**
@@ -1444,6 +1445,10 @@
// We should update mAttachInfo.mDisplayState after registerDisplayListener
// because displayState might be changed before registerDisplayListener.
mAttachInfo.mDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "(" + mBasePackageName + ") Initial DisplayState: "
+ + mAttachInfo.mDisplayState, new Throwable());
+ }
if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
mUseBLASTAdapter = true;
}
@@ -1528,6 +1533,9 @@
* Register any kind of listeners if setView was success.
*/
private void registerListeners() {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Register listeners: " + mBasePackageName);
+ }
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mAccessibilityManager.addHighTextContrastStateChangeListener(
@@ -1553,6 +1561,9 @@
DisplayManagerGlobal
.getInstance()
.unregisterDisplayListener(mDisplayListener);
+ if (mExtraDisplayListenerLogging) {
+ Slog.w(mTag, "Unregister listeners: " + mBasePackageName, new Throwable());
+ }
}
private void setTag() {
@@ -1960,9 +1971,16 @@
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Received onDisplayChanged - " + mView);
+ }
if (mView != null && mDisplay.getDisplayId() == displayId) {
final int oldDisplayState = mAttachInfo.mDisplayState;
final int newDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "DisplayState - old: " + oldDisplayState
+ + ", new: " + newDisplayState);
+ }
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
pokeDrawLockIfNeeded();
@@ -3995,8 +4013,6 @@
mWindowFocusChanged = false;
hasWindowFocus = mUpcomingWindowFocus;
}
- // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
- // config changes.
if (hasWindowFocus) {
mInsetsController.onWindowFocusGained(
getFocusedViewOrNull() != null /* hasViewFocused */);
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 3b8298e..dda3993 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -124,16 +124,16 @@
|| cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, systemBars(), requestedVisibleTypes);
- if (systemBarsInsets.left > 0) {
+ if (systemBarsInsets.left >= cutout.getSafeInsetLeft()) {
displayCutoutSafeExceptMaybeBars.left = MIN_X;
}
- if (systemBarsInsets.top > 0) {
+ if (systemBarsInsets.top >= cutout.getSafeInsetTop()) {
displayCutoutSafeExceptMaybeBars.top = MIN_Y;
}
- if (systemBarsInsets.right > 0) {
+ if (systemBarsInsets.right >= cutout.getSafeInsetRight()) {
displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
- if (systemBarsInsets.bottom > 0) {
+ if (systemBarsInsets.bottom >= cutout.getSafeInsetBottom()) {
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 432f6e1..4cb8788 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1474,7 +1474,7 @@
}
for (int i = 0; i < infos.size(); i++) {
final VirtualViewFillInfo info = infos.valueAt(i);
- final int virtualId = infos.indexOfKey(i);
+ final int virtualId = infos.keyAt(i);
notifyViewReadyInner(getAutofillId(view, virtualId),
(info == null) ? null : info.getAutofillHints());
}
@@ -1488,9 +1488,6 @@
* @hide
*/
public void notifyViewEnteredForFillDialog(View v) {
- if (sDebug) {
- Log.d(TAG, "notifyViewEnteredForFillDialog:" + v.getAutofillId());
- }
if (v.isCredential()
&& mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
@@ -1503,11 +1500,14 @@
notifyViewReadyInner(v.getAutofillId(), v.getAutofillHints());
}
- private void notifyViewReadyInner(AutofillId id, String[] autofillHints) {
+ private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints) {
+ if (sDebug) {
+ Log.d(TAG, "notifyViewReadyInner:" + id);
+ }
+
if (!hasAutofillFeature()) {
return;
}
-
synchronized (mLock) {
if (mAllTrackedViews.contains(id)) {
// The id is tracked and will not trigger pre-fill request again.
@@ -1543,26 +1543,38 @@
final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (clientAdded) {
startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, /* bounds= */ null,
- /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
+ /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
} else {
if (sVerbose) {
Log.v(TAG, "not starting session: no service client");
}
}
-
}
}
}
- if (mIsFillDialogEnabled
- || ArrayUtils.containsAny(autofillHints, mFillDialogEnabledHints)) {
+ // Check if framework should send pre-fill request for fill dialog
+ boolean shouldSendPreFillRequestForFillDialog = false;
+ if (mIsFillDialogEnabled) {
+ shouldSendPreFillRequestForFillDialog = true;
+ } else if (autofillHints != null) {
+ // check if supported autofill hint is present
+ for (String autofillHint : autofillHints) {
+ for (String filldialogEnabledHint : mFillDialogEnabledHints) {
+ if (filldialogEnabledHint.equalsIgnoreCase(autofillHint)) {
+ shouldSendPreFillRequestForFillDialog = true;
+ break;
+ }
+ }
+ if (shouldSendPreFillRequestForFillDialog) break;
+ }
+ }
+ if (shouldSendPreFillRequestForFillDialog) {
if (sDebug) {
Log.d(TAG, "Triggering pre-emptive request for fill dialog.");
}
-
int flags = FLAG_SUPPORTS_FILL_DIALOG;
flags |= FLAG_VIEW_NOT_FOCUSED;
-
synchronized (mLock) {
// To match the id of the IME served view, used AutofillId.NO_AUTOFILL_ID on prefill
// request, because IME will reset the id of IME served view to 0 when activity
@@ -1570,9 +1582,10 @@
// not match the IME served view's, Autofill will be blocking to wait inline
// request from the IME.
notifyViewEnteredLocked(/* view= */ null, AutofillId.NO_AUTOFILL_ID,
- /* bounds= */ null, /* value= */ null, flags);
+ /* bounds= */ null, /* value= */ null, flags);
}
}
+ return;
}
private boolean hasFillDialogUiFeature() {
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 62c5691..d1d871c2 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -5,4 +5,11 @@
name: "scroll_feedback_api"
description: "Enable the scroll feedback APIs"
bug: "239594271"
+}
+
+flag {
+ namespace: "toolkit"
+ name: "use_view_based_rotary_encoder_scroll_haptics"
+ description: "If enabled, the rotary encoder scroll haptic implementation in the View class will be used, and the HapticScrollFeedbackProvider logic for rotary encoder haptic will be muted."
+ bug: "299587011"
}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 61470f2..3557f16 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -417,7 +417,9 @@
if (mBeamer == null) {
return;
}
- mBeamer.forget(token);
+ dispatch(() -> {
+ mBeamer.forget(token);
+ });
}
@Override
diff --git a/core/java/android/view/inputmethod/TEST_MAPPING b/core/java/android/view/inputmethod/TEST_MAPPING
index 4b2ea1a..ad59463 100644
--- a/core/java/android/view/inputmethod/TEST_MAPPING
+++ b/core/java/android/view/inputmethod/TEST_MAPPING
@@ -11,6 +11,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.AppModeFull"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 92d3408..c144289 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -2,7 +2,8 @@
flag {
name: "refactor_insets_controller"
- namespace: "inputmethod"
+ namespace: "input_method"
description: "Feature flag for refactoring InsetsController and removing ImeInsetsSourceConsumer"
bug: "298172246"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 065089f5..53c73c6 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -16,10 +16,7 @@
package android.widget;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Message;
import android.util.AttributeSet;
@@ -48,7 +45,6 @@
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- private boolean mUserPresent = true;
private boolean mAdvancedByHost = false;
public AdapterViewFlipper(Context context) {
@@ -82,40 +78,10 @@
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -126,8 +92,6 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -235,8 +199,7 @@
* true.
*/
private void updateRunning(boolean flipNow) {
- boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
- && mAdapter != null;
+ boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -248,7 +211,7 @@
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1f0e95e..e0158332 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -23,7 +23,6 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
@@ -37,6 +36,9 @@
import android.view.View;
import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
+import android.widget.TextClock.ClockEventDelegate;
+
+import com.android.internal.util.Preconditions;
import java.time.Clock;
import java.time.DateTimeException;
@@ -112,6 +114,7 @@
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mClockEventDelegate = new ClockEventDelegate(context);
mSecondsHandFps = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
context.getResources()
@@ -584,21 +587,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- IntentFilter filter = new IntentFilter();
if (!mReceiverAttached) {
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver,
- android.os.Process.myUserHandle(), filter, null, getHandler());
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
mReceiverAttached = true;
}
@@ -615,12 +606,23 @@
@Override
protected void onDetachedFromWindow() {
if (mReceiverAttached) {
- getContext().unregisterReceiver(mIntentReceiver);
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
mReceiverAttached = false;
}
super.onDetachedFromWindow();
}
+ /**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mReceiverAttached, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
private void onVisible() {
if (!mVisible) {
mVisible = true;
@@ -797,6 +799,7 @@
}
};
private boolean mReceiverAttached;
+ private ClockEventDelegate mClockEventDelegate;
private final Runnable mTick = new Runnable() {
@Override
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index e48afb2..255bd67 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.os.Process.myUserHandle;
import static android.view.ViewDebug.ExportedProperty;
import static android.widget.RemoteViews.RemoteView;
@@ -24,7 +25,6 @@
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -43,6 +43,7 @@
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import com.android.internal.util.Preconditions;
import java.time.Duration;
import java.time.Instant;
@@ -141,6 +142,8 @@
private boolean mRegistered;
private boolean mShouldRunTicker;
+ private ClockEventDelegate mClockEventDelegate;
+
private Calendar mTime;
private String mTimeZone;
@@ -178,8 +181,7 @@
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
createTime(timeZone);
- } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
- || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
+ } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
return;
}
onTimeChanged();
@@ -282,6 +284,7 @@
if (mFormat24 == null) {
mFormat24 = getBestDateTimePattern("Hm");
}
+ mClockEventDelegate = new ClockEventDelegate(getContext());
createTime(mTimeZone);
chooseFormat();
@@ -431,6 +434,17 @@
}
/**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mRegistered, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
+ /**
* Update the displayed time if necessary and invalidate the view.
*/
public void refreshTime() {
@@ -557,7 +571,7 @@
if (!mRegistered) {
mRegistered = true;
- registerReceiver();
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
registerObserver();
createTime(mTimeZone);
@@ -582,7 +596,7 @@
super.onDetachedFromWindow();
if (mRegistered) {
- unregisterReceiver();
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
unregisterObserver();
mRegistered = false;
@@ -598,56 +612,27 @@
mStopTicking = true;
}
- private void registerReceiver() {
- final IntentFilter filter = new IntentFilter();
-
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the receiver as the user
- // the app is running as not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
- }
-
private void registerObserver() {
if (mRegistered) {
if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(getHandler());
}
- final ContentResolver resolver = getContext().getContentResolver();
- Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
- if (mShowCurrentUserTime) {
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.USER_ALL);
- } else {
- // UserHandle.myUserId() is needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the ContentObserver with the user
- // the app is running (e.g. the launcher) and not the user of the
- // context (e.g. the widget's profile).
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.myUserId());
- }
+ // UserHandle.myUserId() is needed. This class is supported by the
+ // remote views mechanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For example, when adding widgets from a managed profile to the
+ // home screen. Therefore, we register the ContentObserver with the user
+ // the app is running (e.g. the launcher) and not the user of the
+ // context (e.g. the widget's profile).
+ int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId();
+ mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle);
}
}
- private void unregisterReceiver() {
- getContext().unregisterReceiver(mIntentReceiver);
- }
-
private void unregisterObserver() {
if (mFormatChangeObserver != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- resolver.unregisterContentObserver(mFormatChangeObserver);
+ mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver);
}
}
@@ -674,4 +659,59 @@
stream.addProperty("format", mFormat == null ? null : mFormat.toString());
stream.addProperty("hasSeconds", mHasSeconds);
}
+
+ /**
+ * Utility class to delegate some system event handling to allow overring the default behavior
+ *
+ * @hide
+ */
+ public static class ClockEventDelegate {
+
+ private final Context mContext;
+
+ public ClockEventDelegate(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and
+ * {@link Intent#ACTION_TIMEZONE_CHANGED}
+ *
+ * OK, this is gross but needed. This class is supported by the remote views mechanism and
+ * as a part of that the remote views can be inflated by a context for another user without
+ * the app having interact users permission - just for loading resources. For example,
+ * when adding widgets from a managed profile to the home screen. Therefore, we register
+ * the receiver as the user the app is running as not the one the context is for.
+ */
+ public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
+ final IntentFilter filter = new IntentFilter();
+
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+
+ mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler);
+ }
+
+ /**
+ * Unregisters a previously registered receiver
+ */
+ public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
+ mContext.unregisterReceiver(receiver);
+ }
+
+ /**
+ * Registers an observer for time format changes
+ */
+ public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+ Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
+ mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle);
+ }
+
+ /**
+ * Unregisters a previously registered observer
+ */
+ public void unregisterFormatChangeObserver(ContentObserver observer) {
+ mContext.getContentResolver().unregisterContentObserver(observer);
+ }
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 59344b0..a0d0656 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,6 +27,7 @@
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import android.R;
@@ -240,7 +241,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
-import com.android.text.flags.Flags;
import libcore.util.EmptyArray;
@@ -1634,7 +1634,7 @@
}
if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
+ mUseBoundsForWidth = false; // TODO: Connect to the flag.
} else {
mUseBoundsForWidth = false;
}
@@ -14151,7 +14151,8 @@
selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR);
final int line = layout.getLineForOffset(offsetTransformed);
final float insertionMarkerX =
- layout.getPrimaryHorizontal(offsetTransformed)
+ layout.getPrimaryHorizontal(
+ offsetTransformed, layout.shouldClampCursor(line))
+ viewportToContentHorizontalOffset;
final float insertionMarkerTop = layout.getLineTop(line)
+ viewportToContentVerticalOffset;
@@ -15143,6 +15144,9 @@
final ClipDescription description =
getClipboardManagerForUser().getPrimaryClipDescription();
+ if (description == null) {
+ return false;
+ }
final boolean isPlainType = description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
return (isPlainType && description.isStyledText())
|| description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 5abb6e1..eaf037e 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -18,10 +18,7 @@
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Message;
@@ -51,8 +48,6 @@
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- @UnsupportedAppUsage
- private boolean mUserPresent = true;
public ViewFlipper(Context context) {
super(context);
@@ -70,39 +65,10 @@
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -113,8 +79,6 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -186,7 +150,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void updateRunning(boolean flipNow) {
- boolean running = mVisible && mStarted && mUserPresent;
+ boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -198,7 +162,7 @@
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/android/widget/inline/TEST_MAPPING b/core/java/android/widget/inline/TEST_MAPPING
index 26a5569..82c6f61 100644
--- a/core/java/android/widget/inline/TEST_MAPPING
+++ b/core/java/android/widget/inline/TEST_MAPPING
@@ -8,6 +8,9 @@
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index 2747233a..8d71a8e 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -109,6 +109,12 @@
}
/**
+ * @return the custom home component specified for the relevant display, if any.
+ */
+ @Nullable
+ public abstract ComponentName getCustomHomeComponent();
+
+ /**
* Returns {@code true} if all of the given activities can be launched on this virtual display
* in the configuration defined by the rest of the arguments.
*
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index f40874b..7585826 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -237,13 +237,14 @@
PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
- if (background == null) {
+ final Canvas c = background != null ? background.lockCanvas() : null;
+ if (c == null) {
Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for "
+ mTitle);
+ mTransaction.clear();
+ childSurfaceControl.release();
return;
}
- // TODO: Support this on HardwareBuffer
- final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
background.unlockCanvasAndPost(c);
mTransaction.setBuffer(mRootSurface,
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 451acbe..a88e394 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -122,7 +122,7 @@
TYPE_PARAMETER_PROCESS_RUNNING,
TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
TYPE_PARAMETER_ACTIVITY_CREATED,
- TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
+ TYPE_PARAMETER_ALLOW_ICON,
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
TYPE_PARAMETER_WINDOWLESS,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
@@ -143,7 +143,7 @@
/** @hide */
public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
/** @hide */
- public static final int TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN = 0x00000020;
+ public static final int TYPE_PARAMETER_ALLOW_ICON = 0x00000020;
/**
* The parameter which indicates if the activity has finished drawing.
* @hide
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index edea297..9b10a7f 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -36,11 +36,17 @@
private final @WindowManager.TransitionType int mType;
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
private @Nullable ActivityManager.RunningTaskInfo mTriggerTask;
+ /**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ private @Nullable ActivityManager.RunningTaskInfo mPipTask;
+
/** If non-null, a remote-transition associated with the source of this transition. */
private @Nullable RemoteTransition mRemoteTransition;
@@ -59,7 +65,8 @@
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
- this(type, triggerTask, remoteTransition, null /* displayChange */, 0 /* flags */);
+ this(type, triggerTask, null /* pipTask */,
+ remoteTransition, null /* displayChange */, 0 /* flags */);
}
/** constructor override */
@@ -68,7 +75,17 @@
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
int flags) {
- this(type, triggerTask, remoteTransition, null /* displayChange */, flags);
+ this(type, triggerTask, null /* pipTask */,
+ remoteTransition, null /* displayChange */, flags);
+ }
+
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ int flags) {
+ this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
}
/** Requested change to a display. */
@@ -246,7 +263,7 @@
};
@DataClass.Generated(
- time = 1691627678294L,
+ time = 1693425051905L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -283,6 +300,9 @@
* @param triggerTask
* If non-null, If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
+ * @param pipTask
+ * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * finish) has caused this transition to occur.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -296,6 +316,7 @@
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable ActivityManager.RunningTaskInfo pipTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
@@ -303,6 +324,7 @@
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
+ this.mPipTask = pipTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -319,7 +341,7 @@
}
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@DataClass.Generated.Member
@@ -328,6 +350,15 @@
}
/**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ActivityManager.RunningTaskInfo getPipTask() {
+ return mPipTask;
+ }
+
+ /**
* If non-null, a remote-transition associated with the source of this transition.
*/
@DataClass.Generated.Member
@@ -354,7 +385,7 @@
}
/**
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@DataClass.Generated.Member
@@ -364,6 +395,16 @@
}
/**
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull TransitionRequestInfo setPipTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
+ mPipTask = value;
+ return this;
+ }
+
+ /**
* If non-null, a remote-transition associated with the source of this transition.
*/
@DataClass.Generated.Member
@@ -392,6 +433,7 @@
return "TransitionRequestInfo { " +
"type = " + mType + ", " +
"triggerTask = " + mTriggerTask + ", " +
+ "pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
"flags = " + mFlags +
@@ -406,11 +448,13 @@
byte flg = 0;
if (mTriggerTask != null) flg |= 0x2;
- if (mRemoteTransition != null) flg |= 0x4;
- if (mDisplayChange != null) flg |= 0x8;
+ if (mPipTask != null) flg |= 0x4;
+ if (mRemoteTransition != null) flg |= 0x8;
+ if (mDisplayChange != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeInt(mType);
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
+ if (mPipTask != null) dest.writeTypedObject(mPipTask, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
@@ -430,14 +474,16 @@
byte flg = in.readByte();
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
- RemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
- TransitionRequestInfo.DisplayChange displayChange = (flg & 0x8) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
+ ActivityManager.RunningTaskInfo pipTask = (flg & 0x4) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
+ TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
+ this.mPipTask = pipTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -460,10 +506,10 @@
};
@DataClass.Generated(
- time = 1691627678327L,
+ time = 1693425051928L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
deleted file mode 100644
index f1d981a..0000000
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.android.window.flags"
-
-flag {
- name: "letterbox_background_wallpaper_flag"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether the letterbox wallpaper style is enabled by default"
- bug: "297195682"
-}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index b8d251f..ec5d4ff 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -15,3 +15,10 @@
description: "Whether the overlay presentation feature is enabled"
bug: "243518738"
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "task_fragment_system_organizer_flag"
+ description: "Whether the TaskFragment system organizer feature is enabled"
+ bug: "284050041"
+}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 6d8512c..7e2c017 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3024,28 +3024,31 @@
return shouldShowTabs()
&& (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
UserHandle.of(UserHandle.myUserId())).getCount() > 0
- || shouldShowContentPreviewWhenEmpty())
+ || shouldShowStickyContentPreviewWhenEmpty())
&& shouldShowContentPreview();
}
/**
- * This method could be used to override the default behavior when we hide the preview area
- * when the current tab doesn't have any items.
+ * This method could be used to override the default behavior when we hide the sticky preview
+ * area when the current tab doesn't have any items.
*
- * @return true if we want to show the content preview area even if the tab for the current
- * user is empty
+ * @return {@code true} if we want to show the sticky content preview area even if the tab for
+ * the current user is empty
*/
- protected boolean shouldShowContentPreviewWhenEmpty() {
+ protected boolean shouldShowStickyContentPreviewWhenEmpty() {
return false;
}
- /**
- * @return true if we want to show the content preview area
- */
- protected boolean shouldShowContentPreview() {
+ @Override
+ public boolean shouldShowContentPreview() {
return isSendAction(getTargetIntent());
}
+ @Override
+ public boolean shouldShowServiceTargets() {
+ return shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic();
+ }
+
private void updateStickyContentPreview() {
if (shouldShowStickyContentPreviewNoOrientationCheck()) {
// The sticky content preview is only shown when we show the work and personal tabs.
@@ -3407,11 +3410,7 @@
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (shouldShowContentPreview()
- && !ActivityManager.isLowRamDeviceStatic()) {
- return 1;
- }
- return 0;
+ return shouldShowServiceTargets() ? 1 : 0;
}
public int getAzLabelRowCount() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index f77e718..b3e828d 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -19,7 +19,6 @@
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
-import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
import android.content.ComponentName;
import android.content.Context;
@@ -426,11 +425,9 @@
}
public int getServiceTargetCount() {
- if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mChooserListCommunicator.shouldShowServiceTargets()) {
return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
}
-
return 0;
}
@@ -779,6 +776,10 @@
void sendListViewUpdateMessage(UserHandle userHandle);
boolean isSendAction(Intent targetIntent);
+
+ boolean shouldShowContentPreview();
+
+ boolean shouldShowServiceTargets();
}
/**
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 8de448b..cb2d934 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -80,6 +80,15 @@
public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = releasedFlag(
"persist.sysui.notification.propagate_channel_updates_to_conversations");
+
+ // TODO: b/291907312 - remove feature flags
+ /** Gating the NMS->NotificationAttentionHelper buzzBeepBlink refactor */
+ public static final Flag ENABLE_ATTENTION_HELPER_REFACTOR = devFlag(
+ "persist.debug.sysui.notification.enable_attention_helper_refactor");
+
+ /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
+ public static final Flag VISIT_RISKY_URIS = devFlag(
+ "persist.sysui.notification.visit_risky_uris");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 18414cf..556c246 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -355,9 +355,10 @@
obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest));
}
- private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) {
- mUnfinishedRequests.remove(finshedRequest);
-
+ private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finishedRequest) {
+ synchronized (mUnfinishedRequests) {
+ mUnfinishedRequests.remove(finishedRequest);
+ }
if (mUnfinishedRequests.isEmpty()) {
scheduleUnbind();
}
@@ -460,7 +461,9 @@
} else {
if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
- mUnfinishedRequests.add(pendingRequest);
+ synchronized (mUnfinishedRequests) {
+ mUnfinishedRequests.add(pendingRequest);
+ }
cancelScheduledUnbind();
pendingRequest.run();
diff --git a/core/java/com/android/internal/policy/AttributeCache.java b/core/java/com/android/internal/policy/AttributeCache.java
index 1bdad25..970f511 100644
--- a/core/java/com/android/internal/policy/AttributeCache.java
+++ b/core/java/com/android/internal/policy/AttributeCache.java
@@ -16,12 +16,18 @@
package com.android.internal.policy;
+import android.annotation.RequiresPermission;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.net.Uri;
+import android.os.Handler;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.LruCache;
@@ -46,6 +52,8 @@
@GuardedBy("this")
private final Configuration mConfiguration = new Configuration();
+ private PackageMonitor mPackageMonitor;
+
public final static class Package {
public final Context context;
private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>();
@@ -77,6 +85,34 @@
}
}
+ /**
+ * Start monitor package change, so the resources can be loaded correctly.
+ */
+ void monitorPackageRemove(Handler handler) {
+ if (mPackageMonitor == null) {
+ mPackageMonitor = new PackageMonitor(mContext, handler);
+ }
+ }
+
+ static class PackageMonitor extends BroadcastReceiver {
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ PackageMonitor(Context context, Handler handler) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
+ context.registerReceiverAsUser(this, UserHandle.ALL, filter,
+ null /* broadcastPermission */, handler);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri packageUri = intent.getData();
+ if (packageUri != null) {
+ final String packageName = packageUri.getEncodedSchemeSpecificPart();
+ AttributeCache.instance().removePackage(packageName);
+ }
+ }
+ }
+
public static AttributeCache instance() {
return sInstance;
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9b5a3f7..e014ab0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -40,6 +40,9 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.SearchManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -123,6 +126,7 @@
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.DecorContentParent;
+import com.android.window.flags.Flags;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -162,6 +166,14 @@
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
+ /**
+ * Makes navigation bar color transparent by default if the target SDK is
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
+
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
(1 << FEATURE_CUSTOM_TITLE) |
(1 << FEATURE_CONTENT_TRANSITIONS) |
@@ -2525,6 +2537,8 @@
mNavigationBarColor =
navBarColor == navBarDefaultColor
+ && !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
+ && Flags.navBarTransparentByDefault())
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
? navBarCompatibleColor
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 8f4df80..40a437f 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -48,6 +48,7 @@
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.media.ImageReader;
+import android.os.Handler;
import android.os.SystemProperties;
import android.util.Slog;
import android.view.InflateException;
@@ -1399,4 +1400,14 @@
// Approximation of WCAG 2.0 relative luminance.
return ((r * 8) + (g * 22) + (b * 2)) >> 5;
}
+
+ /**
+ * For non-system server process, it must call this method to initialize the AttributeCache and
+ * start monitor package change, so the resources can be loaded correctly.
+ */
+ public static void initAttributeCache(Context context, Handler handler) {
+ AttributeCache.init(context);
+ AttributeCache.instance().monitorPackageRemove(handler);
+ }
+
}
diff --git a/core/java/com/android/internal/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java
index 74a9d16..7f7ea8b 100644
--- a/core/java/com/android/internal/security/VerityUtils.java
+++ b/core/java/com/android/internal/security/VerityUtils.java
@@ -17,8 +17,10 @@
package com.android.internal.security;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Build;
import android.os.SystemProperties;
+import android.os.incremental.V4Signature;
import android.system.Os;
import android.system.OsConstants;
import android.util.Slog;
@@ -40,6 +42,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -192,9 +197,9 @@
*
* @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#file-digest-computation">
* File digest computation in Linux kernel documentation</a>
- * @return Bytes of fs-verity digest
+ * @return Bytes of fs-verity digest, or null if the file does not have fs-verity enabled
*/
- public static byte[] getFsverityDigest(@NonNull String filePath) {
+ public static @Nullable byte[] getFsverityDigest(@NonNull String filePath) {
byte[] result = new byte[HASH_SIZE_BYTES];
int retval = measureFsverityNative(filePath, result);
if (retval < 0) {
@@ -206,6 +211,34 @@
return result;
}
+ /**
+ * Generates an fs-verity digest from a V4Signature.HashingInfo and the file's size.
+ */
+ public static @NonNull byte[] generateFsVerityDigest(long fileSize,
+ @NonNull V4Signature.HashingInfo hashingInfo)
+ throws DigestException, NoSuchAlgorithmException {
+ if (hashingInfo.rawRootHash == null || hashingInfo.rawRootHash.length != 32) {
+ throw new IllegalArgumentException("Expect a 32-byte rootHash for SHA256");
+ }
+ if (hashingInfo.log2BlockSize != 12) {
+ throw new IllegalArgumentException(
+ "Unsupported log2BlockSize: " + hashingInfo.log2BlockSize);
+ }
+
+ var buffer = ByteBuffer.allocate(256); // sizeof(fsverity_descriptor)
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put((byte) 1); // version
+ buffer.put((byte) 1); // Merkle tree hash algorithm, 1 for SHA256
+ buffer.put(hashingInfo.log2BlockSize); // log2(block-size), only log2(4096) is supported
+ buffer.put((byte) 0); // size of salt in bytes; 0 if none
+ buffer.putInt(0); // reserved, must be 0
+ buffer.putLong(fileSize); // size of file the Merkle tree is built over
+ buffer.put(hashingInfo.rawRootHash); // Merkle tree root hash
+ // The rest are zeros, including the latter half of root hash unused for SHA256.
+
+ return MessageDigest.getInstance("SHA-256").digest(buffer.array());
+ }
+
/** @hide */
@VisibleForTesting
public static byte[] toFormattedDigest(byte[] digest) {
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 52ffc98..a513ca5 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -21,6 +21,7 @@
import android.annotation.IdRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -53,10 +54,13 @@
private static final String TAG = "ResolverDrawerLayout";
private MetricsLogger mMetricsLogger;
+
+
/**
- * Max width of the whole drawer layout
+ * Max width of the whole drawer layout and its res id
*/
- private final int mMaxWidth;
+ private int mMaxWidthResId;
+ private int mMaxWidth;
/**
* Max total visible height of views not marked always-show when in the closed/initial state
@@ -152,6 +156,7 @@
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResolverDrawerLayout,
defStyleAttr, 0);
+ mMaxWidthResId = a.getResourceId(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxWidth = a.getDimensionPixelSize(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxCollapsedHeight = a.getDimensionPixelSize(
R.styleable.ResolverDrawerLayout_maxCollapsedHeight, 0);
@@ -1042,6 +1047,18 @@
return mAlwaysShowHeight;
}
+ /**
+ * Max width of the drawer needs to be updated after the configuration is changed.
+ * For example, foldables have different layout width when the device is folded and unfolded.
+ */
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mMaxWidthResId > 0) {
+ mMaxWidth = getResources().getDimensionPixelSize(mMaxWidthResId);
+ }
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = getWidth();
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fe95762..e0bcef6 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -565,6 +565,51 @@
return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
}
+static jlong android_os_Debug_getRssPid(JNIEnv* env, jobject clazz, jint pid,
+ jlongArray outMemtrack) {
+ jlong rss = 0;
+ jlong memtrack = 0;
+
+ struct graphics_memory_pss graphics_mem;
+ if (read_memtrack_memory(pid, &graphics_mem) == 0) {
+ rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
+ }
+
+ ::android::meminfo::ProcMemInfo proc_mem(pid);
+ uint64_t status_rss;
+ if (proc_mem.StatusVmRSS(&status_rss)) {
+ rss += status_rss;
+ } else {
+ return 0;
+ }
+
+ if (outMemtrack != NULL) {
+ int outLen = env->GetArrayLength(outMemtrack);
+ if (outLen >= 1) {
+ jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
+ if (outMemtrackArray != NULL) {
+ outMemtrackArray[0] = memtrack;
+ if (outLen >= 2) {
+ outMemtrackArray[1] = graphics_mem.graphics;
+ }
+ if (outLen >= 3) {
+ outMemtrackArray[2] = graphics_mem.gl;
+ }
+ if (outLen >= 4) {
+ outMemtrackArray[3] = graphics_mem.other;
+ }
+ }
+ env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
+ }
+ }
+
+ return rss;
+}
+
+static jlong android_os_Debug_getRss(JNIEnv* env, jobject clazz) {
+ return android_os_Debug_getRssPid(env, clazz, getpid(), NULL);
+}
+
// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
// Debug.java.
enum {
@@ -974,62 +1019,43 @@
*/
static const JNINativeMethod gMethods[] = {
- { "getNativeHeapSize", "()J",
- (void*) android_os_Debug_getNativeHeapSize },
- { "getNativeHeapAllocatedSize", "()J",
- (void*) android_os_Debug_getNativeHeapAllocatedSize },
- { "getNativeHeapFreeSize", "()J",
- (void*) android_os_Debug_getNativeHeapFreeSize },
- { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
- (void*) android_os_Debug_getDirtyPages },
- { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
- (void*) android_os_Debug_getDirtyPagesPid },
- { "getPss", "()J",
- (void*) android_os_Debug_getPss },
- { "getPss", "(I[J[J)J",
- (void*) android_os_Debug_getPssPid },
- { "getMemInfo", "([J)V",
- (void*) android_os_Debug_getMemInfo },
- { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
- (void*) android_os_Debug_dumpNativeHeap },
- { "dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V",
- (void*) android_os_Debug_dumpNativeMallocInfo },
- { "getBinderSentTransactions", "()I",
- (void*) android_os_Debug_getBinderSentTransactions },
- { "getBinderReceivedTransactions", "()I",
- (void*) android_os_getBinderReceivedTransactions },
- { "getBinderLocalObjectCount", "()I",
- (void*)android_os_Debug_getLocalObjectCount },
- { "getBinderProxyObjectCount", "()I",
- (void*)android_os_Debug_getProxyObjectCount },
- { "getBinderDeathObjectCount", "()I",
- (void*)android_os_Debug_getDeathObjectCount },
- { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
- (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
- { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
- (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
- { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
- (void*)android_os_Debug_getUnreachableMemory },
- { "getZramFreeKb", "()J",
- (void*)android_os_Debug_getFreeZramKb },
- { "getIonHeapsSizeKb", "()J",
- (void*)android_os_Debug_getIonHeapsSizeKb },
- { "getDmabufTotalExportedKb", "()J",
- (void*)android_os_Debug_getDmabufTotalExportedKb },
- { "getGpuPrivateMemoryKb", "()J",
- (void*)android_os_Debug_getGpuPrivateMemoryKb },
- { "getDmabufHeapTotalExportedKb", "()J",
- (void*)android_os_Debug_getDmabufHeapTotalExportedKb },
- { "getIonPoolsSizeKb", "()J",
- (void*)android_os_Debug_getIonPoolsSizeKb },
- { "getDmabufMappedSizeKb", "()J",
- (void*)android_os_Debug_getDmabufMappedSizeKb },
- { "getDmabufHeapPoolsSizeKb", "()J",
- (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
- { "getGpuTotalUsageKb", "()J",
- (void*)android_os_Debug_getGpuTotalUsageKb },
- { "isVmapStack", "()Z",
- (void*)android_os_Debug_isVmapStack },
+ {"getNativeHeapSize", "()J", (void*)android_os_Debug_getNativeHeapSize},
+ {"getNativeHeapAllocatedSize", "()J", (void*)android_os_Debug_getNativeHeapAllocatedSize},
+ {"getNativeHeapFreeSize", "()J", (void*)android_os_Debug_getNativeHeapFreeSize},
+ {"getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
+ (void*)android_os_Debug_getDirtyPages},
+ {"getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
+ (void*)android_os_Debug_getDirtyPagesPid},
+ {"getPss", "()J", (void*)android_os_Debug_getPss},
+ {"getPss", "(I[J[J)J", (void*)android_os_Debug_getPssPid},
+ {"getRss", "()J", (void*)android_os_Debug_getRss},
+ {"getRss", "(I[J)J", (void*)android_os_Debug_getRssPid},
+ {"getMemInfo", "([J)V", (void*)android_os_Debug_getMemInfo},
+ {"dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Debug_dumpNativeHeap},
+ {"dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V",
+ (void*)android_os_Debug_dumpNativeMallocInfo},
+ {"getBinderSentTransactions", "()I", (void*)android_os_Debug_getBinderSentTransactions},
+ {"getBinderReceivedTransactions", "()I", (void*)android_os_getBinderReceivedTransactions},
+ {"getBinderLocalObjectCount", "()I", (void*)android_os_Debug_getLocalObjectCount},
+ {"getBinderProxyObjectCount", "()I", (void*)android_os_Debug_getProxyObjectCount},
+ {"getBinderDeathObjectCount", "()I", (void*)android_os_Debug_getDeathObjectCount},
+ {"dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+ (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout},
+ {"dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+ (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout},
+ {"getUnreachableMemory", "(IZ)Ljava/lang/String;",
+ (void*)android_os_Debug_getUnreachableMemory},
+ {"getZramFreeKb", "()J", (void*)android_os_Debug_getFreeZramKb},
+ {"getIonHeapsSizeKb", "()J", (void*)android_os_Debug_getIonHeapsSizeKb},
+ {"getDmabufTotalExportedKb", "()J", (void*)android_os_Debug_getDmabufTotalExportedKb},
+ {"getGpuPrivateMemoryKb", "()J", (void*)android_os_Debug_getGpuPrivateMemoryKb},
+ {"getDmabufHeapTotalExportedKb", "()J",
+ (void*)android_os_Debug_getDmabufHeapTotalExportedKb},
+ {"getIonPoolsSizeKb", "()J", (void*)android_os_Debug_getIonPoolsSizeKb},
+ {"getDmabufMappedSizeKb", "()J", (void*)android_os_Debug_getDmabufMappedSizeKb},
+ {"getDmabufHeapPoolsSizeKb", "()J", (void*)android_os_Debug_getDmabufHeapPoolsSizeKb},
+ {"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb},
+ {"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack},
};
int register_android_os_Debug(JNIEnv *env)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 6ed0a8a..041f9c7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -158,12 +158,8 @@
// ****************************************************************************
// ****************************************************************************
-static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
static constexpr uint32_t GC_INTERVAL = 1000;
-static std::atomic<uint32_t> gNumProxies(0);
-static std::atomic<uint32_t> gProxiesWarned(0);
-
// Number of GlobalRefs held by JavaBBinders.
static std::atomic<uint32_t> gNumLocalRefsCreated(0);
static std::atomic<uint32_t> gNumLocalRefsDeleted(0);
@@ -776,19 +772,7 @@
return NULL;
}
BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
- if (actualNativeData == nativeData) {
- // Created a new Proxy
- uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
- uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
- if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
- // Multiple threads can get here, make sure only one of them gets to
- // update the warn counter.
- if (gProxiesWarned.compare_exchange_strong(numLastWarned,
- numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
- ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
- }
- }
- } else {
+ if (actualNativeData != nativeData) {
delete nativeData;
}
@@ -1143,7 +1127,7 @@
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
{
- return gNumProxies.load();
+ return BpBinder::getBinderProxyCount();
}
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
@@ -1428,7 +1412,6 @@
nativeData->mObject.get(), nativeData->mOrgue.get());
delete nativeData;
IPCThreadState::self()->flushCommands();
- --gNumProxies;
}
JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclass) {
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ddaeb5a..7f69e22 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -240,6 +240,36 @@
return static_cast<jboolean>(*map1 == *map2);
}
+static void nativeApplyOverlay(JNIEnv* env, jobject clazz, jlong ptr, jstring nameObj,
+ jstring overlayObj) {
+ NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+ if (!map || !map->getMap()) {
+ return;
+ }
+ ScopedUtfChars nameChars(env, nameObj);
+ ScopedUtfChars overlayChars(env, overlayObj);
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::loadContents(nameChars.c_str(), overlayChars.c_str(),
+ KeyCharacterMap::Format::OVERLAY);
+ if (ret.ok()) {
+ std::shared_ptr<KeyCharacterMap> overlay = *ret;
+ map->getMap()->combine(*overlay);
+ }
+}
+
+static jint nativeGetMappedKey(JNIEnv* env, jobject clazz, jlong ptr, jint scanCode) {
+ NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+ if (!map || !map->getMap()) {
+ return 0;
+ }
+ int32_t outKeyCode;
+ status_t mapKeyRes = map->getMap()->mapKey(scanCode, /*usageCode=*/0, &outKeyCode);
+ if (mapKeyRes != OK) {
+ return 0;
+ }
+ return static_cast<jint>(outKeyCode);
+}
+
/*
* JNI registration.
*/
@@ -260,7 +290,9 @@
{"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
(void*)nativeObtainEmptyKeyCharacterMap},
{"nativeEquals", "(JJ)Z", (void*)nativeEquals},
-};
+ {"nativeApplyOverlay", "(JLjava/lang/String;Ljava/lang/String;)V",
+ (void*)nativeApplyOverlay},
+ {"nativeGetMappedKey", "(JI)I", (void*)nativeGetMappedKey}};
int register_android_view_KeyCharacterMap(JNIEnv* env)
{
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 74f64b7..9384f41 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -967,6 +967,13 @@
transaction->setFrameRateCategory(ctrl, static_cast<int8_t>(category));
}
+static void nativeSetFrameRateSelectionStrategy(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jint strategy) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setFrameRateSelectionStrategy(ctrl, static_cast<int8_t>(strategy));
+}
+
static void nativeSetFixedTransformHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint transformHint) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2173,6 +2180,8 @@
(void*)nativeSetDefaultFrameRateCompatibility},
{"nativeSetFrameRateCategory", "(JJI)V",
(void*)nativeSetFrameRateCategory},
+ {"nativeSetFrameRateSelectionStrategy", "(JJI)V",
+ (void*)nativeSetFrameRateSelectionStrategy},
{"nativeSetDisplaySurface", "(JLandroid/os/IBinder;J)V",
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 279a5d0..b9905e8 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -30,6 +30,7 @@
optional string non_localized_label = 4;
optional int32 icon = 5;
optional int32 banner = 6;
+ optional bool is_archived = 7;
}
// Proto of android.content.pm.ApplicationInfo which extends PackageItemInfo
diff --git a/core/proto/android/inputmethodservice/softinputwindow.proto b/core/proto/android/inputmethodservice/softinputwindow.proto
index e0ba6bf..32d14f0 100644
--- a/core/proto/android/inputmethodservice/softinputwindow.proto
+++ b/core/proto/android/inputmethodservice/softinputwindow.proto
@@ -27,6 +27,6 @@
reserved 2; // window_type
reserved 3; // gravity
reserved 4; // takes_focus
- optional .android.graphics.RectProto bounds = 5;
+ reserved 5; // bounds
optional int32 window_state = 6;
}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3180ffb..c09f0a3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1699,6 +1699,15 @@
android:description="@string/permdesc_cameraOpenCloseListener"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows camera access by Headless System User 0 when device is running in
+ HSUM Mode.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_cameraHeadlessSystemUser"
+ android:description="@string/permdesc_cameraHeadlessSystemUser"
+ android:protectionLevel="signature" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device sensors -->
<!-- ====================================================================== -->
@@ -7654,6 +7663,10 @@
<permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA"
android:protectionLevel="signature" />
+ <!-- @hide @TestApi Allows tests running in CTS-in-sandbox mode to launch activities -->
+ <permission android:name="android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows the holder to call health connect migration APIs.
@hide -->
<permission android:name="android.permission.MIGRATE_HEALTH_CONNECT_DATA"
diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
index 37d2fa0..196af6d 100644
--- a/core/res/res/layout/autofill_fill_dialog.xml
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -27,7 +27,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
+ android:layout_marginTop="@dimen/autofill_save_outer_margin"
android:layout_marginBottom="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index bed19a8..8b6c901 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -22,59 +22,66 @@
android:background="@drawable/autofill_bottomsheet_background"
android:orientation="vertical">
- <LinearLayout
+ <com.android.server.autofill.ui.BottomSheetLayout
android:id="@+id/autofill_save"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
+ android:layout_marginTop="@dimen/autofill_save_outer_margin"
android:background="?android:attr/colorSurface"
android:gravity="center_horizontal"
android:orientation="vertical">
-
- <LinearLayout
+ <ScrollView
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <ImageView
- android:id="@+id/autofill_save_icon"
- android:scaleType="fitCenter"
- android:layout_gravity="center"
- android:layout_height="@dimen/autofill_save_icon_max_height"
- android:layout_width="fill_parent"/>
-
- <TextView
- android:id="@+id/autofill_save_title"
+ android:layout_height="0dp"
+ android:fillViewport="true"
+ android:layout_weight="1"
+ android:layout_marginBottom="8dp">
+ <LinearLayout
+ android:layout_marginStart="@dimen/autofill_save_outer_margin"
+ android:layout_marginEnd="@dimen/autofill_save_outer_margin"
android:layout_width="fill_parent"
+ android:orientation="vertical"
android:layout_height="wrap_content"
- android:text="@string/autofill_save_title"
- android:layout_marginTop="16dp"
- android:paddingBottom="24dp"
- android:gravity="center"
- android:textAppearance="@style/AutofillSaveUiTitle">
- </TextView>
+ >
+ <ImageView
+ android:id="@+id/autofill_save_icon"
+ android:scaleType="fitCenter"
+ android:layout_gravity="center"
+ android:layout_height="@dimen/autofill_save_icon_max_height"
+ android:layout_width="fill_parent"/>
- <com.android.server.autofill.ui.CustomScrollView
- android:id="@+id/autofill_save_custom_subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"/>
+ <TextView
+ android:id="@+id/autofill_save_title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_title"
+ android:layout_marginTop="16dp"
+ android:paddingBottom="24dp"
+ android:gravity="center"
+ android:textAppearance="@style/AutofillSaveUiTitle">
+ </TextView>
+ <LinearLayout
+ android:id="@+id/autofill_save_custom_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
- </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="end"
android:clipToPadding="false"
- android:layout_marginTop="32dp"
- android:layout_marginBottom="18dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogButton"
android:orientation="horizontal"
- android:gravity="center_vertical">
-
+ android:gravity="center_vertical"
+ android:layout_marginStart="@dimen/autofill_save_outer_margin"
+ android:layout_marginEnd="@dimen/autofill_save_outer_margin"
+ >
<Button
android:id="@+id/autofill_save_no"
android:layout_width="wrap_content"
@@ -106,6 +113,5 @@
</com.android.internal.widget.ButtonBarLayout>
- </LinearLayout>
-
-</LinearLayout>
+ </com.android.server.autofill.ui.BottomSheetLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/simple_list_item_multiple_choice.xml b/core/res/res/layout/simple_list_item_multiple_choice.xml
index 440b6fd..fd9bf76 100644
--- a/core/res/res/layout/simple_list_item_multiple_choice.xml
+++ b/core/res/res/layout/simple_list_item_multiple_choice.xml
@@ -17,7 +17,8 @@
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
diff --git a/core/res/res/layout/simple_list_item_single_choice.xml b/core/res/res/layout/simple_list_item_single_choice.xml
index 02cb7f7..a66308e 100644
--- a/core/res/res/layout/simple_list_item_single_choice.xml
+++ b/core/res/res/layout/simple_list_item_single_choice.xml
@@ -17,7 +17,8 @@
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
diff --git a/core/res/res/values-sw640dp/dimens.xml b/core/res/res/values-sw640dp/dimens.xml
new file mode 100644
index 0000000..c632176
--- /dev/null
+++ b/core/res/res/values-sw640dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <!-- Top and side margins for autofill dialog on tablets -->
+ <dimen name="autofill_save_outer_margin">32dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-w640dp/bools.xml b/core/res/res/values-w640dp/bools.xml
new file mode 100644
index 0000000..64b20f7
--- /dev/null
+++ b/core/res/res/values-w640dp/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- Whether or not to include horizontal space around the dialog -->
+ <bool name="autofill_dialog_horizontal_space_included">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-w640dp/dimens.xml b/core/res/res/values-w640dp/dimens.xml
new file mode 100644
index 0000000..1f0c0b8
--- /dev/null
+++ b/core/res/res/values-w640dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <!-- How much extra space should be left around the autofill dialog -->
+ <dimen name="autofill_dialog_offset">56dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 4027f5c..af30532 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -41,6 +41,10 @@
measured in dips per second. Setting this to -1dp disables rotary encoder fling. -->
<dimen name="config_viewMaxRotaryEncoderFlingVelocity">8000dp</dimen>
+ <!-- Whether the View-based scroll haptic feedback implementation is enabled for
+ {@link InputDevice#SOURCE_ROTARY_ENCODER}s. -->
+ <bool name="config_viewBasedRotaryEncoderHapticsEnabled">true</bool>
+
<!-- Number of notifications to keep in the notification service historical archive.
Reduced intentionally for watches to retain minimal memory footprint -->
<integer name="config_notificationServiceArchiveSize">1</integer>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index fe296c7..b097a61 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -30,4 +30,6 @@
lockscreen, setting this to true should come with customized drawables. -->
<bool name="use_lock_pattern_drawable">false</bool>
<bool name="resolver_landscape_phone">true</bool>
+ <!-- Whether or not to include horizontal space around the dialog -->
+ <bool name="autofill_dialog_horizontal_space_included">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d09bf44..3d0af3d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2814,7 +2814,7 @@
<!-- Base "handwriting slop" value used by ViewConfiguration as a
movement threshold where stylus handwriting should begin. -->
- <dimen name="config_viewConfigurationHandwritingSlop">4dp</dimen>
+ <dimen name="config_viewConfigurationHandwritingSlop">2dp</dimen>
<!-- Base "hover slop" value used by ViewConfiguration as a
movement threshold under which hover is considered "stationary". -->
@@ -5451,6 +5451,10 @@
to enroll the other eligible biometric. -->
<fraction name="config_biometricNotificationFrrThreshold">25%</fraction>
+ <!-- Whether to enable the biometric notification for dual-modality device that enrolled a
+ single biometric and experiences high FRR. -->
+ <bool name="config_biometricFrrNotificationEnabled">false</bool>
+
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
only. The component must be part of a system app. -->
@@ -6707,4 +6711,7 @@
{@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER}
devices. -->
<bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">false</bool>
+ <!-- Whether the View-based scroll haptic feedback implementation is enabled for
+ {@link InputDevice#SOURCE_ROTARY_ENCODER}s. -->
+ <bool name="config_viewBasedRotaryEncoderHapticsEnabled">false</bool>
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 9bf3ce4..878e6b3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -190,6 +190,11 @@
</string-array>
<java-symbol type="array" name="config_satellite_services_supported_by_providers" />
+ <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
+ composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
+ <string name="config_satellite_esim_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_esim_identifier" />
+
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
supported. The network will be torn down on the source transport, and will be
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0f2c264a..49295fd 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -874,7 +874,7 @@
<dimen name="autofill_dataset_picker_max_height">90%</dimen>
<!-- Autofill save dialog padding -->
- <dimen name="autofill_save_outer_top_margin">24dp</dimen>
+ <dimen name="autofill_save_outer_margin">24dp</dimen>
<dimen name="autofill_save_outer_top_padding">16dp</dimen>
<dimen name="autofill_elevation">32dp</dimen>
<dimen name="autofill_save_inner_padding">16dp</dimen>
@@ -885,6 +885,9 @@
<dimen name="autofill_save_button_bar_padding">16dp</dimen>
<dimen name="autofill_dialog_corner_radius">24dp</dimen>
+ <!-- How much extra space should be left around the autofill dialog -->
+ <dimen name="autofill_dialog_offset">72dp</dimen>
+
<!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->
<dimen name="autofill_save_custom_subtitle_max_height">20%</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 591e505..fac6aac 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,6 +1444,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permdesc_cameraOpenCloseListener">This app can receive callbacks when any camera device is being opened (by what application) or closed.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_cameraHeadlessSystemUser">Allow an application or service to access camera as Headless System User.</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permdesc_cameraHeadlessSystemUser">This app can access camera as Headless System User.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_vibrate">control vibration</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f1a6f9..83fb098 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2604,6 +2604,7 @@
<!-- Biometric FRR config -->
<java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+ <java-symbol type="bool" name="config_biometricFrrNotificationEnabled" />
<!-- Biometric FRR notification messages -->
<java-symbol type="string" name="device_unlock_notification_name" />
@@ -3652,6 +3653,7 @@
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
<java-symbol type="layout" name="autofill_fill_dialog"/>
+ <java-symbol type="id" name="autofill_save_icon"/>
<java-symbol type="id" name="autofill" />
<java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/>
@@ -3708,6 +3710,10 @@
<java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/>
<java-symbol type="integer" name="autofill_max_visible_datasets" />
<java-symbol type="dimen" name="autofill_dialog_max_width" />
+ <java-symbol type="dimen" name="autofill_dialog_offset"/>
+ <java-symbol type="dimen" name="autofill_save_outer_margin"/>
+
+ <java-symbol type="bool" name="autofill_dialog_horizontal_space_included"/>
<java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
<java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill" />
@@ -5234,4 +5240,5 @@
<java-symbol type="array" name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames" />
<java-symbol type="array" name="config_tvExternalInputLoggingDeviceBrandNames" />
<java-symbol type="bool" name="config_viewRotaryEncoderHapticScrollFedbackEnabled" />
+ <java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
</resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 3b099e8..af8c69e 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -216,6 +216,9 @@
<!-- Pakistan -->
<shortcode country="pk" pattern="\\d{1,5}" free="2057" />
+ <!-- Palestine: 5 digits, known premium codes listed -->
+ <shortcode country="ps" pattern="\\d{1,5}" free="37477" />
+
<!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index fac3a0e..d34c91e 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -86,16 +86,6 @@
GameModeInfo gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
assertNotNull(gameModeInfo);
assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
- GameModeConfiguration unsupportedFpsConfig =
- new GameModeConfiguration.Builder().setFpsOverride(
- 70).setScalingFactor(0.5f).build();
- mGameManager.updateCustomGameModeConfiguration(mPackageName, unsupportedFpsConfig);
- gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
- assertNotNull(gameModeInfo);
- // TODO(b/243448953): update to non-zero FPS when matching is implemented
- assertEquals(new GameModeConfiguration.Builder().setFpsOverride(
- GameModeConfiguration.FPS_OVERRIDE_NONE).setScalingFactor(0.5f).build(),
- gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
GameModeConfiguration supportedFpsConfig =
new GameModeConfiguration.Builder().setFpsOverride(
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 7f56eb7..9cde296 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -61,11 +61,11 @@
"testng",
"servicestests-utils",
"device-time-shell-utils",
+ "testables",
],
libs: [
"android.test.runner",
- "testables",
"org.apache.http.legacy",
"android.test.base",
"android.test.mock",
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8935507..36e1223 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -33,7 +33,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
@@ -226,7 +226,7 @@
CompatibilityInfo.setOverrideInvertedScale(scale);
try {
// Send process level config change.
- ClientTransaction transaction = newTransaction(activityThread, null);
+ ClientTransaction transaction = newTransaction(activityThread);
transaction.addCallback(ConfigurationChangeItem.obtain(
new Configuration(newConfig), DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
@@ -243,7 +243,7 @@
// Send activity level config change.
newConfig.seq++;
newConfig.smallestScreenWidthDp++;
- transaction = newTransaction(activityThread, activity.getActivityToken());
+ transaction = newTransaction(activityThread);
transaction.addCallback(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), new Configuration(newConfig)));
appThread.scheduleTransaction(transaction);
@@ -444,12 +444,12 @@
activity.mConfigLatch = new CountDownLatch(1);
activity.mTestLatch = new CountDownLatch(1);
- ClientTransaction transaction = newTransaction(activityThread, null);
+ ClientTransaction transaction = newTransaction(activityThread);
transaction.addCallback(ConfigurationChangeItem.obtain(
processConfigLandscape, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
- transaction = newTransaction(activityThread, activity.getActivityToken());
+ transaction = newTransaction(activityThread);
transaction.addCallback(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), activityConfigLandscape));
transaction.addCallback(ConfigurationChangeItem.obtain(
@@ -829,7 +829,8 @@
return thread.getActivityClient(token);
}
- private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
+ @NonNull
+ private static ClientTransaction newRelaunchResumeTransaction(@NonNull Activity activity) {
final Configuration currentConfig = activity.getResources().getConfiguration();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(
activity.getActivityToken(), null, null, 0,
@@ -846,7 +847,8 @@
return transaction;
}
- private static ClientTransaction newResumeTransaction(Activity activity) {
+ @NonNull
+ private static ClientTransaction newResumeTransaction(@NonNull Activity activity) {
final ResumeActivityItem resumeStateRequest =
ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
false /* shouldSendCompatFakeFocus */);
@@ -857,7 +859,8 @@
return transaction;
}
- private static ClientTransaction newStopTransaction(Activity activity) {
+ @NonNull
+ private static ClientTransaction newStopTransaction(@NonNull Activity activity) {
final StopActivityItem stopStateRequest = StopActivityItem.obtain(
activity.getActivityToken(), 0 /* configChanges */);
@@ -867,8 +870,9 @@
return transaction;
}
- private static ClientTransaction newActivityConfigTransaction(Activity activity,
- Configuration config) {
+ @NonNull
+ private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
+ @NonNull Configuration config) {
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), config);
@@ -878,8 +882,9 @@
return transaction;
}
- private static ClientTransaction newNewIntentTransaction(Activity activity,
- List<ReferrerIntent> intents, boolean resume) {
+ @NonNull
+ private static ClientTransaction newNewIntentTransaction(@NonNull Activity activity,
+ @NonNull List<ReferrerIntent> intents, boolean resume) {
final NewIntentItem item = NewIntentItem.obtain(activity.getActivityToken(), intents,
resume);
@@ -889,13 +894,14 @@
return transaction;
}
- private static ClientTransaction newTransaction(Activity activity) {
- return newTransaction(activity.getActivityThread(), activity.getActivityToken());
+ @NonNull
+ private static ClientTransaction newTransaction(@NonNull Activity activity) {
+ return newTransaction(activity.getActivityThread());
}
- private static ClientTransaction newTransaction(ActivityThread activityThread,
- @Nullable IBinder activityToken) {
- return ClientTransaction.obtain(activityThread.getApplicationThread(), activityToken);
+ @NonNull
+ private static ClientTransaction newTransaction(@NonNull ActivityThread activityThread) {
+ return ClientTransaction.obtain(activityThread.getApplicationThread());
}
// Test activity
diff --git a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
index 08033cc..785a8a1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
@@ -66,7 +66,7 @@
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
.obtain(mToken, mConfiguration);
- final Context context = item.getContextToUpdate(mHandler, mToken);
+ final Context context = item.getContextToUpdate(mHandler);
assertEquals(mActivity, context);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index 3d252fb..531404b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -21,7 +21,6 @@
import static org.mockito.Mockito.verify;
import android.app.ClientTransactionHandler;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -46,22 +45,21 @@
@Test
public void testPreExecute() {
- ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
- ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
- ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
- IBinder token = mock(IBinder.class);
+ final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+ final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+ final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ final ClientTransactionHandler clientTransactionHandler =
+ mock(ClientTransactionHandler.class);
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
- token /* activityToken */);
+ final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
transaction.setLifecycleStateRequest(stateRequest);
transaction.preExecute(clientTransactionHandler);
- verify(callback1, times(1)).preExecute(clientTransactionHandler, token);
- verify(callback2, times(1)).preExecute(clientTransactionHandler, token);
- verify(stateRequest, times(1)).preExecute(clientTransactionHandler, token);
+ verify(callback1, times(1)).preExecute(clientTransactionHandler);
+ verify(callback2, times(1)).preExecute(clientTransactionHandler);
+ verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
index 3926cfb..d9f5523 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
@@ -24,7 +24,6 @@
import android.app.ClientTransactionHandler;
import android.content.Context;
import android.content.res.Configuration;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -49,8 +48,6 @@
@Mock
private ClientTransactionHandler mHandler;
- @Mock
- private IBinder mToken;
// Can't mock final class.
private final Configuration mConfiguration = new Configuration();
@@ -63,7 +60,7 @@
public void testGetContextToUpdate() {
final ConfigurationChangeItem item = ConfigurationChangeItem
.obtain(mConfiguration, DEVICE_ID_DEFAULT);
- final Context context = item.getContextToUpdate(mHandler, mToken);
+ final Context context = item.getContextToUpdate(mHandler);
assertEquals(ActivityThread.currentApplication(), context);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index c8d8f4b..4bbde0c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertSame;
import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -41,8 +42,11 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.function.Supplier;
@@ -60,7 +64,15 @@
@Presubmit
public class ObjectPoolTests {
- private final IBinder mActivityToken = new Binder();
+ @Mock
+ private IApplicationThread mApplicationThread;
+ @Mock
+ private IBinder mActivityToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
// 1. Check if two obtained objects from pool are not the same.
// 2. Check if the state of the object is cleared after recycling.
@@ -309,15 +321,15 @@
@Test
public void testRecycleClientTransaction() {
- ClientTransaction emptyItem = ClientTransaction.obtain(null, null);
- ClientTransaction item = ClientTransaction.obtain(null, new Binder());
+ ClientTransaction emptyItem = ClientTransaction.obtain(null);
+ ClientTransaction item = ClientTransaction.obtain(mApplicationThread);
assertNotSame(item, emptyItem);
assertNotEquals(item, emptyItem);
item.recycle();
assertEquals(item, emptyItem);
- ClientTransaction item2 = ClientTransaction.obtain(null, new Binder());
+ ClientTransaction item2 = ClientTransaction.obtain(mApplicationThread);
assertSame(item, item2);
assertNotEquals(item2, emptyItem);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a998b26..a1a2bdb 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -37,6 +37,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -230,8 +231,7 @@
when(stateRequest.getActivityToken()).thenReturn(token);
when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
- token /* activityToken */);
+ ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
transaction.setLifecycleStateRequest(stateRequest);
@@ -240,8 +240,8 @@
mExecutor.execute(transaction);
InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
- inOrder.verify(callback1).execute(eq(mTransactionHandler), eq(token), any());
- inOrder.verify(callback2).execute(eq(mTransactionHandler), eq(token), any());
+ inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
+ inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
}
@@ -254,8 +254,7 @@
// An incoming destroy transaction enters binder thread (preExecute).
final IBinder token = mock(IBinder.class);
- final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */,
- token /* activityToken */);
+ final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
destroyTransaction.setLifecycleStateRequest(
DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
destroyTransaction.preExecute(mTransactionHandler);
@@ -263,8 +262,7 @@
assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
// A previous queued launch transaction runs on main thread (execute).
- final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */,
- token /* activityToken */);
+ final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
final LaunchActivityItem launchItem =
spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
launchTransaction.addCallback(launchItem);
@@ -272,7 +270,7 @@
// The launch transaction should not be executed because its token is in the
// to-be-destroyed container.
- verify(launchItem, never()).execute(any(), any(), any());
+ verify(launchItem, never()).execute(any(), any());
// After the destroy transaction has been executed, the token should be removed.
mExecutor.execute(destroyTransaction);
@@ -286,8 +284,7 @@
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
IBinder token = mock(IBinder.class);
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
- token /* activityToken */);
+ ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(postExecItem);
// Verify resolution that should get to onPause
@@ -439,7 +436,7 @@
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
final IBinder token = mock(IBinder.class);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+ final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(activityItem);
when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
@@ -449,7 +446,7 @@
@Test
public void testActivityItemExecute() {
final IBinder token = mock(IBinder.class);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+ final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
when(activityItem.getActivityToken()).thenReturn(token);
@@ -504,12 +501,12 @@
private StubItem() {
}
- private StubItem(Parcel in) {
+ private StubItem(@NonNull Parcel in) {
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
- PendingTransactionActions pendingActions) {
+ public void execute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
}
@Override
@@ -517,12 +514,11 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
}
- public static final Parcelable.Creator<StubItem> CREATOR =
- new Parcelable.Creator<StubItem>() {
- public StubItem createFromParcel(Parcel in) {
+ public static final Parcelable.Creator<StubItem> CREATOR = new Parcelable.Creator<>() {
+ public StubItem createFromParcel(@NonNull Parcel in) {
return new StubItem(in);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index abc5d6b..7d047c9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -284,9 +284,7 @@
StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
78 /* configChanges */);
- Binder activityToken = new Binder();
-
- ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
transaction.setLifecycleStateRequest(lifecycleRequest);
@@ -307,9 +305,7 @@
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
mActivityToken, config());
- Binder activityToken = new Binder();
-
- ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addCallback(callback1);
transaction.addCallback(callback2);
@@ -328,9 +324,7 @@
StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
78 /* configChanges */);
- Binder activityToken = new Binder();
-
- ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+ ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.setLifecycleStateRequest(lifecycleRequest);
writeAndPrepareForReading(transaction);
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
index db76d26..a801a76 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
@@ -53,8 +53,6 @@
@Mock
private ClientTransactionHandler mHandler;
@Mock
- private IBinder mToken;
- @Mock
private PendingTransactionActions mPendingActions;
@Mock
private IBinder mClientToken;
@@ -72,7 +70,7 @@
public void testExecute() {
final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
.obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- item.execute(mHandler, mToken, mPendingActions);
+ item.execute(mHandler, mPendingActions);
verify(mHandler).handleWindowContextInfoChanged(mClientToken,
new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
@@ -84,7 +82,7 @@
final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
.obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- final Context context = item.getContextToUpdate(mHandler, mToken);
+ final Context context = item.getContextToUpdate(mHandler);
assertEquals(mWindowContext, context);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
index 17e0ebc..cf9935f 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -45,8 +45,6 @@
@Mock
private ClientTransactionHandler mHandler;
@Mock
- private IBinder mToken;
- @Mock
private PendingTransactionActions mPendingActions;
@Mock
private IBinder mClientToken;
@@ -60,7 +58,7 @@
public void testExecute() {
final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
mClientToken);
- item.execute(mHandler, mToken, mPendingActions);
+ item.execute(mHandler, mPendingActions);
verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
}
diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java
index 5bcc593..05fc0e0 100644
--- a/core/tests/coretests/src/android/colormodel/CamTest.java
+++ b/core/tests/coretests/src/android/colormodel/CamTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.LargeTest;
+import androidx.test.filters.LargeTest;
import org.junit.Assert;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 5dbeac2..407c6c3 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -26,6 +26,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BroadcastReceiverTests {
@@ -47,15 +50,22 @@
@Test
public void testReceiverLimit() {
final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
+ final List<EmptyReceiver> receivers = new ArrayList<>(RECEIVER_LIMIT_PER_APP);
try {
for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
- mContext.registerReceiver(new EmptyReceiver(), mockFilter,
+ final EmptyReceiver receiver = new EmptyReceiver();
+ mContext.registerReceiver(receiver, mockFilter,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ receivers.add(receiver);
}
fail("No exception thrown when registering "
+ (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
} catch (IllegalStateException ise) {
// Expected
+ } finally {
+ for (int i = receivers.size() - 1; i >= 0; i--) {
+ mContext.unregisterReceiver(receivers.remove(i));
+ }
}
}
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 5553902..37ef6cb 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -85,9 +85,11 @@
assertEquals(2, cache.getAllServicesSize(U0));
assertEquals(2, cache.getPersistentServicesSize(U0));
assertNotEmptyFileCreated(cache, U0);
+ cache.unregisterReceivers();
// Make sure all services can be loaded from xml
cache = new TestServicesCache();
assertEquals(2, cache.getPersistentServicesSize(U0));
+ cache.unregisterReceivers();
}
public void testGetAllServicesReplaceUid() {
@@ -110,6 +112,7 @@
assertTrue("UID must be updated to the new value",
uids.contains(SYSTEM_IMAGE_UID));
assertFalse("UID must be updated to the new value", uids.contains(UID2));
+ cache.unregisterReceivers();
}
public void testGetAllServicesServiceRemoved() {
@@ -118,6 +121,7 @@
cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
assertEquals(2, cache.getAllServicesSize(U0));
assertEquals(2, cache.getPersistentServicesSize(U0));
+ cache.unregisterReceivers();
// Re-read data from disk and verify services were saved
cache = new TestServicesCache();
assertEquals(2, cache.getPersistentServicesSize(U0));
@@ -125,6 +129,7 @@
cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
assertEquals(1, cache.getAllServicesSize(U0));
assertEquals(1, cache.getPersistentServicesSize(U0));
+ cache.unregisterReceivers();
}
public void testGetAllServicesMultiUser() {
@@ -137,12 +142,14 @@
assertEquals(1, cache.getAllServicesSize(U1));
assertEquals(1, cache.getPersistentServicesSize(U1));
assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
+ cache.unregisterReceivers();
// Re-read data from disk and verify services were saved
cache = new TestServicesCache();
assertEquals(1, cache.getPersistentServicesSize(U0));
assertEquals(1, cache.getPersistentServicesSize(U1));
assertNotEmptyFileCreated(cache, U0);
assertNotEmptyFileCreated(cache, U1);
+ cache.unregisterReceivers();
}
public void testOnRemove() {
@@ -158,6 +165,7 @@
cache.clearServicesForQuerying();
assertEquals(1, cache.getAllServicesSize(U0));
assertEquals(0, cache.getAllServicesSize(U1));
+ cache.unregisterReceivers();
}
public void testMigration() {
@@ -186,10 +194,12 @@
cache.addServiceForQuerying(0, r2, newServiceInfo(t2, 2));
assertEquals(2, cache.getAllServicesSize(u0));
assertEquals(0, cache.getAllServicesSize(u1));
+ cache.unregisterReceivers();
// Re-read data from disk. Verify that services were saved and old file was ignored
cache = new TestServicesCache();
assertEquals(2, cache.getPersistentServicesSize(u0));
assertEquals(0, cache.getPersistentServicesSize(u1));
+ cache.unregisterReceivers();
}
private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 9840e15..7c4136d 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -35,6 +35,7 @@
import org.junit.After;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +72,7 @@
deleteViaContentProvider(NAMESPACE, KEY2);
deleteViaContentProvider(NAMESPACE, KEY3);
DeviceConfig.clearAllLocalOverrides();
+ DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE);
}
/**
@@ -880,6 +882,7 @@
}
@Test
+ @Ignore("b/300174188")
public void syncDisabling() throws Exception {
Properties properties1 = new Properties.Builder(NAMESPACE)
.setString(KEY, VALUE)
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 87e4a42..989c992 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -55,7 +55,6 @@
* Due to how the classes are structured, we have to test it in a somewhat roundabout way. We're
* mocking out the contentProvider and are handcrafting very specific Bundles to answer the queries.
*/
-@Ignore("b/297724333")
@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -229,6 +228,8 @@
@After
public void cleanUp() throws IOException {
+ Settings.Config.clearProviderForTest();
+ Settings.Secure.clearProviderForTest();
mConfigsStorage.clear();
mSettingsStorage.clear();
mSettingsCacheGenerationStore.close();
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index fbf8a92..7ebda00 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksCoreTests",
"options": [
diff --git a/core/tests/coretests/src/android/service/quicksettings/OWNERS b/core/tests/coretests/src/android/service/quicksettings/OWNERS
new file mode 100644
index 0000000..5665490
--- /dev/null
+++ b/core/tests/coretests/src/android/service/quicksettings/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/quicksettings/OWNERS
diff --git a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
index d28eeff..04af5d7 100644
--- a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
+++ b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
@@ -28,10 +28,10 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -40,13 +40,15 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TileServiceTest {
@Mock
private IQSService.Stub mIQSService;
+ private TestableLooper mTestableLooper;
+
private IBinder mTileToken;
private TileService mTileService;
private Tile mTile;
@@ -55,6 +57,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+
mTileToken = new Binder();
when(mIQSService.asBinder()).thenCallRealMethod();
when(mIQSService.queryLocalInterface(anyString())).thenReturn(mIQSService);
@@ -72,6 +76,7 @@
when(mIQSService.getTile(mTileToken)).thenThrow(new RemoteException());
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNull(result);
}
@@ -83,6 +88,7 @@
when(mIQSService.getTile(mTileToken)).thenReturn(null);
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNotNull(result);
verify(mIQSService, never()).onStartSuccessful(any());
@@ -96,6 +102,7 @@
when(mIQSService.getTile(mTileToken)).thenReturn(mTile);
IBinder result = mTileService.onBind(intent);
+ mTestableLooper.processAllMessages();
assertNotNull(result);
verify(mIQSService).onStartSuccessful(mTileToken);
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 6cea2f3..dd8f73f 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -399,7 +399,7 @@
@SmallTest
public void testAutoLinkWebUrl_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld()
throws Exception {
- String url = "thank.you";
+ String url = "thank.unknowntld";
assertFalse("Should not match URL that does not start with a protocol and " +
"does not contain a known TLD",
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
@@ -422,7 +422,7 @@
@SmallTest
public void testAutoLinkWebUrl_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld()
throws Exception {
- String url = "Thank\u263A.you";
+ String url = "Thank\u263A.unknowntld";
assertFalse("Should not match URLs containing emoji and with unknown TLD",
Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
}
diff --git a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
index d2af2a7..3dfeb7f 100644
--- a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
+++ b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.platform.test.annotations.Presubmit;
+import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,6 +50,7 @@
private TestView mView;
@Mock ViewConfiguration mMockViewConfig;
+ @Mock FeatureFlags mMockFeatureFlags;
private HapticScrollFeedbackProvider mProvider;
@@ -56,9 +58,52 @@
public void setUp() {
mMockViewConfig = mock(ViewConfiguration.class);
setHapticScrollFeedbackEnabled(true);
+ when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+ .thenReturn(false);
mView = new TestView(InstrumentationRegistry.getContext());
- mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig);
+ mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
+ /* disabledIfViewPlaysScrollHaptics= */ true);
+ }
+
+ @Test
+ public void testRotaryEncoder_noFeedbackWhenViewBasedFeedbackIsEnabled() {
+ when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+ .thenReturn(true);
+ setHapticScrollTickInterval(5);
+
+ mProvider.onScrollProgress(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+ /* deltaInPixels= */ 10);
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+ mProvider.onScrollLimit(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+ /* isStart= */ true);
+
+ assertNoFeedback(mView);
+ }
+
+ @Test
+ public void testRotaryEncoder_feedbackWhenDisregardingViewBasedScrollHaptics() {
+ mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
+ /* disabledIfViewPlaysScrollHaptics= */ false);
+ when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+ .thenReturn(true);
+ setHapticScrollTickInterval(5);
+
+ mProvider.onScrollProgress(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+ /* deltaInPixels= */ 10);
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+ mProvider.onScrollLimit(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+ /* isStart= */ true);
+
+ assertFeedbackCount(mView, SCROLL_TICK, 1);
+ assertFeedbackCount(mView, SCROLL_ITEM_FOCUS, 1);
+ assertFeedbackCount(mView, SCROLL_LIMIT, 1);
}
@Test
@@ -94,20 +139,26 @@
@Test
public void testScrollLimit_start() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ true);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_stop() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
@@ -207,8 +258,6 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* deltaInPixels= */ 60);
-
-
assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_TICK, 2);
}
@@ -225,6 +274,9 @@
@Test
public void testScrollLimit_startAndEndLimit_playsOnlyOneFeedback() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -232,11 +284,14 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ true);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_doubleStartLimit_playsOnlyOneFeedback() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ true);
@@ -244,11 +299,14 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ true);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_doubleEndLimit_playsOnlyOneFeedback() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
+
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -256,11 +314,13 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_notEnabledWithZeroProgress() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -275,11 +335,13 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_enabledWithProgress() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -291,11 +353,13 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2);
}
@Test
public void testScrollLimit_enabledWithSnap() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -310,7 +374,9 @@
}
@Test
- public void testScrollLimit_enabledWithDissimilarSnap() {
+ public void testScrollLimit_notEnabledWithDissimilarSnap() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -321,11 +387,13 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
@Test
public void testScrollLimit_enabledWithDissimilarProgress() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
@@ -337,28 +405,27 @@
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 2);
}
@Test
- public void testScrollLimit_enabledWithMotionFromDifferentDeviceId() {
+ public void testScrollLimit_doesNotEnabledWithMotionFromDifferentDeviceId() {
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- mProvider.onScrollLimit(
- INPUT_DEVICE_2,
- InputDevice.SOURCE_ROTARY_ENCODER,
- MotionEvent.AXIS_SCROLL,
- /* isStart= */ false);
+ mProvider.onSnapToItem(
+ INPUT_DEVICE_2, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL);
mProvider.onScrollLimit(
INPUT_DEVICE_1,
InputDevice.SOURCE_ROTARY_ENCODER,
MotionEvent.AXIS_SCROLL,
/* isStart= */ false);
- assertOnlyFeedback(mView, HapticFeedbackConstants.SCROLL_LIMIT, 3);
+ assertFeedbackCount(mView, HapticFeedbackConstants.SCROLL_LIMIT, 1);
}
diff --git a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
new file mode 100644
index 0000000..9a5c1c5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
+import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
+import static android.view.MotionEvent.ACTION_SCROLL;
+import static android.view.MotionEvent.AXIS_HSCROLL;
+import static android.view.MotionEvent.AXIS_SCROLL;
+import static android.view.MotionEvent.AXIS_VSCROLL;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/** Test for the rotary scroll haptics implementation in the View class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public final class RotaryScrollHapticsTest {
+ private static final int TEST_ROTARY_DEVICE_ID = 1;
+ private static final int TEST_RANDOM_DEVICE_ID = 2;
+
+ private static final float TEST_SCALED_VERTICAL_SCROLL_FACTOR = 5f;
+
+ @Mock ViewConfiguration mMockViewConfig;
+ @Mock HapticScrollFeedbackProvider mMockScrollFeedbackProvider;
+
+ private TestGenericMotionEventControllingView mView;
+
+ @Before
+ public void setUp() {
+ mMockViewConfig = mock(ViewConfiguration.class);
+ mMockScrollFeedbackProvider = mock(HapticScrollFeedbackProvider.class);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mView = new TestGenericMotionEventControllingView(context);
+ mView.mScrollFeedbackProvider = mMockScrollFeedbackProvider;
+
+ ViewConfiguration.setInstanceForTesting(context, mMockViewConfig);
+ when(mMockViewConfig.getScaledVerticalScrollFactor())
+ .thenReturn(TEST_SCALED_VERTICAL_SCROLL_FACTOR);
+ mockRotaryScrollHapticsEnabled(true);
+ }
+
+ @After
+ public void tearDown() {
+ ViewConfiguration.resetCacheForTesting();
+ }
+
+ @Test
+ public void testRotaryScrollHapticsDisabled_producesNoHapticEvent() {
+ mockRotaryScrollHapticsEnabled(false);
+
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ false);
+ mView.dispatchGenericMotionEvent(createRotaryEvent(-20));
+
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ true);
+ mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ true);
+ mView.dispatchGenericMotionEvent(createRotaryEvent(10));
+
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ false);
+ mView.dispatchGenericMotionEvent(createRotaryEvent(-10));
+
+ verifyNoScrollLimit();
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testNonRotaryEncoderMotion_producesNoHapticEvent() {
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ false);
+ mView.dispatchGenericMotionEvent(createGenericPointerEvent(1, 2));
+
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ true);
+ mView.dispatchGenericMotionEvent(createGenericPointerEvent(2, 2));
+
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ true);
+ mView.dispatchGenericMotionEvent(createGenericPointerEvent(1, 3));
+
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ false);
+ mView.dispatchGenericMotionEvent(createGenericPointerEvent(-1, -2));
+
+ verifyNoScrollLimit();
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testScrollLimit_start_genericMotionEventCallbackReturningFalse_doesScrollLimit() {
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ false);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+
+ verifyScrollLimit(/* isStart= */ true);
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testScrollLimit_start_genericMotionEventCallbackReturningTrue_doesScrollLimit() {
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ false);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+
+ verifyScrollLimit(/* isStart= */ true);
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testScrollLimit_end_genericMotionEventCallbackReturningFalse_doesScrollLimit() {
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ false);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(-20));
+
+ verifyScrollLimit(/* isStart= */ false);
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testScrollLimit_end_genericMotionEventCallbackReturningTrue_doesScrollLimit() {
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ false);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(-20));
+
+ verifyScrollLimit(/* isStart= */ false);
+ verifyNoScrollProgress();
+ }
+
+ @Test
+ public void testScrollProgress_genericMotionEventCallbackReturningFalse_doesScrollProgress() {
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ true);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+
+ verifyScrollProgress(-1 * 20 * (int) TEST_SCALED_VERTICAL_SCROLL_FACTOR);
+ verifyNoScrollLimit();
+ }
+
+ @Test
+ public void testScrollProgress_genericMotionEventCallbackReturningTrue_doesScrollProgress() {
+ mView.configureGenericMotion(/* result= */ true, /* scroll= */ true);
+
+ mView.dispatchGenericMotionEvent(createRotaryEvent(-20));
+
+ verifyScrollProgress(-1 * -20 * (int) TEST_SCALED_VERTICAL_SCROLL_FACTOR);
+ verifyNoScrollLimit();
+ }
+
+ private void verifyScrollProgress(int scrollPixels) {
+ verify(mMockScrollFeedbackProvider).onScrollProgress(
+ TEST_ROTARY_DEVICE_ID, SOURCE_ROTARY_ENCODER, AXIS_SCROLL, scrollPixels);
+ }
+
+ private void verifyNoScrollProgress() {
+ verify(mMockScrollFeedbackProvider, never()).onScrollProgress(
+ anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ private void verifyScrollLimit(boolean isStart) {
+ verify(mMockScrollFeedbackProvider).onScrollLimit(
+ TEST_ROTARY_DEVICE_ID, SOURCE_ROTARY_ENCODER, AXIS_SCROLL, isStart);
+ }
+
+ private void verifyNoScrollLimit() {
+ verify(mMockScrollFeedbackProvider, never()).onScrollLimit(
+ anyInt(), anyInt(), anyInt(), anyBoolean());
+ }
+
+ private void mockRotaryScrollHapticsEnabled(boolean enabled) {
+ when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+ .thenReturn(enabled);
+ }
+
+ /**
+ * Test implementation for View giving control on behavior of
+ * {@link View#onGenericMotionEvent(MotionEvent)}.
+ */
+ private static final class TestGenericMotionEventControllingView extends View {
+ private boolean mGenericMotionResult;
+ private boolean mScrollOnGenericMotion;
+
+ TestGenericMotionEventControllingView(Context context) {
+ super(context);
+ }
+
+ void configureGenericMotion(boolean result, boolean scroll) {
+ mGenericMotionResult = result;
+ mScrollOnGenericMotion = scroll;
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if (mScrollOnGenericMotion) {
+ scrollTo(100, 200); // scroll values random (not relevant for tests).
+ }
+ return mGenericMotionResult;
+ }
+ }
+
+ private static MotionEvent createRotaryEvent(float scroll) {
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.setAxisValue(AXIS_SCROLL, scroll);
+
+ return createGenericMotionEvent(
+ TEST_ROTARY_DEVICE_ID, SOURCE_ROTARY_ENCODER, ACTION_SCROLL, coords);
+ }
+
+ private static MotionEvent createGenericPointerEvent(float hScroll, float vScroll) {
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.setAxisValue(AXIS_HSCROLL, hScroll);
+ coords.setAxisValue(AXIS_VSCROLL, vScroll);
+
+ return createGenericMotionEvent(
+ TEST_RANDOM_DEVICE_ID, SOURCE_CLASS_POINTER, ACTION_SCROLL, coords);
+ }
+
+ private static MotionEvent createGenericMotionEvent(
+ int deviceId, int source, int action, MotionEvent.PointerCoords coords) {
+ MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
+ props.id = 0;
+
+ return MotionEvent.obtain(
+ /* downTime= */ 0, /* eventTime= */ 100, action, /* pointerCount= */ 1,
+ new MotionEvent.PointerProperties[] {props},
+ new MotionEvent.PointerCoords[] {coords},
+ /* metaState= */ 0, /* buttonState= */ 0, /* xPrecision= */ 0, /* yPrecision= */ 0,
+ deviceId, /* edgeFlags= */ 0, source, /* flags= */ 0);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
index 60a0a2a..c210fd6 100644
--- a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
@@ -90,22 +90,73 @@
assertGetChildLocalHitRegionEmpty(R.id.view_cover_top, R.id.view_cover_bottom);
}
+ @Test
+ public void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView() {
+ // In this case, two views overlap with each other and the MotionEvent is injected to the
+ // bottom view. It verifies that the hit region of the top view won't be blocked by the
+ // bottom view.
+ testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ true);
+ testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ false);
+ }
+
+ private void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(boolean isHover) {
+ // In this case, two views overlap with each other and the MotionEvent is injected to the
+ // bottom view. It verifies that the hit region of the top view won't be blocked by the
+ // bottom view.
+ mScenarioRule.getScenario().onActivity(activity -> {
+ View viewTop = activity.findViewById(R.id.view_overlap_top);
+ View viewBottom = activity.findViewById(R.id.view_overlap_bottom);
+
+ // The viewTop covers the left side of the viewBottom. To avoid the MotionEvent gets
+ // blocked by viewTop, we inject MotionEvents into viewBottom's right bottom corner.
+ float x = viewBottom.getWidth() - 1;
+ float y = viewBottom.getHeight() - 1;
+ injectMotionEvent(viewBottom, x, y, isHover);
+
+ Matrix actualMatrix = new Matrix();
+ Region actualRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+ boolean actualNotEmpty = viewTop.getParent()
+ .getChildLocalHitRegion(viewTop, actualRegion, actualMatrix, isHover);
+
+ int[] windowLocation = new int[2];
+ viewTop.getLocationInWindow(windowLocation);
+ Matrix expectMatrix = new Matrix();
+ expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]);
+ // Though viewTop and viewBottom overlaps, viewTop's hit region won't be blocked by
+ // viewBottom.
+ Region expectRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+
+ assertThat(actualNotEmpty).isTrue();
+ assertThat(actualMatrix).isEqualTo(expectMatrix);
+ assertThat(actualRegion).isEqualTo(expectRegion);
+ });
+ }
+
private void injectMotionEvent(View view, boolean isHover) {
+ float x = view.getWidth() / 2f;
+ float y = view.getHeight() / 2f;
+ injectMotionEvent(view, x, y, isHover);
+ }
+
+ /**
+ * Inject MotionEvent into the given view, at the given location specified in the view's
+ * coordinates.
+ */
+ private void injectMotionEvent(View view, float x, float y, boolean isHover) {
int[] location = new int[2];
view.getLocationInWindow(location);
- float x = location[0] + view.getWidth() / 2f;
- float y = location[1] + view.getHeight() / 2f;
+ float globalX = location[0] + x;
+ float globalY = location[1] + y;
int action = isHover ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_DOWN;
MotionEvent motionEvent = MotionEvent.obtain(/* downtime= */ 0, /* eventTime= */ 0, action,
- x, y, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
+ globalX, globalY, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
/* xPrecision= */ 1, /* yPrecision= */ 1, /* deviceId= */0, /* edgeFlags= */0);
View rootView = view.getRootView();
rootView.dispatchPointerEvent(motionEvent);
}
-
private void assertGetChildLocalHitRegion(int viewId) {
assertGetChildLocalHitRegion(viewId, /* isHover= */ true);
assertGetChildLocalHitRegion(viewId, /* isHover= */ false);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
index 2eeaf53..1c9f04a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
@@ -19,8 +19,12 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.UserHandle
import android.service.chooser.ChooserTarget
@@ -32,12 +36,15 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.R
import com.android.internal.app.ChooserListAdapter.LoadDirectShareIconTask
+import com.android.internal.app.chooser.DisplayResolveInfo
import com.android.internal.app.chooser.SelectableTargetInfo
import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
import com.android.internal.app.chooser.TargetInfo
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
@@ -46,22 +53,25 @@
@RunWith(AndroidJUnit4::class)
class ChooserListAdapterTest {
- private val packageManager = mock<PackageManager> {
- whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
- }
+ private val packageManager =
+ mock<PackageManager> { whenever(resolveActivity(any(), anyInt())).thenReturn(mock()) }
private val context = InstrumentationRegistry.getInstrumentation().getContext()
private val resolverListController = mock<ResolverListController>()
- private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
- whenever(maxRankedTargets).thenReturn(0)
- }
- private val selectableTargetInfoCommunicator =
- mock<SelectableTargetInfoCommunicator> {
- whenever(targetIntent).thenReturn(mock())
+ private val chooserListCommunicator =
+ mock<ChooserListAdapter.ChooserListCommunicator> {
+ whenever(maxRankedTargets).thenReturn(0)
}
+ private val selectableTargetInfoCommunicator =
+ mock<SelectableTargetInfoCommunicator> { whenever(targetIntent).thenReturn(mock()) }
private val chooserActivityLogger = mock<ChooserActivityLogger>()
+ @Before
+ fun setUp() {
+ whenever(resolverListController.userHandle).thenReturn(UserHandle.CURRENT)
+ }
+
private fun createChooserListAdapter(
- taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
+ taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask = createTaskProvider()
) =
ChooserListAdapterOverride(
context,
@@ -98,9 +108,8 @@
view.tag = viewHolderOne
val targetInfo = createSelectableTargetInfo()
val iconTaskOne = mock<LoadDirectShareIconTask>()
- val testTaskProvider = mock<() -> LoadDirectShareIconTask> {
- whenever(invoke()).thenReturn(iconTaskOne)
- }
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
val testSubject = createChooserListAdapter { testTaskProvider.invoke() }
testSubject.testViewBind(view, targetInfo, 0)
@@ -114,6 +123,65 @@
verify(testTaskProvider, times(1)).invoke()
}
+ @Test
+ fun getServiceTargetCount_shouldNotShowServiceTargets_returnsZero() {
+ whenever(chooserListCommunicator.shouldShowServiceTargets()).thenReturn(false)
+ val adapter = createChooserListAdapter()
+ whenever(chooserListCommunicator.maxRankedTargets).thenReturn(10)
+ addServiceTargets(adapter, targetCount = 50)
+
+ assertThat(adapter.serviceTargetCount).isEqualTo(0)
+ }
+
+ private fun createTaskProvider(): (SelectableTargetInfo?) -> LoadDirectShareIconTask {
+ val iconTaskOne = mock<LoadDirectShareIconTask>()
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
+ return { testTaskProvider.invoke() }
+ }
+
+ private fun addServiceTargets(adapter: ChooserListAdapter, targetCount: Int) {
+ val origTarget =
+ DisplayResolveInfo(
+ Intent(),
+ createResolveInfo(),
+ Intent(),
+ ResolverListAdapter.ResolveInfoPresentationGetter(context, 200, createResolveInfo())
+ )
+ val targets = mutableListOf<ChooserTarget>()
+ for (i in 1..targetCount) {
+ val score = 1f
+ val componentName = ComponentName("chooser.list.adapter", "Test$i")
+ val extras = Bundle()
+ val icon: Icon? = null
+ targets += ChooserTarget("Title $i", icon, score, componentName, extras)
+ }
+ val directShareToShortcutInfos = mapOf<ChooserTarget, ShortcutInfo>()
+ adapter.addServiceResults(
+ origTarget,
+ targets,
+ ChooserActivity.TARGET_TYPE_DEFAULT,
+ directShareToShortcutInfos
+ )
+ }
+
+ private fun createResolveInfo(): ResolveInfo {
+ val applicationInfo =
+ ApplicationInfo().apply {
+ packageName = "chooser.list.adapter"
+ name = "ChooserListAdapterTestApplication"
+ }
+ val activityInfo =
+ ActivityInfo().apply {
+ packageName = applicationInfo.packageName
+ name = "ChooserListAdapterTest"
+ }
+ activityInfo.applicationInfo = applicationInfo
+ val resolveInfo = ResolveInfo()
+ resolveInfo.activityInfo = activityInfo
+ return resolveInfo
+ }
+
private fun createSelectableTargetInfo(): SelectableTargetInfo =
SelectableTargetInfo(
context,
@@ -125,13 +193,7 @@
)
private fun createChooserTarget(): ChooserTarget =
- ChooserTarget(
- "Title",
- null,
- 1f,
- ComponentName("package", "package.Class"),
- Bundle()
- )
+ ChooserTarget("Title", null, 1f, ComponentName("package", "package.Class"), Bundle())
private fun createView(): View {
val view = FrameLayout(context)
@@ -164,23 +226,23 @@
chooserActivityLogger: ChooserActivityLogger?,
initialIntentsUserHandle: UserHandle?,
private val taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
-) : ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- resolverListController,
- chooserListCommunicator,
- selectableTargetInfoCommunicator,
- packageManager,
- chooserActivityLogger,
- initialIntentsUserHandle,
-) {
+) :
+ ChooserListAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ resolverListController,
+ chooserListCommunicator,
+ selectableTargetInfoCommunicator,
+ packageManager,
+ chooserActivityLogger,
+ initialIntentsUserHandle,
+ ) {
override fun createLoadDirectShareIconTask(
info: SelectableTargetInfo?
- ): LoadDirectShareIconTask =
- taskProvider.invoke(info)
+ ): LoadDirectShareIconTask = taskProvider.invoke(info)
fun testViewBind(view: View?, info: TargetInfo?, position: Int) {
onBindView(view, info, position)
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
index e870d60..8d825e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -99,6 +99,7 @@
if (isCharging(intent) == mExpectedChargingState) {
mReady.open();
}
+ context.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
diff --git a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
index b5e5b25..cb98855 100644
--- a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
+++ b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
@@ -364,6 +364,12 @@
TestClassLoader loader = new TestClassLoader();
parcel.readParcelableArray(loader);
},
+ (parcel, provider) -> {
+ parcel.readParcelable(null);
+ },
+ (parcel, provider) -> {
+ parcel.readParcelableArray(null);
+ },
// read lists
(parcel, provider) -> {
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
index d0645b0..23a2a7fb 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
@@ -28,7 +28,7 @@
srcs: ["src/**/*.java"],
- sdk_version: "16",
+ sdk_version: "19",
javacflags: ["-nowarn"],
@@ -43,5 +43,5 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
index c0c8aba..4ddaeef 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
@@ -40,7 +40,7 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
android_test_helper_app {
@@ -66,5 +66,5 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
index fe29416..afb111d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
@@ -28,7 +28,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
index c558153..8c57df6 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
@@ -31,7 +31,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
@@ -51,7 +51,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
index 9a4c3c5..840daab 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
@@ -5,8 +5,8 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="18"/>
+ <uses-sdk android:minSdkVersion="19"
+ android:targetSdkVersion="19"/>
<application android:name="androidx.multidex.MultiDexApplication"
android:allowBackup="true"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
index 75c753c..2244a83 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
@@ -24,5 +24,5 @@
libs: ["android-support-multidex"],
static_libs: ["androidx.test.rules"],
- sdk_version: "16",
+ sdk_version: "19",
}
diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING
index f3333d8..2f3afa6 100644
--- a/core/tests/vibrator/TEST_MAPPING
+++ b/core/tests/vibrator/TEST_MAPPING
@@ -3,7 +3,7 @@
{
"name": "FrameworksVibratorCoreTests",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3206dd2..69aa401 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -235,6 +235,7 @@
<privapp-permissions package="com.android.shell">
<!-- Needed for test only -->
+ <permission name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"/>
<permission name="android.permission.MANAGE_HEALTH_DATA"/>
<permission name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"/>
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ee2eacf..ad0ead7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -253,12 +253,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1883484959": {
- "message": "Content Recording: Display %d state is now (%d), so update recording?",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"-1872288685": {
"message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s",
"level": "VERBOSE",
@@ -445,6 +439,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
},
+ "-1700778361": {
+ "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"-1699018375": {
"message": "Adding activity %s to task %s callers: %s",
"level": "INFO",
@@ -649,12 +649,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "-1480264178": {
- "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"-1478175541": {
"message": "No longer animating wallpaper targets!",
"level": "VERBOSE",
@@ -1855,12 +1849,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-452750194": {
- "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"-451552570": {
"message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.",
"level": "DEBUG",
@@ -2071,12 +2059,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
- "-262984451": {
- "message": "Relaunch failed %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-251259736": {
"message": "No longer freezing: %s",
"level": "VERBOSE",
@@ -2377,6 +2359,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
+ "34106798": {
+ "message": "Content Recording: Display %d state was (%d), is now (%d), so update recording?",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"34682671": {
"message": "Not moving display (displayId=%d) to top. Top focused displayId=%d. Reason: FLAG_STEAL_TOP_FOCUS_DISABLED",
"level": "INFO",
@@ -2413,6 +2401,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "61363198": {
+ "message": "Auto-PIP allowed, requesting PIP mode via requestStartTransition(): %s, willAutoPip: %b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"74885950": {
"message": "Waiting for top state to be released by %s",
"level": "VERBOSE",
@@ -2425,12 +2419,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "90764070": {
- "message": "Could not report token removal to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
- },
"95216706": {
"message": "hideIme target: %s ",
"level": "DEBUG",
@@ -3079,12 +3067,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "643263584": {
- "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x %d for display %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"644675193": {
"message": "Real start recents",
"level": "DEBUG",
@@ -3991,12 +3973,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1563836923": {
- "message": "Content Recording: Unable to record task since feature is disabled %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"1577579529": {
"message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b",
"level": "ERROR",
@@ -4105,6 +4081,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1687944543": {
+ "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s, since the surface is not available.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"1699269281": {
"message": "Don't organize or trigger events for untrusted displayId=%d",
"level": "WARN",
@@ -4333,6 +4315,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1936800105": {
+ "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"1945495497": {
"message": "Focused window didn't have a valid surface drawn.",
"level": "DEBUG",
@@ -4351,12 +4339,6 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "1948483534": {
- "message": "Could not report config changes to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
- },
"1964565370": {
"message": "Starting remote animation",
"level": "INFO",
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 919a93b..0f3488b 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.maintenance.IKeystoreMaintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
@@ -51,6 +52,7 @@
* @hide
*/
public static int onUserAdded(@NonNull int userId) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserAdded(userId);
return 0;
@@ -71,6 +73,7 @@
* @hide
*/
public static int onUserRemoved(int userId) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserRemoved(userId);
return 0;
@@ -93,6 +96,7 @@
* @hide
*/
public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
+ StrictMode.noteDiskWrite();
try {
getService().onUserPasswordChanged(userId, password);
return 0;
@@ -110,6 +114,7 @@
* be cleared.
*/
public static int clearNamespace(@Domain int domain, long namespace) {
+ StrictMode.noteDiskWrite();
try {
getService().clearNamespace(domain, namespace);
return 0;
@@ -129,6 +134,7 @@
* @return UserState enum variant as integer if successful or an error
*/
public static int getState(int userId) {
+ StrictMode.noteDiskRead();
try {
return getService().getState(userId);
} catch (ServiceSpecificException e) {
@@ -144,6 +150,7 @@
* Informs Keystore 2.0 that an off body event was detected.
*/
public static void onDeviceOffBody() {
+ StrictMode.noteDiskWrite();
try {
getService().onDeviceOffBody();
} catch (Exception e) {
@@ -172,6 +179,7 @@
* * SYSTEM_ERROR if an unexpected error occurred.
*/
public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) {
+ StrictMode.noteDiskWrite();
try {
getService().migrateKeyNamespace(source, destination);
return 0;
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 00219e7..2d2dd24 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -22,6 +22,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.authorization.IKeystoreAuthorization;
import android.security.authorization.LockScreenEvent;
import android.system.keystore2.ResponseCode;
@@ -48,6 +49,7 @@
* @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
*/
public static int addAuthToken(@NonNull HardwareAuthToken authToken) {
+ StrictMode.noteSlowCall("addAuthToken");
try {
getService().addAuthToken(authToken);
return 0;
@@ -81,6 +83,7 @@
*/
public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
@Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
+ StrictMode.noteDiskWrite();
try {
if (locked) {
getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8811a7f..8045f55 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -18,6 +18,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.os.StrictMode;
import android.os.UserHandle;
import android.security.maintenance.UserState;
@@ -126,6 +127,8 @@
* a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
*/
public int addAuthToken(byte[] authToken) {
+ StrictMode.noteDiskWrite();
+
return Authorization.addAuthToken(authToken);
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index c83f298..5e16bce 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreService;
@@ -148,6 +149,8 @@
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.deleteKey(descriptor);
return 0;
@@ -158,6 +161,8 @@
* List all entries in the keystore for in the given namespace.
*/
public KeyDescriptor[] list(int domain, long namespace) throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service) -> service.listEntries(domain, namespace));
}
@@ -166,6 +171,8 @@
*/
public KeyDescriptor[] listBatch(int domain, long namespace, String startPastAlias)
throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry(
(service) -> service.listEntriesBatched(domain, namespace, startPastAlias));
}
@@ -228,6 +235,8 @@
*/
public KeyDescriptor grant(KeyDescriptor descriptor, int granteeUid, int accessVector)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleRemoteExceptionWithRetry(
(service) -> service.grant(descriptor, granteeUid, accessVector)
);
@@ -243,6 +252,8 @@
*/
public void ungrant(KeyDescriptor descriptor, int granteeUid)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.ungrant(descriptor, granteeUid);
return 0;
@@ -259,6 +270,8 @@
*/
public KeyEntryResponse getKeyEntry(@NonNull KeyDescriptor descriptor)
throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service) -> service.getKeyEntry(descriptor));
}
@@ -290,6 +303,8 @@
*/
public void updateSubcomponents(@NonNull KeyDescriptor key, byte[] publicCert,
byte[] publicCertChain) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.updateSubcomponent(key, publicCert, publicCertChain);
return 0;
@@ -305,6 +320,8 @@
*/
public void deleteKey(@NonNull KeyDescriptor descriptor)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
handleRemoteExceptionWithRetry((service) -> {
service.deleteKey(descriptor);
return 0;
@@ -315,6 +332,8 @@
* Returns the number of Keystore entries for a given domain and namespace.
*/
public int getNumberOfEntries(int domain, long namespace) throws KeyStoreException {
+ StrictMode.noteDiskRead();
+
return handleRemoteExceptionWithRetry((service)
-> service.getNumberOfEntries(domain, namespace));
}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 737ff2b..7c9b8eb 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -21,6 +21,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreOperation;
import android.system.keystore2.ResponseCode;
@@ -97,6 +98,7 @@
* @throws KeyStoreException
*/
public void updateAad(@NonNull byte[] input) throws KeyStoreException {
+ StrictMode.noteSlowCall("updateAad");
handleExceptions(() -> {
mOperation.updateAad(input);
return 0;
@@ -112,6 +114,7 @@
* @hide
*/
public byte[] update(@NonNull byte[] input) throws KeyStoreException {
+ StrictMode.noteSlowCall("update");
return handleExceptions(() -> mOperation.update(input));
}
@@ -125,6 +128,7 @@
* @hide
*/
public byte[] finish(byte[] input, byte[] signature) throws KeyStoreException {
+ StrictMode.noteSlowCall("finish");
return handleExceptions(() -> mOperation.finish(input, signature));
}
@@ -135,6 +139,7 @@
* @hide
*/
public void abort() throws KeyStoreException {
+ StrictMode.noteSlowCall("abort");
handleExceptions(() -> {
mOperation.abort();
return 0;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 9c0b46c..6ab148a 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -22,6 +22,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.keystore.BackendBusyException;
import android.security.keystore.KeyStoreConnectException;
import android.system.keystore2.AuthenticatorSpec;
@@ -75,6 +76,7 @@
*/
public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
Collection<KeyParameter> args) throws KeyStoreException {
+ StrictMode.noteDiskWrite();
while (true) {
try {
CreateOperationResponse createOperationResponse =
@@ -142,6 +144,8 @@
public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
Collection<KeyParameter> args, int flags, byte[] entropy)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleExceptions(() -> mSecurityLevel.generateKey(
descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
flags, entropy));
@@ -163,6 +167,8 @@
public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
Collection<KeyParameter> args, int flags, byte[] keyData)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
+
return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
args.toArray(new KeyParameter[args.size()]), flags, keyData));
}
@@ -186,6 +192,7 @@
@NonNull byte[] wrappedKey, byte[] maskingKey,
Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
throws KeyStoreException {
+ StrictMode.noteDiskWrite();
KeyDescriptor keyDescriptor = new KeyDescriptor();
keyDescriptor.alias = wrappedKeyDescriptor.alias;
keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
index c85b6b1..0cc4dfa 100644
--- a/keystore/java/android/security/LegacyVpnProfileStore.java
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.StrictMode;
import android.security.legacykeystore.ILegacyKeystore;
import android.util.Log;
@@ -51,6 +52,7 @@
* @hide
*/
public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
+ StrictMode.noteDiskWrite();
try {
getService().put(alias, ILegacyKeystore.UID_SELF, profile);
return true;
@@ -70,6 +72,7 @@
* @hide
*/
public static byte[] get(@NonNull String alias) {
+ StrictMode.noteDiskRead();
try {
return getService().get(alias, ILegacyKeystore.UID_SELF);
} catch (ServiceSpecificException e) {
@@ -89,6 +92,7 @@
* @hide
*/
public static boolean remove(@NonNull String alias) {
+ StrictMode.noteDiskWrite();
try {
getService().remove(alias, ILegacyKeystore.UID_SELF);
return true;
@@ -109,6 +113,7 @@
* @hide
*/
public static @NonNull String[] list(@NonNull String prefix) {
+ StrictMode.noteDiskRead();
try {
final String[] aliases = getService().list(prefix, ILegacyKeystore.UID_SELF);
for (int i = 0; i < aliases.length; ++i) {
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index e07eaa2..d481a07 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -18,6 +18,9 @@
import android.os.Environment;
import android.os.FileUtils;
+import android.os.StrictMode;
+
+import libcore.io.IoUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -28,8 +31,6 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
-import libcore.io.IoUtils;
-
/**
*@hide
*/
@@ -69,6 +70,7 @@
public byte[] generateNewKey(int numBits, String algName, String keyName)
throws NoSuchAlgorithmException {
+ StrictMode.noteDiskWrite();
// Check if key with similar name exists. If so, return null.
File keyFile = getKeyFile(keyName);
@@ -103,6 +105,7 @@
}
private File getKeyFile(String keyName) {
+ StrictMode.noteDiskWrite();
File sysKeystoreDir = new File(Environment.getDataDirectory(),
SYSTEM_KEYSTORE_DIRECTORY);
File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION);
@@ -114,6 +117,7 @@
}
public byte[] retrieveKey(String keyName) throws IOException {
+ StrictMode.noteDiskRead();
File keyFile = getKeyFile(keyName);
if (!keyFile.exists()) {
return null;
@@ -122,6 +126,7 @@
}
public void deleteKey(String keyName) {
+ StrictMode.noteDiskWrite();
// Get the file first.
File keyFile = getKeyFile(keyName);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index d129891..9ac0f6d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.security.keymint.KeyParameter;
+import android.os.StrictMode;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -137,6 +138,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
String transform = getTransform();
@@ -203,6 +205,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
mCipher.init(opmode, key, params, random);
return;
@@ -233,6 +236,7 @@
if (!(key instanceof AndroidKeyStorePrivateKey)
&& (key instanceof PrivateKey || key instanceof PublicKey)) {
try {
+ StrictMode.noteSlowCall("engineInit");
mCipher = Cipher.getInstance(getTransform());
mCipher.init(opmode, key, params, random);
return;
@@ -346,6 +350,7 @@
parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
try {
+ StrictMode.noteDiskRead();
mOperation = mKey.getSecurityLevel().createOperation(
mKey.getKeyIdDescriptor(),
parameters
@@ -521,6 +526,7 @@
@Override
protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
if (mCipher != null) {
+ StrictMode.noteSlowCall("engineUpdateAAD");
mCipher.updateAAD(input, inputOffset, inputLen);
return;
}
@@ -562,6 +568,7 @@
@Override
protected final void engineUpdateAAD(ByteBuffer src) {
if (mCipher != null) {
+ StrictMode.noteSlowCall("engineUpdateAAD");
mCipher.updateAAD(src);
return;
}
@@ -715,6 +722,7 @@
throw new NullPointerException("key == null");
}
byte[] encoded = null;
+ StrictMode.noteSlowCall("engineWrap");
if (key instanceof SecretKey) {
if ("RAW".equalsIgnoreCase(key.getFormat())) {
encoded = key.getEncoded();
@@ -807,6 +815,7 @@
throw new InvalidKeyException("Failed to unwrap key", e);
}
+ StrictMode.noteSlowCall("engineUnwrap");
switch (wrappedKeyType) {
case Cipher.SECRET_KEY:
{
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index ace2053..9d3fca8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -195,7 +195,7 @@
protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
if (!ACCEPTED_SIGNING_SCHEMES.contains(key.getAlgorithm().toLowerCase())) {
throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
- + ". Only" + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray())
+ + ". Only " + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray())
+ " supported");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
index 7292cd3..66e9f71 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -20,6 +20,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.Tag;
+import android.os.StrictMode;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keystore.KeyStoreCryptoOperation;
@@ -174,6 +175,7 @@
}
byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+ StrictMode.noteSlowCall("engineGenerateSecret");
try {
return mOperation.finish(otherPartyKeyEncoded, null);
} catch (KeyStoreException e) {
@@ -245,6 +247,7 @@
Tag.PURPOSE, KeyPurpose.AGREE_KEY
));
+ StrictMode.noteDiskWrite();
try {
mOperation =
mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index f1681ec..d283b05 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -18,6 +18,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
@@ -281,6 +282,7 @@
@Override
protected SecretKey engineGenerateKey() {
+ StrictMode.noteSlowCall("engineGenerateKey");
KeyGenParameterSpec spec = mSpec;
if (spec == null) {
throw new IllegalStateException("Not initialized");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 474b7ea..1398da3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -27,6 +27,7 @@
import android.hardware.security.keymint.SecurityLevel;
import android.hardware.security.keymint.Tag;
import android.os.Build;
+import android.os.StrictMode;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -617,6 +618,7 @@
@Override
public KeyPair generateKeyPair() {
+ StrictMode.noteSlowCall("generateKeyPair");
if (mKeyStore == null || mSpec == null) {
throw new IllegalStateException("Not initialized");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 931c2f8..d5fb49a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -189,7 +189,7 @@
protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
- + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+ + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
}
super.initKey(key);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index b4d8def..273dff1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -24,6 +24,7 @@
import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -164,6 +165,7 @@
KeyDescriptor descriptor = makeKeyDescriptor(alias);
try {
+ StrictMode.noteDiskRead();
return mKeyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -447,6 +449,7 @@
assertCanReplace(alias, targetDomain, mNamespace, descriptor);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(
((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
userCertBytes, chainBytes);
@@ -597,6 +600,7 @@
importArgs, flags, pkcs8EncodedPrivateKeyBytes);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
} catch (android.security.KeyStoreException e) {
mKeyStore.deleteKey(metadata.key);
@@ -938,6 +942,7 @@
KeyEntryResponse response = null;
try {
+ StrictMode.noteDiskRead();
response = mKeyStore.getKeyEntry(wrappingkey);
} catch (android.security.KeyStoreException e) {
throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
@@ -994,6 +999,7 @@
}
try {
+ StrictMode.noteDiskWrite();
securityLevel.importWrappedKey(
wrappedKey, wrappingkey,
entry.getWrappedKeyBytes(),
@@ -1054,6 +1060,7 @@
}
try {
+ StrictMode.noteDiskWrite();
mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
null /* publicCert - unused when used as pure certificate store. */,
encoded);
@@ -1066,6 +1073,7 @@
public void engineDeleteEntry(String alias) throws KeyStoreException {
KeyDescriptor descriptor = makeKeyDescriptor(alias);
try {
+ StrictMode.noteDiskWrite();
mKeyStore.deleteKey(descriptor);
} catch (android.security.KeyStoreException e) {
if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -1076,6 +1084,7 @@
private KeyDescriptor[] getAliasesBatch(String startPastAlias) {
try {
+ StrictMode.noteDiskRead();
return mKeyStore.listBatch(
getTargetDomain(),
mNamespace,
@@ -1103,6 +1112,7 @@
@Override
public int engineSize() {
try {
+ StrictMode.noteDiskRead();
return mKeyStore.getNumberOfEntries(
getTargetDomain(),
mNamespace
@@ -1166,6 +1176,7 @@
KeyDescriptor[] keyDescriptors = null;
try {
+ StrictMode.noteDiskRead();
keyDescriptors = mKeyStore.list(
getTargetDomain(),
mNamespace
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index c3d8f9a..a663f9f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -20,7 +20,6 @@
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
-import android.window.TaskFragmentOrganizer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -83,13 +82,7 @@
Context context = getApplication();
DeviceStateManagerFoldingFeatureProducer producer =
getFoldingFeatureProducer();
- // TODO(b/263263909) Use the organizer to tell if an Activity is embededed.
- // Need to improve our Dependency Injection and centralize the logic.
- TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(command -> {
- throw new RuntimeException("Not allowed!");
- });
- mWindowLayoutComponent = new WindowLayoutComponentImpl(context, organizer,
- producer);
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(context, producer);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 0112e32..15d14e8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -213,9 +213,6 @@
if (mRearDisplayStateRequest != null || isRearDisplayActive()) {
mRearDisplayStateRequest = null;
mDeviceStateManager.cancelStateRequest();
- } else {
- throw new IllegalStateException(
- "Unable to cancel a rear display session as there is no active session");
}
}
}
@@ -432,10 +429,6 @@
synchronized (mLock) {
if (mRearDisplayPresentationController != null) {
mDeviceStateManager.cancelStateRequest();
- } else {
- throw new IllegalStateException(
- "Unable to cancel a rear display presentation session as there is no "
- + "active session");
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a5ee19e..cdfc4c8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -879,14 +879,12 @@
// Skip resolving if the activity is on a pinned TaskFragmentContainer.
// TODO(b/243518738): skip resolving for overlay container.
- if (container != null) {
- final TaskContainer taskContainer = container.getTaskContainer();
- if (taskContainer.isTaskFragmentContainerPinned(container)) {
- return true;
- }
+ final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
+ if (container != null && taskContainer != null
+ && taskContainer.isTaskFragmentContainerPinned(container)) {
+ return true;
}
- final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
!= container) {
@@ -895,6 +893,28 @@
return true;
}
+ // Ensure the top TaskFragments are updated to the right config if activity is resolved
+ // to a new TaskFragment while pin TF exists.
+ final boolean handled = resolveActivityToContainerByRule(wct, activity, container,
+ isOnReparent);
+ if (handled && taskContainer != null) {
+ final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer();
+ if (splitPinContainer != null) {
+ final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity);
+ if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
+ @NonNull Activity activity, @Nullable TaskFragmentContainer container,
+ boolean isOnReparent) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new launched activity should always expand.
@@ -1301,6 +1321,26 @@
}
}
+ // Ensure the top TaskFragments are updated to the right config if the intent is resolved
+ // to a new TaskFragment while pin TF exists.
+ final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct,
+ taskId, intent, launchingActivity);
+ if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) {
+ final SplitPinContainer splitPinContainer =
+ launchingContainer.getTaskContainer().getSplitPinContainer();
+ if (splitPinContainer != null) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ return launchingContainer;
+ }
+
+ /**
+ * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ @Nullable
+ TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct,
+ int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new activity intent should always expand.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index ba57b76..9b84a48 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -17,6 +17,7 @@
package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.isZero;
@@ -24,7 +25,7 @@
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity;
-import android.app.ActivityClient;
+import android.app.ActivityThread;
import android.app.Application;
import android.app.WindowConfiguration;
import android.content.ComponentCallbacks;
@@ -34,8 +35,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.WindowManager;
-import android.window.TaskFragmentOrganizer;
+import android.util.Log;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -51,7 +51,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
/**
@@ -63,7 +62,7 @@
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
- private static final String TAG = "SampleExtension";
+ private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName();
private final Object mLock = new Object();
@@ -85,16 +84,15 @@
private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
mJavaToExtConsumers = new ArrayMap<>();
- private final TaskFragmentOrganizer mTaskFragmentOrganizer;
+ private final RawConfigurationChangedListener mRawConfigurationChangedListener =
+ new RawConfigurationChangedListener();
public WindowLayoutComponentImpl(@NonNull Context context,
- @NonNull TaskFragmentOrganizer taskFragmentOrganizer,
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mFoldingFeatureProducer = foldingFeatureProducer;
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- mTaskFragmentOrganizer = taskFragmentOrganizer;
}
/**
@@ -109,6 +107,7 @@
final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
synchronized (mLock) {
mJavaToExtConsumers.put(consumer, extConsumer);
+ updateListenerRegistrations();
}
addWindowLayoutInfoListener(activity, extConsumer);
}
@@ -162,6 +161,7 @@
final Consumer<WindowLayoutInfo> extConsumer;
synchronized (mLock) {
extConsumer = mJavaToExtConsumers.remove(consumer);
+ updateListenerRegistrations();
}
if (extConsumer != null) {
removeWindowLayoutInfoListener(extConsumer);
@@ -192,6 +192,17 @@
}
@GuardedBy("mLock")
+ private void updateListenerRegistrations() {
+ ActivityThread currentThread = ActivityThread.currentActivityThread();
+ if (mJavaToExtConsumers.isEmpty()) {
+ currentThread.removeConfigurationChangedListener(mRawConfigurationChangedListener);
+ } else {
+ currentThread.addConfigurationChangedListener(Runnable::run,
+ mRawConfigurationChangedListener);
+ }
+ }
+
+ @GuardedBy("mLock")
@NonNull
private Set<Context> getContextsListeningForLayoutChanges() {
return mWindowLayoutChangeListeners.keySet();
@@ -336,25 +347,28 @@
continue;
}
if (featureRect.left != 0 && featureRect.top != 0) {
- throw new IllegalArgumentException("Bounding rectangle must start at the top or "
+ Log.wtf(TAG, "Bounding rectangle must start at the top or "
+ "left of the window. BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.left == 0
&& featureRect.width() != windowConfiguration.getBounds().width()) {
- throw new IllegalArgumentException("Horizontal FoldingFeature must have full width."
+ Log.wtf(TAG, "Horizontal FoldingFeature must have full width."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.top == 0
&& featureRect.height() != windowConfiguration.getBounds().height()) {
- throw new IllegalArgumentException("Vertical FoldingFeature must have full height."
+ Log.wtf(TAG, "Vertical FoldingFeature must have full height."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
@@ -374,38 +388,11 @@
// Display features are not supported on secondary displays.
return false;
}
- final int windowingMode;
- IBinder activityToken = context.getActivityToken();
- if (activityToken != null) {
- final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
- activityToken);
- if (taskConfig == null) {
- // If we cannot determine the task configuration for any reason, it is likely that
- // we won't be able to determine its position correctly as well. DisplayFeatures'
- // bounds in this case can't be computed correctly, so we should skip.
- return false;
- }
- final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
- final WindowManager windowManager = Objects.requireNonNull(
- context.getSystemService(WindowManager.class));
- final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
- boolean isTaskExpanded = maxBounds.equals(taskBounds);
- /*
- * We need to proxy being in full screen because when a user enters PiP and exits PiP
- * the task windowingMode will report multi-window/pinned until the transition is
- * finished in WM Shell.
- * maxBounds == taskWindowBounds is a proxy check to verify the window is full screen
- */
- return isTaskExpanded;
- } else {
- // TODO(b/242674941): use task windowing mode for window context that associates with
- // activity.
- windowingMode = context.getResources().getConfiguration().windowConfiguration
- .getWindowingMode();
- }
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return !WindowConfiguration.inMultiWindowMode(windowingMode);
+
+ // We do not report folding features for Activities in PiP because the bounds are
+ // not updated fast enough and the window is too small for the UI to adapt.
+ return context.getResources().getConfiguration().windowConfiguration
+ .getWindowingMode() != WindowConfiguration.WINDOWING_MODE_PINNED;
}
@GuardedBy("mLock")
@@ -434,6 +421,16 @@
}
}
+ private final class RawConfigurationChangedListener implements
+ java.util.function.Consumer<IBinder> {
+ @Override
+ public void accept(IBinder activityToken) {
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(activityToken);
+ }
+ }
+ }
+
private final class ConfigurationChangeListener implements ComponentCallbacks {
final IBinder mToken;
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
index 4ee10f4..15837ad 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<shape android:shape="rectangle"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@android:color/white" />
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
index 3e0297a..e9df936 100644
--- a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
+++ b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
@@ -14,8 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<vector android:height="24dp" android:tint="#000000"
- android:viewportHeight="24" android:viewportWidth="24"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorSecondary">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
</vector>
+
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 7e0c207..fa56516 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -31,35 +31,35 @@
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
- android:paddingStart="8dp">
+ android:paddingStart="16dp">
<ImageView
android:id="@+id/application_icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_margin="4dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/app_icon_text" />
<TextView
android:id="@+id/application_name"
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="20dp"
android:minWidth="80dp"
android:textColor="@color/desktop_mode_caption_app_name_dark"
+ android:textAppearance="@android:style/TextAppearance.Material.Title"
android:textSize="14sp"
android:textFontWeight="500"
- android:gravity="center_vertical"
+ android:lineHeight="20dp"
+ android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
tools:text="Gmail"/>
<ImageButton
android:id="@+id/expand_menu_button"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:padding="4dp"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
android:contentDescription="@string/expand_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:tint="@color/desktop_mode_caption_expand_button_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index d93e9ba..7638132 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -26,8 +26,8 @@
<ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
- android:layout_height="42dp"
- android:paddingVertical="19dp"
+ android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
+ android:paddingVertical="16dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
index c03d240..c2ee306 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
@@ -35,7 +36,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="Gmail"
- android:textColor="@color/desktop_mode_caption_menu_text_color"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textSize="14sp"
android:textFontWeight="500"
android:lineHeight="20dp"
@@ -52,6 +53,6 @@
android:contentDescription="@string/collapse_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:rotation="180"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:background="?android:selectableItemBackgroundBorderless"/>
</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
index cdf4937..e637671 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
android:orientation="vertical"
@@ -25,7 +26,7 @@
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
<Button
@@ -33,15 +34,14 @@
android:contentDescription="@string/select_text"
android:text="@string/select_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
-
<Button
android:id="@+id/close_button"
android:contentDescription="@string/close_text"
android:text="@string/close_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
index 08d9149..c4b688d 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
android:orientation="horizontal"
@@ -26,7 +27,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/fullscreen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -36,7 +37,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/split_screen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -46,7 +47,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/float_button_text"
android:src="@drawable/desktop_mode_ic_handle_menu_floating"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -55,7 +56,7 @@
android:layout_marginStart="4dp"
android:contentDescription="@string/desktop_text"
android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_active"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index 5f9dbdb..da8abde 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -44,6 +44,12 @@
if a custom action is present before closing it. -->
<integer name="config_pipForceCloseDelay">5000</integer>
+ <!-- Animation duration when exit starting window: fade out icon -->
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
+
+ <!-- Animation delay when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_delay">0</integer>
+
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">500</integer>
diff --git a/libs/WindowManager/Shell/res/values-watch/config.xml b/libs/WindowManager/Shell/res/values-watch/config.xml
new file mode 100644
index 0000000..03736ed
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for watch products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Animation duration when exit starting window: fade out icon -->
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">50</integer>
+
+ <!-- Animation delay when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_delay">50</integer>
+
+ <!-- Animation duration when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_duration">200</integer>
+
+ <!-- Default animation type when hiding the starting window. The possible values are:
+ - 0 for radial vanish + slide up
+ - 1 for fade out -->
+ <integer name="starting_window_exit_animation_type">1</integer>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index f76a346..9bfd1b4 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,9 +68,6 @@
<color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color>
<color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
<color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
- <color name="desktop_mode_caption_menu_text_color">#191C1D</color>
- <color name="desktop_mode_caption_menu_buttons_color_inactive">#191C1D</color>
- <color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color>
<color name="desktop_mode_resize_veil_light">#EFF1F2</color>
<color name="desktop_mode_resize_veil_dark">#1C1C17</color>
<color name="desktop_mode_maximize_menu_button">#DDDACD</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index cba86c8..7faf380 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -401,6 +401,12 @@
<!-- Height of button (32dp) + 2 * margin (5dp each). -->
<dimen name="freeform_decor_caption_height">42dp</dimen>
+ <!-- Height of desktop mode caption for freeform tasks. -->
+ <dimen name="desktop_mode_freeform_decor_caption_height">42dp</dimen>
+
+ <!-- Height of desktop mode caption for fullscreen tasks. -->
+ <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
+
<!-- The width of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index d902fd4..468cfd5 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- Theme used for the activity that shows when the system forced an app to be resizable -->
<style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowBackground">@drawable/forced_resizable_background</item>
@@ -37,7 +38,7 @@
<item name="android:padding">16dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textFontWeight">500</item>
- <item name="android:textColor">@color/desktop_mode_caption_menu_text_color</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:background">?android:selectableItemBackground</item>
</style>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 66e6930..bb30c5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -155,14 +155,14 @@
Bitmap rawBadgeBitmap;
// Only populated when showing in taskbar
- BubbleBarExpandedView bubbleBarExpandedView;
+ @Nullable BubbleBarExpandedView bubbleBarExpandedView;
// These are only populated when not showing in taskbar
- BadgedImageView imageView;
- BubbleExpandedView expandedView;
+ @Nullable BadgedImageView imageView;
+ @Nullable BubbleExpandedView expandedView;
int dotColor;
Path dotPath;
- Bubble.FlyoutMessage flyoutMessage;
+ @Nullable Bubble.FlyoutMessage flyoutMessage;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
@@ -257,8 +257,16 @@
return false;
}
- // Badged bubble image
- Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon());
+ Drawable bubbleDrawable = null;
+ try {
+ // Badged bubble image
+ bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
+ b.getIcon());
+ } catch (Exception e) {
+ // If we can't create the icon we'll default to the app icon
+ Log.w(TAG, "Exception creating icon for the bubble: " + b.getKey());
+ }
+
if (bubbleDrawable == null) {
// Default to app icon
bubbleDrawable = appIcon;
@@ -268,7 +276,7 @@
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
// Raw badge bitmap never includes the important conversation ring
- info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar?
+ info.rawBadgeBitmap = b.isImportantConversation()
? iconFactory.getBadgeBitmap(badgedIcon, false).icon
: badgeBitmapInfo.icon;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index a5000fe..f9a286e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
+
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30;
@@ -36,6 +37,8 @@
import android.view.Display;
import android.view.DisplayInfo;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
/**
@@ -203,6 +206,21 @@
}
}
+ /**
+ * Gets the SnapTarget corresponding to the given {@link SnapPosition}, or null if no such
+ * SnapTarget exists.
+ */
+ @Nullable
+ public SnapTarget findSnapTarget(@SnapPosition int snapPosition) {
+ for (SnapTarget t : mTargets) {
+ if (t.snapPosition == snapPosition) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+
public float calculateDismissingFraction(int position) {
if (position < mFirstSplitTarget.position) {
return 1f - (float) (position - getStartInset())
@@ -356,9 +374,9 @@
* Adds a target at {@param position} but only if the area with size of {@param smallerSize}
* meets the minimal size requirement.
*/
- private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapTo) {
+ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
if (smallerSize >= mMinimalSizeResizableTask) {
- mTargets.add(new SnapTarget(position, position, snapTo));
+ mTargets.add(new SnapTarget(position, position, snapPosition));
}
}
@@ -419,6 +437,13 @@
}
/**
+ * Finds the {@link SnapPosition} nearest to the given position.
+ */
+ public int calculateNearestSnapPosition(int currentPosition) {
+ return snap(currentPosition, /* hardDismiss */ true).snapPosition;
+ }
+
+ /**
* Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
* if {@param increment} is negative and moves right otherwise.
*/
@@ -454,7 +479,7 @@
/**
* An int describing the placement of the divider in this snap target.
*/
- public final @SnapPosition int snapTo;
+ public final @SnapPosition int snapPosition;
public boolean isMiddleTarget;
@@ -464,15 +489,15 @@
*/
private final float distanceMultiplier;
- public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo) {
- this(position, taskPosition, snapTo, 1f);
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) {
+ this(position, taskPosition, snapPosition, 1f);
}
- public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo,
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition,
float distanceMultiplier) {
this.position = position;
this.taskPosition = taskPosition;
- this.snapTo = snapTo;
+ this.snapPosition = snapPosition;
this.distanceMultiplier = distanceMultiplier;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 4af03fd..26b5a50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
@@ -66,6 +67,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import java.io.PrintWriter;
@@ -115,7 +117,7 @@
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
- private int mDividePosition;
+ private int mDividerPosition;
private boolean mInitialized = false;
private boolean mFreezeDividerWindow = false;
private int mOrientation;
@@ -267,7 +269,14 @@
}
int getDividePosition() {
- return mDividePosition;
+ return mDividerPosition;
+ }
+
+ /**
+ * Finds the {@link SnapPosition} nearest to the current divider position.
+ */
+ public int calculateCurrentSnapPosition() {
+ return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition);
}
/**
@@ -344,16 +353,16 @@
}
private void initDividerPosition(Rect oldBounds) {
- final float snapRatio = (float) mDividePosition
+ final float snapRatio = (float) mDividerPosition
/ (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
// Estimate position by previous ratio.
final float length =
(float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
final int estimatePosition = (int) (length * snapRatio);
// Init divider position by estimated position using current bounds snap algorithm.
- mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+ mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
estimatePosition).position;
- updateBounds(mDividePosition);
+ updateBounds(mDividerPosition);
}
private void updateBounds(int position) {
@@ -467,27 +476,29 @@
}
void setDividePosition(int position, boolean applyLayoutChange) {
- mDividePosition = position;
- updateBounds(mDividePosition);
+ mDividerPosition = position;
+ updateBounds(mDividerPosition);
if (applyLayoutChange) {
mSplitLayoutHandler.onLayoutSizeChanged(this);
}
}
/** Updates divide position and split bounds base on the ratio within root bounds. */
- public void setDivideRatio(float ratio) {
- final int position = isLandscape()
- ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
- : mRootBounds.top + (int) (mRootBounds.height() * ratio);
- final DividerSnapAlgorithm.SnapTarget snapTarget =
- mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ public void setDivideRatio(@SnapPosition int snapPosition) {
+ final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
+ snapPosition);
+
+ if (snapTarget == null) {
+ throw new IllegalArgumentException("No SnapTarget for position " + snapPosition);
+ }
+
setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
public void resetDividerPosition() {
- mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
- updateBounds(mDividePosition);
+ mDividerPosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+ updateBounds(mDividerPosition);
mWinToken1 = null;
mWinToken2 = null;
mWinBounds1.setEmpty();
@@ -510,7 +521,7 @@
* target indicates dismissing split.
*/
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
- switch (snapTarget.snapTo) {
+ switch (snapTarget.snapPosition) {
case SNAP_TO_START_AND_DISMISS:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
@@ -668,8 +679,8 @@
@Override
public void onAnimationEnd(Animator animation) {
- mDividePosition = dividerPos;
- updateBounds(mDividePosition);
+ mDividerPosition = dividerPos;
+ updateBounds(mDividerPosition);
finishCallback.accept(insets);
InteractionJankMonitorUtils.endTracing(CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index c111ce6..953efa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -22,10 +22,13 @@
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
@@ -173,6 +176,18 @@
// be shown.
private boolean mKeyguardShowing;
+ /**
+ * The id of the task for the application we're currently attempting to show the user aspect
+ * ratio settings button for, or have most recently shown the button for.
+ */
+ private int mTopActivityTaskId;
+
+ /**
+ * Whether the user aspect ratio settings button has been shown for the current application
+ * associated with the task id stored in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ private boolean mHasShownUserAspectRatioSettingsButton = false;
+
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -227,6 +242,11 @@
if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
}
+
+ if (taskInfo != null && taskListener != null) {
+ updateActiveTaskInfo(taskInfo);
+ }
+
if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
@@ -319,6 +339,46 @@
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
+ /**
+ * Invoked when a new task is created or the info of an existing task has changed. Updates the
+ * shown status of the user aspect ratio settings button and the task id it relates to.
+ */
+ void updateActiveTaskInfo(@NonNull TaskInfo taskInfo) {
+ // If the activity belongs to the task we are currently tracking, don't update any variables
+ // as they are still relevant. Else, if the activity is visible and focused (the one the
+ // user can see and is using), the user aspect ratio button can potentially be displayed so
+ // start tracking the buttons visibility for this task.
+ if (mTopActivityTaskId != taskInfo.taskId && !taskInfo.isTopActivityTransparent
+ && taskInfo.isVisible && taskInfo.isFocused) {
+ mTopActivityTaskId = taskInfo.taskId;
+ setHasShownUserAspectRatioSettingsButton(false);
+ }
+ }
+
+ /**
+ * Informs the system that the user aspect ratio button has been displayed for the application
+ * associated with the task id in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ void setHasShownUserAspectRatioSettingsButton(boolean state) {
+ mHasShownUserAspectRatioSettingsButton = state;
+ }
+
+ /**
+ * Returns whether the user aspect ratio settings button has been show for the application
+ * associated with the task id in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ boolean hasShownUserAspectRatioSettingsButton() {
+ return mHasShownUserAspectRatioSettingsButton;
+ }
+
+ /**
+ * Returns the task id of the application we are currently attempting to show, of have most
+ * recently shown, the user aspect ratio settings button for.
+ */
+ int getTopActivityTaskId() {
+ return mTopActivityTaskId;
+ }
+
private boolean showOnDisplay(int displayId) {
return !mKeyguardShowing && !isImeShowingOnDisplay(displayId);
}
@@ -569,7 +629,8 @@
return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor,
- mDisappearTimeSupplier);
+ mDisappearTimeSupplier, this::hasShownUserAspectRatioSettingsButton,
+ this::setHasShownUserAspectRatioSettingsButton);
}
private void launchUserAspectRatioSettings(
@@ -577,7 +638,13 @@
final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivity(intent);
+ final ComponentName appComponent = taskInfo.topActivity;
+ if (appComponent != null) {
+ final Uri packageUri = Uri.parse("package:" + appComponent.getPackageName());
+ intent.setData(packageUri);
+ }
+ final UserHandle userHandle = UserHandle.of(taskInfo.userId);
+ mContext.startActivityAsUser(intent, userHandle);
}
private void removeLayouts(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 77aefc8..c2dec62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -37,7 +37,9 @@
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
/**
* Window manager for the user aspect ratio settings button which allows users to go to
@@ -55,6 +57,12 @@
private final ShellExecutor mShellExecutor;
+ @NonNull
+ private final Supplier<Boolean> mUserAspectRatioButtonShownChecker;
+
+ @NonNull
+ private final Consumer<Boolean> mUserAspectRatioButtonStateConsumer;
+
@VisibleForTesting
@NonNull
final CompatUIHintsState mCompatUIHintsState;
@@ -72,9 +80,13 @@
@NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
@NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
@NonNull ShellExecutor shellExecutor,
- @NonNull Function<Integer, Integer> disappearTimeSupplier) {
+ @NonNull Function<Integer, Integer> disappearTimeSupplier,
+ @NonNull Supplier<Boolean> userAspectRatioButtonStateChecker,
+ @NonNull Consumer<Boolean> userAspectRatioButtonShownConsumer) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mShellExecutor = shellExecutor;
+ mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker;
+ mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer;
mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
mCompatUIHintsState = compatUIHintsState;
mOnButtonClicked = onButtonClicked;
@@ -180,11 +192,18 @@
}
}
+ @VisibleForTesting
+ boolean isShowingButton() {
+ return (mUserAspectRatioButtonShownChecker.get()
+ && !isHideDelayReached(mNextButtonHideTimeMs));
+ }
+
private void showUserAspectRatioButton() {
if (mLayout == null) {
return;
}
mLayout.setUserAspectRatioButtonVisibility(true);
+ mUserAspectRatioButtonStateConsumer.accept(true);
// Only show by default for the first time.
if (!mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint) {
mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
@@ -210,7 +229,8 @@
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
return taskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.topActivityBoundsLetterboxed
- || taskInfo.isUserFullscreenOverrideEnabled);
+ || taskInfo.isUserFullscreenOverrideEnabled)
+ && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
}
private long getDisappearTimeMs() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index fd23d14..9f9854e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -205,6 +205,7 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
if (DesktopModeStatus.isEnabled()) {
return new DesktopModeWindowDecorViewModel(
@@ -218,6 +219,7 @@
syncQueue,
transitions,
desktopTasksController,
+ recentsTransitionHandler,
rootTaskDisplayAreaOrganizer);
}
return new CaptionWindowDecorViewModel(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 9ce0118..f8dd208 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -61,6 +60,7 @@
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -213,6 +213,7 @@
"DesktopTasksController: moveToDesktop taskId=%d",
task.taskId
)
+ exitSplitIfApplicable(wct, task)
// Bring other apps to front first
bringDesktopAppsToFront(task.displayId, wct)
addMoveToDesktopChanges(wct, task)
@@ -240,6 +241,7 @@
taskInfo.taskId
)
val wct = WindowContainerTransaction()
+ exitSplitIfApplicable(wct, taskInfo)
moveHomeTaskToFront(wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, startBounds)
@@ -319,7 +321,7 @@
task.taskId
)
val wct = WindowContainerTransaction()
- wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+ wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW)
wct.setBounds(task.token, Rect())
wct.setDensityDpi(task.token, getDefaultDensityDpi())
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -329,6 +331,13 @@
}
}
+ private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
+ if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ splitScreenController.prepareExitSplitScreen(wct,
+ splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+ }
+ }
+
/**
* The second part of the animated move to desktop transition, called after
* {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 13c0ac4..b71c48e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -76,6 +76,10 @@
private IRemoteTransition mOccludeByDreamTransition = null;
private IRemoteTransition mUnoccludeTransition = null;
+ // While set true, Keyguard has created a remote animation runner to handle the open app
+ // transition.
+ private boolean mIsLaunchingActivityOverLockscreen;
+
private final class StartedTransition {
final TransitionInfo mInfo;
final SurfaceControl.Transaction mFinishT;
@@ -120,7 +124,7 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback) {
- if (!handles(info)) {
+ if (!handles(info) || mIsLaunchingActivityOverLockscreen) {
return false;
}
@@ -313,5 +317,11 @@
mUnoccludeTransition = unoccludeTransition;
});
}
+
+ @Override
+ public void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
+ mMainExecutor.execute(() ->
+ mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
index b4b327f..33c299f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
@@ -38,4 +38,9 @@
@NonNull IRemoteTransition occludeTransition,
@NonNull IRemoteTransition occludeByDreamTransition,
@NonNull IRemoteTransition unoccludeTransition) {}
+
+ /**
+ * Notify whether keyguard has created a remote animation runner for next app launch.
+ */
+ default void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 069066e..2616b8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -34,4 +34,10 @@
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
Consumer<List<GroupedRecentTaskInfo>> callback) {
}
+
+ /**
+ * Adds the listener to be notified of whether the recent task animation is running.
+ */
+ default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 94e1b33..ccc3438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -428,6 +428,21 @@
executor.execute(() -> callback.accept(tasks));
});
}
+
+ @Override
+ public void addAnimationStateListener(Executor executor, Consumer<Boolean> listener) {
+ mMainExecutor.execute(() -> {
+ if (mTransitionHandler == null) {
+ return;
+ }
+ mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onAnimationStateChanged(boolean running) {
+ executor.execute(() -> listener.accept(running));
+ }
+ });
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 016771d..ead2f9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -76,6 +76,7 @@
private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
private final ArrayList<RecentsController> mControllers = new ArrayList<>();
+ private final ArrayList<RecentsTransitionStateListener> mStateListeners = new ArrayList<>();
/**
* List of other handlers which might need to mix recents with other things. These are checked
@@ -106,6 +107,11 @@
mMixers.remove(mixer);
}
+ /** Adds the callback for receiving the state change of transition. */
+ public void addTransitionStateListener(RecentsTransitionStateListener listener) {
+ mStateListeners.add(listener);
+ }
+
@VisibleForTesting
public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -128,6 +134,9 @@
}
final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
mixedHandler == null ? this : mixedHandler);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onTransitionStarted(transition);
+ }
if (mixer != null) {
mixer.setRecentsTransition(transition);
}
@@ -226,6 +235,13 @@
private ArrayList<TaskState> mPausingTasks = null;
/**
+ * List of tasks were pausing but closed in a subsequent merged transition. If a
+ * closing task is reopened, the leash is not initially hidden since it is already
+ * visible.
+ */
+ private ArrayList<TaskState> mClosingTasks = null;
+
+ /**
* List of tasks that we are switching to. Upon finish, these will remain visible and
* on top.
*/
@@ -376,11 +392,15 @@
}
mFinishTransaction = null;
mPausingTasks = null;
+ mClosingTasks = null;
mOpeningTasks = null;
mInfo = null;
mTransition = null;
mPendingPauseSnapshotsForCancel = null;
mControllers.remove(this);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(false);
+ }
}
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
@@ -420,6 +440,7 @@
mFinishCB = finishCB;
mFinishTransaction = finishT;
mPausingTasks = new ArrayList<>();
+ mClosingTasks = new ArrayList<>();
mOpeningTasks = new ArrayList<>();
mLeashMap = new ArrayMap<>();
mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
@@ -519,6 +540,9 @@
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
new Rect(0, 0, 0, 0), new Rect(), b);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(true);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error starting recents animation", e);
cancel("onAnimationStart() failed");
@@ -671,7 +695,10 @@
final TransitionInfo.Change change = closingTasks.get(i);
final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
if (pausingIdx >= 0) {
- mPausingTasks.remove(pausingIdx);
+ // We are closing the pausing task, but it is still visible and can be
+ // restart by another transition prior to this transition finishing
+ final TaskState closingTask = mPausingTasks.remove(pausingIdx);
+ mClosingTasks.add(closingTask);
didMergeThings = true;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" closing pausing taskId=%d", change.getTaskInfo().taskId);
@@ -707,7 +734,12 @@
for (int i = 0; i < openingTasks.size(); ++i) {
final TransitionInfo.Change change = openingTasks.get(i);
final boolean isLeaf = openingTaskIsLeafs.get(i) == 1;
- int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+ final int closingIdx = TaskState.indexOf(mClosingTasks, change);
+ if (closingIdx >= 0) {
+ // Remove opening tasks from closing set
+ mClosingTasks.remove(closingIdx);
+ }
+ final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
if (pausingIdx >= 0) {
// Something is showing/opening a previously-pausing app.
if (isLeaf) {
@@ -730,12 +762,23 @@
appearedTargets[nextTargetIdx++] = target;
// reparent into the original `mInfo` since that's where we are animating.
final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+ final boolean wasClosing = closingIdx >= 0;
t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(target.leash, layer);
- // Hide the animation leash, let listener show it.
- t.hide(target.leash);
+ if (wasClosing) {
+ // App was previously visible and is closing
+ t.show(target.leash);
+ t.setAlpha(target.leash, 1f);
+ // Also override the task alpha as it was set earlier when dispatching
+ // the transition and setting up the leash to hide the
+ t.setAlpha(change.getLeash(), 1f);
+ } else {
+ // Hide the animation leash, let the listener show it
+ t.hide(target.leash);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " opening new leaf taskId=%d", target.taskId);
+ " opening new leaf taskId=%d wasClosing=%b",
+ target.taskId, wasClosing);
mOpeningTasks.add(new TaskState(change, target.leash));
} else {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
new file mode 100644
index 0000000..e8733eb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.wm.shell.recents;
+
+import android.os.IBinder;
+
+/** The listener for the events from {@link RecentsTransitionHandler}. */
+public interface RecentsTransitionStateListener {
+
+ /** Notifies whether the recents animation is running. */
+ default void onAnimationStateChanged(boolean running) {
+ }
+
+ /** Notifies that a recents shell transition has started. */
+ default void onTransitionStarted(IBinder transition) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 14304a3..253acc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -91,42 +91,42 @@
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int taskId1, in Bundle options1, int taskId2, in Bundle options2,
- int splitPosition, float splitRatio, in RemoteTransition remoteTransition,
+ int splitPosition, int snapPosition, in RemoteTransition remoteTransition,
in InstanceId instanceId) = 10;
/**
* Starts a pair of intent and task in one transition.
*/
oneway void startIntentAndTask(in PendingIntent pendingIntent, int userId1, in Bundle options1,
- int taskId, in Bundle options2, int sidePosition, float splitRatio,
+ int taskId, in Bundle options2, int sidePosition, int snapPosition,
in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
/**
* Starts a pair of shortcut and task in one transition.
*/
oneway void startShortcutAndTask(in ShortcutInfo shortcutInfo, in Bundle options1, int taskId,
- in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options2, int splitPosition, int snapPosition,
in RemoteTransition remoteTransition, in InstanceId instanceId) = 17;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int taskId1, in Bundle options1, int taskId2,
- in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
/**
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, int userId1,
- in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
/**
* Starts a pair of shortcut and task using legacy transition system.
*/
oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo,
- in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
/**
@@ -135,7 +135,7 @@
oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1, int userId1,
in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
- float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
+ int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
/**
* Start a pair of intents in one transition.
@@ -143,7 +143,7 @@
oneway void startIntents(in PendingIntent pendingIntent1, int userId1,
in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
- float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
+ int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 991b699..ccffa02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,7 +23,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -86,6 +85,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -131,6 +131,7 @@
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
public static final int EXIT_REASON_RECREATE_SPLIT = 10;
public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
+ public static final int EXIT_REASON_ENTER_DESKTOP = 12;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -144,6 +145,7 @@
EXIT_REASON_CHILD_TASK_ENTER_PIP,
EXIT_REASON_RECREATE_SPLIT,
EXIT_REASON_FULLSCREEN_SHORTCUT,
+ EXIT_REASON_ENTER_DESKTOP
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -599,8 +601,8 @@
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (options1 == null) options1 = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
@@ -624,13 +626,14 @@
}
mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
- activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
+ activityOptions.toBundle(), taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
if (options1 == null) options1 = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
final String packageName1 = shortcutInfo.getPackage();
@@ -657,7 +660,7 @@
}
}
mStageCoordinator.startShortcutAndTask(shortcutInfo, activityOptions.toBundle(), taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId);
+ options2, splitPosition, snapPosition, remoteTransition, instanceId);
}
/**
@@ -672,8 +675,8 @@
private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
Intent fillInIntent = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
@@ -694,12 +697,12 @@
}
}
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
- options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+ options1, taskId, options2, splitPosition, snapPosition, adapter, instanceId);
}
private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
@@ -726,14 +729,14 @@
}
}
mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId);
+ options2, splitPosition, snapPosition, remoteTransition, instanceId);
}
private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -757,14 +760,15 @@
}
mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
- splitPosition, splitRatio, adapter, instanceId);
+ splitPosition, snapPosition, adapter, instanceId);
}
private void startIntents(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -799,7 +803,7 @@
}
mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
- activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ activityOptions2.toBundle(), splitPosition, snapPosition, remoteTransition,
instanceId);
}
@@ -1011,6 +1015,8 @@
return "CHILD_TASK_ENTER_PIP";
case EXIT_REASON_RECREATE_SPLIT:
return "RECREATE_SPLIT";
+ case EXIT_REASON_ENTER_DESKTOP:
+ return "ENTER_DESKTOP";
default:
return "unknown reason, reason int = " + exitReason;
}
@@ -1219,78 +1225,82 @@
@Override
public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
- taskId1, options1, taskId2, options2, splitPosition,
- splitRatio, adapter, instanceId));
+ taskId1, options1, taskId2, options2, splitPosition, snapPosition,
+ adapter, instanceId));
}
@Override
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
- Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ Bundle options1, int taskId, Bundle options2, int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startIntentAndTaskWithLegacyTransition", (controller) ->
controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
- userId1, options1, taskId, options2, splitPosition, splitRatio,
- adapter, instanceId));
+ userId1, options1, taskId, options2, splitPosition,
+ snapPosition, adapter, instanceId));
}
@Override
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, options1, taskId, options2, splitPosition,
- splitRatio, adapter, instanceId));
+ snapPosition, adapter, instanceId));
}
@Override
public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
- taskId2, options2, splitPosition, splitRatio, remoteTransition,
+ taskId2, options2, splitPosition, snapPosition, remoteTransition,
instanceId));
}
@Override
public void startIntentAndTask(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
(controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1,
- taskId, options2, splitPosition, splitRatio, remoteTransition,
+ taskId, options2, splitPosition, snapPosition, remoteTransition,
instanceId));
}
@Override
public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
(controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId));
+ options2, splitPosition, snapPosition, remoteTransition, instanceId));
}
@Override
public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
(controller) ->
controller.startIntentsWithLegacyTransition(pendingIntent1, userId1,
shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2,
- options2, splitPosition, splitRatio, adapter, instanceId)
+ options2, splitPosition, snapPosition, adapter, instanceId)
);
}
@@ -1298,13 +1308,14 @@
public void startIntents(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntents",
(controller) ->
controller.startIntents(pendingIntent1, userId1, shortcutInfo1,
options1, pendingIntent2, userId2, shortcutInfo2, options2,
- splitPosition, splitRatio, remoteTransition, instanceId)
+ splitPosition, snapPosition, remoteTransition, instanceId)
);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 5483fa5..f4ab226 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,6 +25,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -42,6 +43,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -192,6 +194,8 @@
return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
case EXIT_REASON_FULLSCREEN_SHORTCUT:
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+ case EXIT_REASON_ENTER_DESKTOP:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 94fa485..3d825f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -128,6 +128,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitWindowManager;
@@ -631,8 +632,8 @@
}
/** Starts 2 tasks in one transition. */
- void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+ void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId2 == INVALID_TASK_ID) {
@@ -654,13 +655,13 @@
addActivityOptions(options1, mSideStage);
wct.startTask(taskId1, options1);
- startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
}
/** Start an intent and a task to a split pair in one transition. */
void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
@@ -676,13 +677,14 @@
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
- startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
/** Starts a shortcut and a task to a split pair in one transition. */
void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -697,7 +699,7 @@
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
- startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
/**
@@ -708,14 +710,14 @@
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
*/
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
- @Nullable Bundle mainOptions, float splitRatio,
+ @Nullable Bundle mainOptions, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
if (!mMainStage.isActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(wct, false /* reparent */);
}
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
setRootForceTranslucent(false, wct);
@@ -740,7 +742,7 @@
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, Intent fillInIntent2,
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (pendingIntent2 == null) {
@@ -762,7 +764,7 @@
}
setSideStagePosition(splitPosition, wct);
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
setRootForceTranslucent(false, wct);
@@ -790,7 +792,7 @@
/** Starts a pair of tasks using legacy transition. */
void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId2 == INVALID_TASK_ID) {
@@ -811,7 +813,7 @@
addActivityOptions(options1, mSideStage);
wct.startTask(taskId1, options1);
mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition);
- startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId2, options2, splitPosition, snapPosition, adapter,
instanceId);
}
@@ -820,8 +822,8 @@
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
@Nullable PendingIntent pendingIntent2, Intent fillInIntent2,
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (pendingIntent2 == null) {
@@ -840,13 +842,13 @@
pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition);
}
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
- splitPosition, splitRatio, adapter, instanceId);
+ splitPosition, snapPosition, adapter, instanceId);
}
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
@@ -859,15 +861,15 @@
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition);
- startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
/** Starts a pair of shortcut and task using legacy transition. */
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
@@ -878,7 +880,7 @@
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
- startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
@@ -928,18 +930,19 @@
private void startWithLegacyTransition(WindowContainerTransaction wct,
@Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
@Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent,
- mainShortcutInfo, mainOptions, sidePosition, splitRatio, adapter, instanceId);
+ mainShortcutInfo, mainOptions, sidePosition, snapPosition, adapter, instanceId);
}
private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
- @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle mainOptions, @SplitPosition int sidePosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */,
null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition,
- splitRatio, adapter, instanceId);
+ snapPosition, adapter, instanceId);
}
/**
@@ -950,15 +953,15 @@
private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
@Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
@Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (!isSplitScreenVisible()) {
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
}
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
// Apply surface bounds before animation start.
SurfaceControl.Transaction startT = mTransactionPool.acquire();
@@ -1551,6 +1554,11 @@
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
onSplitScreenEnter();
+ // Preemptively reset the reparenting behavior if we know that we are entering, as starting
+ // split tasks with activity trampolines can inadvertently trigger the task to be
+ // reparented out of the split root mid-launch
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* setReparentLeafTaskIfRelaunch */);
if (isSplitActive()) {
prepareBringSplit(wct, taskInfo, startPosition, resizeAnim);
} else {
@@ -1761,7 +1769,7 @@
rightBottomTaskId = sideStageTopTaskId;
}
SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
- leftTopTaskId, rightBottomTaskId);
+ leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index cb76044..edb5aba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -20,7 +20,6 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
import android.animation.Animator;
-import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
import android.util.Slog;
@@ -47,7 +46,7 @@
private final int mIconFadeOutDuration;
private final int mAppRevealDelay;
private final int mAppRevealDuration;
- @ExitAnimationType
+ @SplashScreenExitAnimationUtils.ExitAnimationType
private final int mAnimationType;
private final int mAnimationDuration;
private final float mIconStartAlpha;
@@ -58,24 +57,6 @@
private Runnable mFinishCallback;
- /**
- * This splash screen exit animation type uses a radial vanish to hide
- * the starting window and slides up the main window content.
- */
- private static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
-
- /**
- * This splash screen exit animation type fades out the starting window
- * to reveal the main window content.
- */
- private static final int TYPE_FADE_OUT = 1;
-
- @IntDef(prefix = { "TYPE_" }, value = {
- TYPE_RADIAL_VANISH_SLIDE_UP,
- TYPE_FADE_OUT,
- })
- private @interface ExitAnimationType {}
-
SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
Rect frame, int mainWindowShiftLength, TransactionPool pool, Runnable handleFinish,
float roundedCornerRadius) {
@@ -121,15 +102,10 @@
}
void startAnimations() {
- if (mAnimationType == TYPE_FADE_OUT) {
- SplashScreenExitAnimationUtils.startFadeOutAnimation(mSplashScreenView,
- mAnimationDuration, this);
- } else {
- SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
- mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
- mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
- mAppRevealDuration, this, mRoundedCornerRadius);
- }
+ SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView,
+ mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame,
+ mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha,
+ mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius);
}
private void reset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
index 64f09a7..e86b62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -20,6 +20,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
@@ -54,6 +55,7 @@
public class SplashScreenExitAnimationUtils {
private static final boolean DEBUG_EXIT_ANIMATION = false;
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
private static final String TAG = "SplashScreenExitAnimationUtils";
private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
@@ -62,19 +64,47 @@
private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
/**
+ * This splash screen exit animation type uses a radial vanish to hide
+ * the starting window and slides up the main window content.
+ * @hide
+ */
+ public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
+
+ /**
+ * This splash screen exit animation type fades out the starting window
+ * to reveal the main window content.
+ * @hide
+ */
+ public static final int TYPE_FADE_OUT = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_RADIAL_VANISH_SLIDE_UP,
+ TYPE_FADE_OUT,
+ })
+ public @interface ExitAnimationType {}
+
+ /**
* Creates and starts the animator to fade out the icon, reveal the app, and shift up main
* window with rounded corner radius.
*/
- static void startAnimations(ViewGroup splashScreenView,
- SurfaceControl firstWindowSurface, int mainWindowShiftLength,
- TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
- int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
- int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
- float roundedCornerRadius) {
- ValueAnimator animator = createRadialVanishSlideUpAnimator(splashScreenView,
- firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
- animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
- appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+ static void startAnimations(@ExitAnimationType int animationType,
+ ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
+ int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
+ ValueAnimator animator;
+ if (animationType == TYPE_FADE_OUT) {
+ animator = createFadeOutAnimation(splashScreenView, animationDuration,
+ iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
+ appRevealDuration, animatorListener);
+ } else {
+ animator = createRadialVanishSlideUpAnimator(splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+ }
animator.start();
}
@@ -88,10 +118,11 @@
TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
- startAnimations(splashScreenView, firstWindowSurface, mainWindowShiftLength,
- transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
- iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
- animatorListener, 0f /* roundedCornerRadius */);
+ // Start the default 'reveal' animation.
+ startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
}
/**
@@ -209,18 +240,57 @@
return nightMode == Configuration.UI_MODE_NIGHT_YES;
}
- static void startFadeOutAnimation(ViewGroup splashScreenView,
- int animationDuration, Animator.AnimatorListener animatorListener) {
- final ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
+ private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener) {
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ splashScreenView.setBackgroundColor(Color.BLUE);
+ }
+
+ final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(animationDuration);
animator.setInterpolator(Interpolators.LINEAR);
animator.addUpdateListener(animation -> {
- splashScreenView.setAlpha((float) animation.getAnimatedValue());
+
+ float linearProgress = (float) animation.getAnimatedValue();
+
+ // Icon fade out progress (always starts immediately)
+ final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
+ linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+ View iconView = null;
+ View brandingView = null;
+
+ if (splashScreenView instanceof SplashScreenView) {
+ iconView = ((SplashScreenView) splashScreenView).getIconView();
+ brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+ }
+ if (iconView != null) {
+ iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
+ }
+ if (brandingView != null) {
+ brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
+ }
+
+ // Splash screen fade out progress (possibly delayed)
+ final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
+ getProgress(linearProgress, appRevealDelay,
+ appRevealDuration, animationDuration));
+
+ splashScreenView.setAlpha(1f - splashFadeProgress);
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ Slog.d(TAG, "progress -> animation: " + linearProgress
+ + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
+ + "\t splash alpha: " + splashScreenView.getAlpha()
+ );
+ }
});
if (animatorListener != null) {
animator.addListener(animatorListener);
}
- animator.start();
+ return animator;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686..6ea6516 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -52,8 +52,7 @@
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- final boolean isSolidColorSplashScreen =
- (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
+ final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -67,13 +66,13 @@
+ "processRunning=%b, "
+ "allowTaskSnapshot=%b, "
+ "activityCreated=%b, "
- + "isSolidColorSplashScreen=%b, "
+ + "allowIcon=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
+ "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
+ allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
topIsHome);
if (windowlessSurface) {
@@ -81,7 +80,7 @@
}
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
@@ -95,18 +94,18 @@
}
}
if (!activityDrawn && !topIsHome) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
- private static int getSplashscreenType(boolean solidColorSplashScreen,
- boolean legacySplashScreen) {
- return solidColorSplashScreen
- ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
- : legacySplashScreen
- ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
+ if (allowIcon) {
+ return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
: STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index adae21b..93d7636 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -54,12 +54,13 @@
private static final String TAG = TaskViewTaskController.class.getSimpleName();
private final CloseGuard mGuard = new CloseGuard();
-
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ /** Used to inset the activity content to allow space for a caption bar. */
+ private final Binder mCaptionInsetsOwner = new Binder();
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
private final SyncTransactionQueue mSyncQueue;
private final TaskViewTransitions mTaskViewTransitions;
- private TaskViewBase mTaskViewBase;
private final Context mContext;
/**
@@ -70,21 +71,19 @@
* in this situation to allow us to notify listeners correctly if the task failed to open.
*/
private ActivityManager.RunningTaskInfo mPendingInfo;
- /* Indicates that the task we attempted to launch in the task view failed to launch. */
- private boolean mTaskNotFound;
+ private TaskViewBase mTaskViewBase;
protected ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
private SurfaceControl mTaskLeash;
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ /* Indicates that the task we attempted to launch in the task view failed to launch. */
+ private boolean mTaskNotFound;
private boolean mSurfaceCreated;
private SurfaceControl mSurfaceControl;
private boolean mIsInitialized;
private boolean mNotifiedForInitialized;
+ private boolean mHideTaskWithSurface = true;
private TaskView.Listener mListener;
private Executor mListenerExecutor;
-
- /** Used to inset the activity content to allow space for a caption bar. */
- private final Binder mCaptionInsetsOwner = new Binder();
private Rect mCaptionInsets;
public TaskViewTaskController(Context context, ShellTaskOrganizer organizer,
@@ -102,6 +101,19 @@
mGuard.open("release");
}
+ /**
+ * Specifies if the task should be hidden when the surface is destroyed.
+ * <p>This is {@code true} by default.
+ *
+ * @param hideTaskWithSurface {@code false} if task needs to remain visible even when the
+ * surface is destroyed, {@code true} otherwise.
+ */
+ public void setHideTaskWithSurface(boolean hideTaskWithSurface) {
+ // TODO(b/299535374): Remove mHideTaskWithSurface once the taskviews with launch root tasks
+ // are moved to a window in SystemUI in auto.
+ mHideTaskWithSurface = hideTaskWithSurface;
+ }
+
SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
@@ -257,9 +269,17 @@
mTaskNotFound = false;
}
+ /** This method shouldn't be called when shell transitions are enabled. */
private void updateTaskVisibility() {
+ boolean visible = mSurfaceCreated;
+ if (!visible && !mHideTaskWithSurface) {
+ return;
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+ wct.setHidden(mTaskToken, !visible /* hidden */);
+ if (!visible) {
+ wct.reorder(mTaskToken, false /* onTop */);
+ }
mSyncQueue.queue(wct);
if (mListener == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 00f6a1c..83dc7fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -111,6 +111,14 @@
WindowContainerTransaction mFinishWCT = null;
/**
+ * Whether the transition has request for remote transition while mLeftoversHandler
+ * isn't remote transition handler.
+ * If true and the mLeftoversHandler can handle the transition, need to notify remote
+ * transition handler to consume the transition.
+ */
+ boolean mHasRequestToRemote;
+
+ /**
* Mixed transitions are made up of multiple "parts". This keeps track of how many
* parts are currently animating.
*/
@@ -200,6 +208,10 @@
MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition);
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
+ if (mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+ mixed.mHasRequestToRemote = true;
+ mPlayer.getRemoteTransitionHandler().handleRequest(transition, request);
+ }
return handler.second;
} else if (mSplitHandler.isSplitScreenVisible()
&& isOpeningType(request.getType())
@@ -316,12 +328,22 @@
// the time of handleRequest, but we need more information than is available at that time.
if (KeyguardTransitionHandler.handles(info)) {
if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Converting mixed transition into a keyguard transition");
- onTransitionConsumed(transition, false, null);
+ final MixedTransition keyguardMixed =
+ new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
+ mActiveTransitions.add(keyguardMixed);
+ final boolean hasAnimateKeyguard = animateKeyguard(keyguardMixed, info,
+ startTransaction, finishTransaction, finishCallback);
+ if (hasAnimateKeyguard) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Converting mixed transition into a keyguard transition");
+ // Consume the original mixed transition
+ onTransitionConsumed(transition, false, null);
+ return true;
+ } else {
+ // Keyguard handler cannot handle it, process through original mixed
+ mActiveTransitions.remove(keyguardMixed);
+ }
}
- mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
- mActiveTransitions.add(mixed);
}
if (mixed == null) return false;
@@ -332,8 +354,17 @@
} else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
return false;
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
- return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
- finishTransaction, finishCallback);
+ final boolean handledToPip = animateOpenIntentWithRemoteAndPip(mixed, info,
+ startTransaction, finishTransaction, finishCallback);
+ // Consume the transition on remote handler if the leftover handler already handle this
+ // transition. And if it cannot, the transition will be handled by remote handler, so
+ // don't consume here.
+ // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip
+ if (handledToPip && mixed.mHasRequestToRemote
+ && mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null);
+ }
+ return handledToPip;
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -799,5 +830,8 @@
} else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
}
+ if (mixed.mHasRequestToRemote) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 7df658e..de03f58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -99,7 +99,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
@@ -182,7 +181,7 @@
/* broadcastPermission = */ null,
mMainHandler);
- AttributeCache.init(mContext);
+ TransitionAnimation.initAttributeCache(mContext, mMainHandler);
}
private void updateEnterpriseThumbnailDrawable() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
index 0edcff4..a68b41d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
+
import java.util.Objects;
/**
@@ -37,6 +39,7 @@
public final float leftTaskPercent;
public final float dividerWidthPercent;
public final float dividerHeightPercent;
+ public final @SnapPosition int snapPosition;
/**
* If {@code true}, that means at the time of creation of this object, the
* split-screened apps were vertically stacked. This is useful in scenarios like
@@ -47,12 +50,13 @@
public final int leftTopTaskId;
public final int rightBottomTaskId;
- public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
- int leftTopTaskId, int rightBottomTaskId) {
+ public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+ int rightBottomTaskId, @SnapPosition int snapPosition) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
this.leftTopTaskId = leftTopTaskId;
this.rightBottomTaskId = rightBottomTaskId;
+ this.snapPosition = snapPosition;
if (rightBottomBounds.top > leftTopBounds.top) {
// vertical apps, horizontal divider
@@ -83,8 +87,9 @@
appsStackedVertically = parcel.readBoolean();
leftTopTaskId = parcel.readInt();
rightBottomTaskId = parcel.readInt();
- dividerWidthPercent = parcel.readInt();
- dividerHeightPercent = parcel.readInt();
+ dividerWidthPercent = parcel.readFloat();
+ dividerHeightPercent = parcel.readFloat();
+ snapPosition = parcel.readInt();
}
@Override
@@ -99,6 +104,7 @@
parcel.writeInt(rightBottomTaskId);
parcel.writeFloat(dividerWidthPercent);
parcel.writeFloat(dividerHeightPercent);
+ parcel.writeInt(snapPosition);
}
@Override
@@ -129,7 +135,8 @@
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
+ "Divider: " + visualDividerBounds + "\n"
- + "AppsVertical? " + appsStackedVertically;
+ + "AppsVertical? " + appsStackedVertically + "\n"
+ + "snapPosition: " + snapPosition;
}
public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index c189731..82fc0f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -114,7 +115,7 @@
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -227,7 +228,7 @@
}
@Override
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return R.dimen.freeform_decor_caption_height;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 026d0cd..afa2754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
@@ -68,10 +69,13 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -101,6 +105,7 @@
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final RecentsTransitionHandler mRecentsTransitionHandler;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -120,7 +125,8 @@
private MoveToDesktopAnimator mMoveToDesktopAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
- private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
+ private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
+ new DesktopModeKeyguardChangeListener();
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
public DesktopModeWindowDecorViewModel(
@@ -134,6 +140,7 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
) {
this(
@@ -147,10 +154,10 @@
syncQueue,
transitions,
desktopTasksController,
+ recentsTransitionHandler,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- new DesktopModeKeyguardChangeListener(),
rootTaskDisplayAreaOrganizer);
}
@@ -166,10 +173,10 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mMainHandler = mainHandler;
@@ -181,11 +188,11 @@
mSyncQueue = syncQueue;
mTransitions = transitions;
mDesktopTasksController = desktopTasksController;
+ mRecentsTransitionHandler = recentsTransitionHandler;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
- mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
shellInit.addInitCallback(this::onInit, this);
@@ -193,6 +200,12 @@
private void onInit() {
mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
+ mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onTransitionStarted(IBinder transition) {
+ onRecentsTransitionStarted(transition);
+ }
+ });
}
@Override
@@ -318,6 +331,16 @@
}
}
+ private void onRecentsTransitionStarted(IBinder transition) {
+ // Block relayout on window decorations originating from #onTaskInfoChanges until the
+ // animation completes to avoid interfering with the transition animation.
+ for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
+ final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ decor.incrementRelayoutBlock();
+ decor.addTransitionPausingRelayout(transition);
+ }
+ }
+
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
DragDetector.MotionEventHandler{
@@ -348,13 +371,8 @@
final int id = v.getId();
if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken);
- if (mSplitScreenController != null
- && mSplitScreenController.isSplitScreenVisible()) {
- int remainingTaskPosition = mTaskId == mSplitScreenController
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController
- .getTaskInfo(remainingTaskPosition);
+ if (isTaskInSplitScreen(mTaskId)) {
+ RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
}
decoration.closeMaximizeMenu();
@@ -376,6 +394,7 @@
mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
decoration.incrementRelayoutBlock();
mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
+ closeOtherSplitTask(mTaskId);
}
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
@@ -720,6 +739,7 @@
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
+ closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
relevantDecor.mTaskSurface);
@@ -810,7 +830,8 @@
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
// We can't look at focused task here as only one task will have focus.
- return getSplitScreenDecor(ev);
+ DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev);
+ return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor;
} else {
return getFocusedDecor();
}
@@ -942,6 +963,24 @@
}
}
+ private RunningTaskInfo getOtherSplitTask(int taskId) {
+ @SplitPosition int remainingTaskPosition = mSplitScreenController
+ .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ return mSplitScreenController.getTaskInfo(remainingTaskPosition);
+ }
+
+ private void closeOtherSplitTask(int taskId) {
+ if (isTaskInSplitScreen(taskId)) {
+ mTaskOperations.closeTask(getOtherSplitTask(taskId).token);
+ }
+ }
+
+ private boolean isTaskInSplitScreen(int taskId) {
+ return mSplitScreenController != null
+ && mSplitScreenController.isTaskInSplitScreen(taskId);
+ }
+
private class DragStartListenerImpl
implements DragPositioningCallbackUtility.DragStartListener {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dbff20e..84ec6b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -17,8 +17,10 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -56,6 +58,7 @@
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Supplier;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -109,7 +112,30 @@
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig);
+ this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
+ }
+
+ DesktopModeWindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Configuration windowDecorConfig,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
+ SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ windowContainerTransactionSupplier, surfaceControlViewHostFactory);
mHandler = handler;
mChoreographer = choreographer;
@@ -150,7 +176,7 @@
return;
}
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
// Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
// synced with the buffer transaction (that draws the View). Both will be shown on screen
// at the same, whereas applying them independently causes flickering. See b/270202228.
@@ -180,13 +206,23 @@
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-
- mRelayoutParams.mWindowDecorConfig = DesktopTasksController.isDesktopDensityOverrideSet()
- ? mContext.getResources().getConfiguration() // Use system context
- : mTaskInfo.configuration; // Use task configuration
+ // The configuration used to lay out the window decoration. The system context's config is
+ // used when the task density has been overridden to a custom density so that the resources
+ // and views of the decoration aren't affected and match the rest of the System UI, if not
+ // then just use the task's configuration. A copy is made instead of using the original
+ // reference so that the configuration isn't mutated on config changes and diff checks can
+ // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+ // See b/301119301.
+ // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+ // instead of using a whole Configuration as a parameter.
+ final Configuration windowDecorConfig = new Configuration();
+ windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+ ? mContext.getResources().getConfiguration() // Use system context.
+ : mTaskInfo.configuration); // Use task configuration.
+ mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
mRelayoutParams.mCornerRadius =
(int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
@@ -286,7 +322,7 @@
final int displayWidth = displayLayout.width();
final int displayHeight = displayLayout.height();
- final int captionHeight = getCaptionHeight();
+ final int captionHeight = getCaptionHeight(mTaskInfo.getWindowingMode());
final ImageButton maximizeWindowButton =
mResult.mRootView.findViewById(R.id.maximize_window);
@@ -442,6 +478,7 @@
@Override
void releaseViews() {
closeHandleMenu();
+ closeMaximizeMenu();
super.releaseViews();
}
@@ -545,7 +582,7 @@
super.close();
}
- private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
+ private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
@@ -574,7 +611,7 @@
exclusionRegion = new Region();
}
exclusionRegion.union(new Rect(0, 0, mResult.mWidth,
- getCaptionHeight()));
+ getCaptionHeight(mTaskInfo.getWindowingMode())));
exclusionRegion.translate(mPositionInParent.x, mPositionInParent.y);
return exclusionRegion;
}
@@ -590,12 +627,14 @@
}
@Override
- int getCaptionHeightId() {
- return R.dimen.freeform_decor_caption_height;
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ ? R.dimen.desktop_mode_fullscreen_decor_caption_height
+ : R.dimen.desktop_mode_freeform_decor_caption_height;
}
- private int getCaptionHeight() {
- return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId());
+ private int getCaptionHeight(@WindowingMode int windowingMode) {
+ return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 1ec8a17..f82b212 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -26,7 +26,10 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
@@ -167,10 +170,9 @@
desktopBtn.setOnClickListener(mOnClickListener);
// The button corresponding to the windowing mode that the task is currently in uses a
// different color than the others.
- final ColorStateList activeColorStateList = ColorStateList.valueOf(
- mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
- final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
- mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
+ final int[] iconColors = getWindowingIconColor();
+ final ColorStateList inActiveColorStateList = ColorStateList.valueOf(iconColors[0]);
+ final ColorStateList activeColorStateList = ColorStateList.valueOf(iconColors[1]);
fullscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
? activeColorStateList : inActiveColorStateList);
@@ -197,6 +199,23 @@
}
/**
+ * Returns array of windowing icon color based on current UI theme. First element of the
+ * array is for inactive icons and the second is for active icons.
+ */
+ private int[] getWindowingIconColor() {
+ final int mode = mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
+ final TypedArray typedArray = mContext.obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.materialColorOnSurface,
+ com.android.internal.R.attr.materialColorPrimary});
+ final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK);
+ final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
+ typedArray.recycle();
+ return new int[] {inActiveColor, activeColor};
+ }
+
+ /**
* Updates the handle menu pills' position variables to reflect their next positions
*/
private void updateHandleMenuPillPositions() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 8d76fc6..07fee43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -17,8 +17,10 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -193,12 +195,16 @@
rootView = null; // Clear it just in case we use it accidentally
final int oldDensityDpi = mWindowDecorConfig.densityDpi;
+ final int oldNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
: mTaskInfo.getConfiguration();
- if (oldDensityDpi != mWindowDecorConfig.densityDpi
+ final int newDensityDpi = mWindowDecorConfig.densityDpi;
+ final int newNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (oldDensityDpi != newDensityDpi
|| mDisplay == null
|| mDisplay.getDisplayId() != mTaskInfo.displayId
- || oldLayoutResId != mLayoutResId) {
+ || oldLayoutResId != mLayoutResId
+ || oldNightMode != newNightMode) {
releaseViews();
if (!obtainDisplayOrRegisterListener()) {
@@ -206,6 +212,7 @@
return;
}
mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
+ mDecorWindowContext.setTheme(mContext.getThemeResId());
if (params.mLayoutResId != 0) {
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
.inflate(params.mLayoutResId, null);
@@ -220,6 +227,8 @@
final Resources resources = mDecorWindowContext.getResources();
final Configuration taskConfig = mTaskInfo.getConfiguration();
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
+ final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN;
outResult.mWidth = taskBounds.width();
outResult.mHeight = taskBounds.height();
@@ -279,13 +288,24 @@
mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
final Point taskPosition = mTaskInfo.positionInParent;
- startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
- .setShadowRadius(mTaskSurface, shadowRadius)
+ if (isFullscreen) {
+ // Setting the task crop to the width/height stops input events from being sent to
+ // some regions of the app window. See b/300324920
+ // TODO(b/296921174): investigate whether crop/position needs to be set by window
+ // decorations at all when transition handlers are already taking ownership of the task
+ // surface placement/crop, especially when in fullscreen where tasks cannot be
+ // drag-resized by the window decoration.
+ startT.setWindowCrop(mTaskSurface, null);
+ finishT.setWindowCrop(mTaskSurface, null);
+ } else {
+ startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ }
+ startT.setShadowRadius(mTaskSurface, shadowRadius)
.setColor(mTaskSurface, mTmpColor)
.show(mTaskSurface);
finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
- .setShadowRadius(mTaskSurface, shadowRadius)
- .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ .setShadowRadius(mTaskSurface, shadowRadius);
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
@@ -322,7 +342,7 @@
}
}
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
@@ -444,7 +464,7 @@
* Adds caption inset source to a WCT
*/
public void addCaptionInset(WindowContainerTransaction wct) {
- final int captionHeightId = getCaptionHeightId();
+ final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 6fd3354..0058d11 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -119,6 +119,20 @@
],
}
+java_library {
+ name: "wm-shell-flicker-platinum-tests",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: [
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+ static_libs: [
+ "wm-shell-flicker-utils",
+ ],
+}
+
java_defaults {
name: "WMShellFlickerTestsDefault",
manifest: "manifests/AndroidManifest.xml",
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 77f14f1..adf92d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -50,7 +50,7 @@
}
@Before
- fun before() {
+ fun setUp() {
Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
new file mode 100644
index 0000000..ba2b3e7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.os.Build
+import android.tools.common.datatypes.Rect
+import android.platform.test.annotations.Postsubmit
+import android.system.helpers.CommandsHelper
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test rotating an immersive app in fullscreen.
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:RotateImmersiveAppInFullscreenTest`
+ *
+ * Actions:
+ * ```
+ * Rotate the device by 90 degrees to trigger a rotation through sensors
+ * Verify that the button exists
+ * ```
+ *
+ * Notes:
+ * ```
+ * Some default assertions that are inherited from
+ * the `BaseTest` are ignored due to the nature of the immersive apps.
+ *
+ * This test only works with Cuttlefish devices.
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
+
+ private val immersiveApp = LetterboxAppHelper(instrumentation,
+ launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL,
+ component =
+ ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent())
+
+ private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+ private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) }
+
+ protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+
+ private val isCuttlefishDevice: Boolean = Build.MODEL.contains("Cuttlefish")
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ setStartRotation()
+ immersiveApp.launchViaIntent(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Display not found")
+ }
+ transitions {
+ if (isCuttlefishDevice) {
+ // Simulates a device rotation through sensors because the rotation button
+ // only appears in a rotation event through sensors
+ execAdb("/vendor/bin/cuttlefish_sensor_injection rotate 0")
+ // verify rotation button existence
+ val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion")
+ uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT)
+ uiDevice.findObject(rotationButtonSelector)
+ ?: error("rotation button not found")
+ }
+ }
+ teardown {
+ immersiveApp.exit(wmHelper)
+ }
+ }
+
+ @Before
+ fun setUpForImmersiveAppTests() {
+ Assume.assumeTrue(isCuttlefishDevice)
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun taskBarWindowIsAlwaysVisible() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun navBarWindowIsAlwaysVisible() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarWindowIsAlwaysVisible() {
+ }
+
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ }
+
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ }
+
+ /** Test that app is fullscreen by checking status bar and task bar visibility. */
+ @Postsubmit
+ @Test
+ fun appWindowFullScreen() {
+ flicker.assertWmEnd {
+ this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR)
+ .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
+ .visibleRegion(immersiveApp).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /** Test that app is in the original rotation we have set up. */
+ @Postsubmit
+ @Test
+ fun appInOriginalRotation() {
+ flicker.assertWmEnd {
+ this.hasRotation(Rotation.ROTATION_90)
+ }
+ }
+
+ companion object {
+ private var startDisplayBounds = Rect.EMPTY
+ const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher"
+
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90),
+ // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index c335d3d..213d596 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -23,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.common.flicker.subject.region.RegionTraceSubject
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -80,7 +81,9 @@
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
+ SplitScreenUtils.enterSplit(
+ wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
+ flicker.scenario.startRotation)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -126,9 +129,20 @@
if (tapl.isTablet) {
flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
} else {
- // on phones home does not rotate in landscape, PiP enters back to portrait
- // orientation so use display bounds from that orientation for assertion
- flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) }
+ // on phones home screen does not rotate in landscape, PiP enters back to portrait
+ // orientation - if we go from landscape to portrait it should switch between the bounds
+ // otherwise it should be the same as tablet (i.e. portrait to portrait)
+ if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
+ flicker.assertWmVisibleRegion(pipApp) {
+ // first check against landscape bounds then against portrait bounds
+ coversAtMost(displayBounds).then().coversAtMost(
+ portraitDisplayBounds
+ )
+ }
+ } else {
+ // always check against the display bounds which do not change during transition
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 8cb25fe..69499b9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -31,7 +31,8 @@
class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 2539fd5..bd627f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
- // TODO(b/300260196): Not detecting this scenario right now
+ // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
@ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index aa35237..a8f4d0a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index e195360..cee9bbf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 5f771c7..169b5cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 729a401..412c011 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index e37d806..b444bad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 2a50912..e2ab989 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index d5da1a8..22b8102 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 7fdcb9b..3fb014f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 308e954..ea1f942 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 39e75bd..8f23a79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index e18da17..b0f39e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 00d60e7..6ce8746 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index d7efbc8..9f74edf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 4eece3f..1e4055e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index d96b056..c3b8132 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 809b690..7756d04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index bbdf2d7..c72aa5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 5c29fd8..cc88f27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index a7398eb..87b38b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index eae88ad..ca347ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 7e8ee04..6597819 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 9295c33..6df31fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 4b59e9f..02596c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 5ff36d4..9d579f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index c0cb721..da85342 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 8c14088..1ae2c9e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 7b6614b..b1b56257 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 5df5be9..08c437e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 9d63003..efbf86d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 9fa04b2..f7072fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index 9386aa2..d80d112 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 5ef2167..30ec37a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 9caab9b..1e086d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -18,13 +18,18 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index bf484e5..932f892 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -18,13 +18,18 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index 245184c..80ab24d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -51,7 +51,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
index 1f2f1ec..4c39104 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
index ebbf7c5..f6d1afc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index 71e701c..db5a32a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
index c433b21..d7b306c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -49,11 +50,13 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
+ tapl.goHome()
+
+ primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
-
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 3f087a5..cc982d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -50,14 +51,16 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
// Send a notification
sendNotificationApp.launchViaIntent(wmHelper)
sendNotificationApp.postNotification(wmHelper)
tapl.goHome()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 767e7b5..fa12bb8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -49,12 +50,13 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
tapl.goHome()
SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index f2cbf24..983653b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -46,9 +46,6 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
tapl.goHome()
@@ -57,11 +54,14 @@
.withAppTransitionIdle()
.withHomeActivityVisible()
.waitForAndVerify()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
open fun enterSplitScreenFromOverview() {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 538ed96..068171d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -48,11 +48,12 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
tapl.workspace.switchToOverview().dismissAllTasks()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 0dab5ad..64b75c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -50,7 +50,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index ad3a2d4..1795010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -49,7 +49,7 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index b780a16..7065846 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -46,11 +46,12 @@
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
tapl.workspace.switchToOverview().dismissAllTasks()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 329d61d..251cb50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -51,8 +51,8 @@
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index e59ed64..1387536 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -62,8 +62,10 @@
get() = {
setup {
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
+ flicker.scenario.startRotation)
pipApp.enableAutoEnterForPipActivity()
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 4d90070..3b9e53f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -39,7 +39,8 @@
protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ textEditApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.copyContentInSplit(
instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 8360e94..5fdde3a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -35,7 +35,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
if (tapl.isTablet) {
SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index e745878..b7f6bfe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -35,7 +35,10 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
+ flicker.scenario.startRotation)
+ }
transitions {
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index c3beb36..bb2a7aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -37,7 +37,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index be507d8..5e5e7d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -46,7 +46,7 @@
.waitForAndVerify()
}
transitions {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index ed0debd..46b0bd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -39,7 +39,8 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.doubleTapDividerToSwitch(device)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 9b7939a..baf7693 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -39,7 +39,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index 9326ef3..33b55f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -37,7 +37,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index b928e40..b79dfb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -37,7 +37,8 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index f314995..0204d75 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -39,8 +39,10 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
+ flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 3f8a1ae..6b3cfaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
@@ -41,6 +42,7 @@
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
@@ -101,13 +103,15 @@
tapl: LauncherInstrumentation,
device: UiDevice,
primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ secondaryApp: StandardAppHelper,
+ rotation: Rotation
) {
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- splitFromOverview(tapl, device)
+ splitFromOverview(tapl, device, rotation)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
@@ -121,7 +125,7 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+ fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice, rotation: Rotation) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
// split to left side.
@@ -129,7 +133,9 @@
// TAPL's currentTask on tablet is sometimes not what we expected if the overview
// contains more than 3 task views. We need to use uiautomator directly to find the
// second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
+ val home = tapl.workspace.switchToOverview()
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.overviewActions.clickSplit()
val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
@@ -145,9 +151,13 @@
}
snapshots[0].click()
} else {
- tapl.workspace
+ val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled()
+ tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview
+ val home = tapl.workspace
.switchToOverview()
- .currentTask
+ tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.currentTask
.tapMenu()
.tapSplitMenuItem()
.currentTask
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
new file mode 100644
index 0000000..f583321
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.wm.shell.bubbles
+
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Handler
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.IWindowManager
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.statusbar.IStatusBarService
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.WindowManagerShellWrapper
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView
+import com.android.wm.shell.bubbles.properties.BubbleProperties
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.taskview.TaskViewTransitions
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for loading / inflating views & icons for a bubble.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class BubbleViewInfoTest : ShellTestCase() {
+
+ private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
+ private lateinit var iconFactory: BubbleIconFactory
+ private lateinit var bubble: Bubble
+
+ private lateinit var bubbleController: BubbleController
+ private lateinit var mainExecutor: ShellExecutor
+ private lateinit var bubbleStackView: BubbleStackView
+ private lateinit var bubbleBarLayerView: BubbleBarLayerView
+
+ @Before
+ fun setup() {
+ metadataFlagListener = Bubbles.BubbleMetadataFlagListener {}
+ iconFactory = BubbleIconFactory(context,
+ 60,
+ 30,
+ Color.RED,
+ mContext.resources.getDimensionPixelSize(
+ R.dimen.importance_ring_stroke_width))
+
+ mainExecutor = TestShellExecutor()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ val shellInit = ShellInit(mainExecutor)
+ val shellCommandHandler = ShellCommandHandler()
+ val shellController = ShellController(context, shellInit, shellCommandHandler,
+ mainExecutor)
+ val bubblePositioner = BubblePositioner(context, windowManager)
+ val bubbleData = BubbleData(context, mock<BubbleLogger>(), bubblePositioner,
+ BubbleEducationController(context), mainExecutor)
+ val surfaceSynchronizer = { obj: Runnable -> obj.run() }
+
+ bubbleController = BubbleController(
+ context,
+ shellInit,
+ shellCommandHandler,
+ shellController,
+ bubbleData,
+ surfaceSynchronizer,
+ FloatingContentCoordinator(),
+ mock<BubbleDataRepository>(),
+ mock<IStatusBarService>(),
+ windowManager,
+ WindowManagerShellWrapper(mainExecutor),
+ mock<UserManager>(),
+ mock<LauncherApps>(),
+ mock<BubbleLogger>(),
+ mock<TaskStackListenerImpl>(),
+ ShellTaskOrganizer(mainExecutor),
+ bubblePositioner,
+ mock<DisplayController>(),
+ null,
+ null,
+ mainExecutor,
+ mock<Handler>(),
+ mock<ShellExecutor>(),
+ mock<TaskViewTransitions>(),
+ mock<Transitions>(),
+ mock<SyncTransactionQueue>(),
+ mock<IWindowManager>(),
+ mock<BubbleProperties>())
+
+ bubbleStackView = BubbleStackView(context, bubbleController, bubbleData,
+ surfaceSynchronizer, FloatingContentCoordinator(), mainExecutor)
+ bubbleBarLayerView = BubbleBarLayerView(context, bubbleController)
+ }
+
+ @Test
+ fun testPopulate() {
+ bubble = createBubbleWithShortcut()
+ val info = BubbleViewInfoTask.BubbleViewInfo.populate(context,
+ bubbleController, bubbleStackView, iconFactory, bubble, false /* skipInflation */)
+ assertThat(info!!).isNotNull()
+
+ assertThat(info.imageView).isNotNull()
+ assertThat(info.expandedView).isNotNull()
+ assertThat(info.bubbleBarExpandedView).isNull()
+
+ assertThat(info.shortcutInfo).isNotNull()
+ assertThat(info.appName).isNotEmpty()
+ assertThat(info.rawBadgeBitmap).isNotNull()
+ assertThat(info.dotPath).isNotNull()
+ assertThat(info.bubbleBitmap).isNotNull()
+ assertThat(info.badgeBitmap).isNotNull()
+ }
+
+ @Test
+ fun testPopulateForBubbleBar() {
+ bubble = createBubbleWithShortcut()
+ val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
+ bubbleController, bubbleBarLayerView, iconFactory, bubble,
+ false /* skipInflation */)
+ assertThat(info!!).isNotNull()
+
+ assertThat(info.imageView).isNull()
+ assertThat(info.expandedView).isNull()
+ assertThat(info.bubbleBarExpandedView).isNotNull()
+
+ assertThat(info.shortcutInfo).isNotNull()
+ assertThat(info.appName).isNotEmpty()
+ assertThat(info.rawBadgeBitmap).isNotNull()
+ assertThat(info.dotPath).isNotNull()
+ assertThat(info.bubbleBitmap).isNotNull()
+ assertThat(info.badgeBitmap).isNotNull()
+ }
+
+ @Test
+ fun testPopulate_invalidShortcutIcon() {
+ bubble = createBubbleWithShortcut()
+
+ // This eventually calls down to load the shortcut icon from the app, simulate an
+ // exception here if the app has an issue loading the shortcut icon; we default to
+ // the app icon in that case / none of the icons will be null.
+ val mockIconFactory = mock<BubbleIconFactory>()
+ whenever(mockIconFactory.getBubbleDrawable(eq(context), eq(bubble.shortcutInfo),
+ any())).doThrow(RuntimeException())
+
+ val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
+ bubbleController, bubbleBarLayerView, iconFactory, bubble,
+ true /* skipInflation */)
+ assertThat(info).isNotNull()
+
+ assertThat(info?.shortcutInfo).isNotNull()
+ assertThat(info?.appName).isNotEmpty()
+ assertThat(info?.rawBadgeBitmap).isNotNull()
+ assertThat(info?.dotPath).isNotNull()
+ assertThat(info?.bubbleBitmap).isNotNull()
+ assertThat(info?.badgeBitmap).isNotNull()
+ }
+
+ private fun createBubbleWithShortcut(): Bubble {
+ val shortcutInfo = ShortcutInfo.Builder(mContext, "mockShortcutId").build()
+ return Bubble("mockKey", shortcutInfo, 1000, Resources.ID_NULL,
+ "mockTitle", 0 /* taskId */, "mockLocus", true /* isDismissible */,
+ mainExecutor, metadataFlagListener)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 9f0d89b..5237585 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -27,15 +27,13 @@
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -66,7 +64,7 @@
@Before
fun setup() {
- launcherApps = mock(LauncherApps::class.java)
+ launcherApps = mock<LauncherApps>()
repository = BubbleVolatileRepository(launcherApps)
}
@@ -98,7 +96,7 @@
repository.addBubbles(user11.identifier, listOf(bubble12))
assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier))
- Mockito.verifyNoMoreInteractions(launcherApps)
+ verifyNoMoreInteractions(launcherApps)
}
@Test
@@ -167,9 +165,8 @@
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -184,9 +181,8 @@
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -200,9 +196,8 @@
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble2, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -219,9 +214,8 @@
user11.identifier))
assertThat(ret).isFalse() // bubbles were NOT removed
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -237,9 +231,8 @@
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
@@ -261,9 +254,8 @@
// bubble2 is the work profile bubble and should be removed
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index ad84c7f..56d0f8e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -18,9 +18,13 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -130,7 +134,7 @@
@Test
public void testSetDivideRatio() {
mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
- mSplitLayout.setDivideRatio(0.5f);
+ mSplitLayout.setDivideRatio(SNAP_TO_50_50);
assertThat(mSplitLayout.getDividePosition()).isEqualTo(
mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 9b9600e..f85d707 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -61,6 +61,7 @@
import dagger.Lazy;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -523,13 +524,175 @@
.createLayout(anyBoolean());
}
+ @Test
+ public void testUpdateActiveTaskInfo_newTask_visibleAndFocused_updated() {
+ // Simulate user aspect ratio button being shown for previous task
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_newTask_notVisibleOrFocused_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ final int newTaskId = TASK_ID + 1;
+
+ // Create visible but NOT focused task
+ final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ false);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo1);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create focused but NOT visible task
+ final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo2);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create NOT focused but NOT visible task
+ final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
+ /* isFocused */ false);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo3);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_sameTask_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate same task being re-shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_transparentTask_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ final int newTaskId = TASK_ID + 1;
+
+ // Create transparent task
+ final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true, /* isTopActivityTransparent */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo1);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
@CameraCompatControlState int cameraCompatControlState) {
+ return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
+ /* isVisible */ false, /* isFocused */ false,
+ /* isTopActivityTransparent */ false);
+ }
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
+ boolean isFocused) {
+ return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
+ isVisible, isFocused, /* isTopActivityTransparent */ false);
+ }
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
+ boolean isFocused, boolean isTopActivityTransparent) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.displayId = displayId;
taskInfo.topActivityInSizeCompat = hasSizeCompat;
taskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.isVisible = isVisible;
+ taskInfo.isFocused = isFocused;
+ taskInfo.isTopActivityTransparent = isTopActivityTransparent;
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index ce1290b..f460d1b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -93,7 +93,8 @@
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
new CompatUIController.CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0);
+ mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0,
+ () -> false, s -> {});
mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
R.layout.user_aspect_ratio_settings_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 08cc2f7..5a4d6c8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -36,6 +36,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.util.Pair;
import android.view.DisplayInfo;
import android.view.InsetsSource;
@@ -66,6 +67,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Supplier;
/**
* Tests for {@link UserAspectRatioSettingsWindowManager}.
@@ -74,6 +76,7 @@
* atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest
*/
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
@@ -81,6 +84,8 @@
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock
+ private Supplier<Boolean> mUserAspectRatioButtonShownChecker;
+ @Mock
private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener>
mOnUserAspectRatioSettingsButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@@ -106,10 +111,12 @@
false, /* topActivityBoundsLetterboxed */ true);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0);
+ mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
+ mUserAspectRatioButtonShownChecker, s -> {});
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
}
@Test
@@ -293,6 +300,39 @@
}
@Test
+ public void testLayoutHasUserAspectRatioSettingsButton() {
+ clearInvocations(mWindowManager);
+ spyOn(mWindowManager);
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true);
+
+ // User aspect ratio settings button has not yet been shown.
+ doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout has the user aspect ratio settings button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton);
+
+ // User aspect ratio settings button has been shown and is still visible.
+ spyOn(mWindowManager);
+ doReturn(true).when(mWindowManager).isShowingButton();
+ doReturn(true).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout still has the user aspect ratio settings button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton);
+
+ // User aspect ratio settings button has been shown and has timed out so is no longer
+ // visible.
+ doReturn(false).when(mWindowManager).isShowingButton();
+ doReturn(true).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout no longer has the user aspect ratio button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertFalse(mWindowManager.mHasUserAspectRatioSettingsButton);
+ }
+
+ @Test
public void testAttachToParentSurface() {
final SurfaceControl.Builder b = new SurfaceControl.Builder();
mWindowManager.attachToParentSurface(b);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index be4a287..c6cccc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -52,6 +52,8 @@
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -94,6 +96,7 @@
ToggleResizeDesktopTaskTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
+ @Mock lateinit var splitScreenController: SplitScreenController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -116,6 +119,7 @@
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
controller = createController()
+ controller.splitScreenController = splitScreenController
shellInit.init()
}
@@ -341,6 +345,30 @@
}
@Test
+ fun moveToDesktop_splitTaskExitsSplit() {
+ var task = setUpSplitScreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
+ fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
+ var task = setUpFullscreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
@@ -695,6 +723,13 @@
return task
}
+ private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createSplitScreenTask(displayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
private fun markTaskVisible(task: RunningTaskInfo) {
desktopModeTaskRepository.updateVisibleFreeformTasks(
task.displayId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 29a757c..2f6f320 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,6 +21,7 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.MockToken
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -45,12 +46,25 @@
@JvmOverloads
fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setToken(MockToken().token())
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .setLastActiveTime(100)
- .build()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build()
+ }
+
+ /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */
+ @JvmStatic
+ @JvmOverloads
+ fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setLastActiveTime(100)
+ .build()
}
/** Create a new home task */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
index baa06f2..bbd65be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -24,6 +24,7 @@
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
import com.android.wm.shell.util.GroupedRecentTaskInfo
import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
@@ -123,6 +124,7 @@
assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+ assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_50_50)
}
@Test
@@ -156,7 +158,7 @@
private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
val task1 = createTaskInfo(id = 1)
val task2 = createTaskInfo(id = 2)
- val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_50_50)
return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 40ce785..10e9e11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -173,10 +174,10 @@
// Verify only one update if the split info is the same
SplitBounds bounds1 = new SplitBounds(new Rect(0, 0, 50, 50),
- new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
SplitBounds bounds2 = new SplitBounds(new Rect(0, 0, 50, 50),
- new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
}
@@ -207,8 +208,10 @@
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
- SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+ SplitBounds pair2Bounds =
+ new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -236,8 +239,10 @@
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
- SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+ SplitBounds pair2Bounds =
+ new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -334,7 +339,8 @@
setRawList(t1, t2, t3);
// Add a pair
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 3);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 3, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
reset(mRecentTasksController);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
index 50d02ae..b790aee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
@@ -1,5 +1,7 @@
package com.android.wm.shell.recents;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,21 +46,21 @@
@Test
public void testVerticalStacked() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
assertTrue(ssb.appsStackedVertically);
}
@Test
public void testHorizontalStacked() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
assertFalse(ssb.appsStackedVertically);
}
@Test
public void testHorizontalDividerBounds() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(0, dividerBounds.left);
assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top);
@@ -69,7 +71,7 @@
@Test
public void testVerticalDividerBounds() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
assertEquals(0, dividerBounds.top);
@@ -80,7 +82,7 @@
@Test
public void testEqualVerticalTaskPercent() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
}
@@ -88,7 +90,7 @@
@Test
public void testEqualHorizontalTaskPercent() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index d098d33..0088051 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -221,6 +221,20 @@
}
@Test
+ public void testSurfaceDestroyed_withTask_shouldNotHideTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
+ mTaskViewTaskController.setHideTaskWithSurface(false);
+
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.surfaceCreated(sh);
+ reset(mViewListener);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
public void testSurfaceDestroyed_withTask_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
deleted file mode 100644
index d8afe68..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2022 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.wm.shell.windowdecor;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.InputMonitor;
-import android.view.SurfaceControl;
-import android.view.SurfaceView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopTasksController;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/** Tests of {@link DesktopModeWindowDecorViewModel} */
-@SmallTest
-public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
-
- private static final String TAG = "DesktopModeWindowDecorViewModelTests";
- private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0);
-
- @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
- @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
-
- @Mock private Handler mMainHandler;
- @Mock private Choreographer mMainChoreographer;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
- @Mock private DisplayLayout mDisplayLayout;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private DesktopTasksController mDesktopTasksController;
- @Mock private InputMonitor mInputMonitor;
- @Mock private InputManager mInputManager;
- @Mock private Transitions mTransitions;
- @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
- @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
- @Mock private SurfaceControl.Transaction mTransaction;
- @Mock private Display mDisplay;
- @Mock private ShellController mShellController;
- @Mock private ShellInit mShellInit;
- @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
- mDesktopModeKeyguardChangeListener;
- @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- private final List<InputManager> mMockInputManagers = new ArrayList<>();
-
- private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
-
- @Before
- public void setUp() {
- mMockInputManagers.add(mInputManager);
-
- mDesktopModeWindowDecorViewModel =
- new DesktopModeWindowDecorViewModel(
- mContext,
- mMainHandler,
- mMainChoreographer,
- mShellInit,
- mTaskOrganizer,
- mDisplayController,
- mShellController,
- mSyncQueue,
- mTransitions,
- Optional.of(mDesktopTasksController),
- mDesktopModeWindowDecorFactory,
- mMockInputMonitorFactory,
- mTransactionFactory,
- mDesktopModeKeyguardChangeListener,
- mRootTaskDisplayAreaOrganizer
- );
-
- doReturn(mDesktopModeWindowDecoration)
- .when(mDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
- doReturn(mTransaction).when(mTransactionFactory).get();
- doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
- doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
- doNothing().when(mShellController).addKeyguardChangeListener(any());
-
- when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
- // InputChannel cannot be mocked because it passes to InputEventReceiver.
- final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
- inputChannels[0].dispose();
- when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
-
- mDesktopModeWindowDecoration.mDisplay = mDisplay;
- doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
- }
-
- @Test
- public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory)
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue,
- mRootTaskDisplayAreaOrganizer);
- verify(mDesktopModeWindowDecoration).close();
- }
-
- @Test
- public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, times(1))
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue,
- mRootTaskDisplayAreaOrganizer);
- }
-
- @Test
- public void testCreateAndDisposeEventReceiver() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- runOnMainThread(() -> {
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- });
- verify(mMockInputMonitorFactory).create(any(), any());
- verify(mInputMonitor).dispose();
- }
-
- @Test
- public void testEventReceiversOnMultipleDisplays() throws Exception {
- runOnMainThread(() -> {
- SurfaceView surfaceView = new SurfaceView(mContext);
- final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
- final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay(
- "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400,
- /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- try {
- int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo secondTaskInfo =
- createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo thirdTaskInfo =
- createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
-
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT,
- finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- } finally {
- secondaryDisplay.release();
- }
- });
- verify(mMockInputMonitorFactory, times(2)).create(any(), any());
- verify(mInputMonitor, times(1)).dispose();
- }
-
- @Test
- public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
- doReturn(true).when(
- mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
- taskInfo.isFocused = true;
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
- }
-
- private void runOnMainThread(Runnable r) throws Exception {
- final Handler mainHandler = new Handler(Looper.getMainLooper());
- final CountDownLatch latch = new CountDownLatch(1);
- mainHandler.post(() -> {
- r.run();
- latch.countDown();
- });
- latch.await(1, TimeUnit.SECONDS);
- }
-
- private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
- int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
- ActivityManager.RunningTaskInfo taskInfo =
- new TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setVisible(true)
- .build();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
- return taskInfo;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
new file mode 100644
index 0000000..00d70a7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.os.Handler
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.InputChannel
+import android.view.InputMonitor
+import android.view.SurfaceControl
+import android.view.SurfaceView
+import androidx.core.content.getSystemService
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.sysui.KeyguardChangeListener
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import java.util.Optional
+import java.util.function.Supplier
+
+/** Tests of [DesktopModeWindowDecorViewModel] */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+ @Mock private lateinit var mockDesktopModeWindowDecorFactory:
+ DesktopModeWindowDecoration.Factory
+ @Mock private lateinit var mockMainHandler: Handler
+ @Mock private lateinit var mockMainChoreographer: Choreographer
+ @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var mockDisplayController: DisplayController
+ @Mock private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock private lateinit var mockSyncQueue: SyncTransactionQueue
+ @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+ @Mock private lateinit var mockInputMonitor: InputMonitor
+ @Mock private lateinit var mockTransitions: Transitions
+ @Mock private lateinit var mockInputMonitorFactory:
+ DesktopModeWindowDecorViewModel.InputMonitorFactory
+ @Mock private lateinit var mockShellController: ShellController
+ @Mock private lateinit var mockShellExecutor: ShellExecutor
+ @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler
+
+ private val transactionFactory = Supplier<SurfaceControl.Transaction> {
+ SurfaceControl.Transaction()
+ }
+
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
+
+ @Before
+ fun setUp() {
+ shellInit = ShellInit(mockShellExecutor)
+ desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
+ mContext,
+ mockMainHandler,
+ mockMainChoreographer,
+ shellInit,
+ mockTaskOrganizer,
+ mockDisplayController,
+ mockShellController,
+ mockSyncQueue,
+ mockTransitions,
+ Optional.of(mockDesktopTasksController),
+ mockRecentsTransitionHandler,
+ mockDesktopModeWindowDecorFactory,
+ mockInputMonitorFactory,
+ transactionFactory,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
+ whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
+
+ // InputChannel cannot be mocked because it passes to InputEventReceiver.
+ val inputChannels = InputChannel.openInputChannelPair(TAG)
+ inputChannels.first().dispose()
+ whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1])
+
+ shellInit.init()
+ }
+
+ @Test
+ fun testDeleteCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task, taskSurface)
+
+ verify(mockDesktopModeWindowDecorFactory).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ verify(decoration).close()
+ }
+
+ @Test
+ fun testCreateCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_UNDEFINED,
+ activityType = ACTIVITY_TYPE_UNDEFINED
+ )
+ val taskSurface = SurfaceControl()
+ setUpMockDecorationForTask(task)
+
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, never()).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ task.setWindowingMode(WINDOWING_MODE_FREEFORM)
+ task.setActivityType(ACTIVITY_TYPE_STANDARD)
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, times(1)).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ }
+
+ @Test
+ fun testCreateAndDisposeEventReceiver() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+ verify(mockInputMonitorFactory).create(any(), any())
+ verify(mockInputMonitor).dispose()
+ }
+
+ @Test
+ fun testEventReceiversOnMultipleDisplays() {
+ val secondaryDisplay = createVirtualDisplay() ?: return
+ val secondaryDisplayId = secondaryDisplay.display.displayId
+ val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM)
+ val secondTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val thirdTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+ onTaskOpening(task)
+ onTaskOpening(secondTask)
+ onTaskOpening(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+ secondaryDisplay.release()
+
+ verify(mockInputMonitorFactory, times(2)).create(any(), any())
+ verify(mockInputMonitor, times(1)).dispose()
+ }
+
+ @Test
+ fun testCaptionIsNotCreatedWhenKeyguardIsVisible() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>()
+ verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture())
+
+ keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged(
+ true /* visible */,
+ true /* occluded */,
+ false /* animatingDismiss */
+ )
+ onTaskOpening(task)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task)
+
+ verify(mockDesktopModeWindowDecorFactory, never())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun testRelayoutBlockedDuringRecentsTransition() {
+ val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>()
+ verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture())
+
+ val transition = mock(IBinder::class.java)
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration = setUpMockDecorationForTask(task)
+
+ // Make sure a window decorations exists first by launching a freeform task.
+ onTaskOpening(task)
+ // Now call back when a Recents transition starts.
+ recentsCaptor.firstValue.onTransitionStarted(transition)
+
+ verify(decoration).incrementRelayoutBlock()
+ verify(decoration).addTransitionPausingRelayout(transition)
+ }
+
+ private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskOpening(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskChanging(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun createTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ @WindowConfiguration.WindowingMode windowingMode: Int,
+ activityType: Int = ACTIVITY_TYPE_STANDARD,
+ focused: Boolean = true
+ ): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setWindowingMode(windowingMode)
+ .setVisible(true)
+ .setActivityType(activityType)
+ .build().apply {
+ isFocused = focused
+ }
+ }
+
+ private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration {
+ val decoration = mock(DesktopModeWindowDecoration::class.java)
+ whenever(mockDesktopModeWindowDecorFactory.create(
+ any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ ).thenReturn(decoration)
+ return decoration
+ }
+
+ private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) {
+ tasks.forEach { setUpMockDecorationForTask(it) }
+ }
+
+ private fun createVirtualDisplay(): VirtualDisplay? {
+ val surfaceView = SurfaceView(mContext)
+ return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay(
+ "testEventReceiversOnMultipleDisplays",
+ /*width=*/ 400,
+ /*height=*/ 400,
+ /*densityDpi=*/320,
+ surfaceView.holder.surface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ )
+ }
+
+ private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) {
+ configuration.windowConfiguration.windowingMode = mode
+ }
+
+ private fun RunningTaskInfo.setActivityType(type: Int) {
+ configuration.windowConfiguration.activityType = type
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeWindowDecorViewModelTests"
+ private val STABLE_INSETS = Rect(0, 100, 0, 0)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
new file mode 100644
index 0000000..a2dbab1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -0,0 +1,132 @@
+/*
+ * 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.wm.shell.windowdecor;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.function.Supplier;
+
+/**
+ * Tests for {@link DesktopModeWindowDecoration}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DesktopModeWindowDecorationTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DesktopModeWindowDecorationTests extends ShellTestCase {
+ @Mock
+ private DisplayController mMockDisplayController;
+ @Mock
+ private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock
+ private Handler mMockHandler;
+ @Mock
+ private Choreographer mMockChoreographer;
+ @Mock
+ private SyncTransactionQueue mMockSyncQueue;
+ @Mock
+ private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
+ @Mock
+ private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
+ @Mock
+ private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private SurfaceControl mMockSurfaceControl;
+ @Mock
+ private SurfaceControlViewHost mMockSurfaceControlViewHost;
+ @Mock
+ private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setUp() {
+ doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
+ any(), any(), any());
+ doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+ }
+
+ @Test
+ public void testMenusClosedWhenTaskIsInvisible() {
+ doReturn(mMockTransaction).when(mMockTransaction).hide(any());
+
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(false /* visible */);
+ final DesktopModeWindowDecoration spyWindowDecor =
+ spy(createWindowDecoration(taskInfo));
+
+ spyWindowDecor.relayout(taskInfo);
+
+ // Menus should close if open before the task being invisible causes relayout to return.
+ verify(spyWindowDecor).closeHandleMenu();
+ verify(spyWindowDecor).closeMaximizeMenu();
+
+ }
+
+ private DesktopModeWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo) {
+ return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
+ mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration,
+ mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, mMockTransactionSupplier,
+ WindowContainerTransaction::new, mMockSurfaceControlViewHostFactory);
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(visible)
+ .build();
+ taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ return taskInfo;
+
+ }
+}
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 436f107..ef4cc46 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,5 +1,4 @@
set noparent
-toddke@google.com
zyy@google.com
patb@google.com
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 502ff87..e1dd145 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -36,11 +36,6 @@
],
}
-java_aconfig_library {
- name: "hwui_flags_java_lib",
- aconfig_declarations: "hwui_flags",
-}
-
cc_aconfig_library {
name: "hwui_flags_cc_lib",
aconfig_declarations: "hwui_flags",
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index d074a90..d0d3c5e 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -5,4 +5,18 @@
namespace: "core_graphics"
description: "API to enable apps to restrict the amount of HDR headroom that is used"
bug: "234181960"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "hdr_10bit_plus"
+ namespace: "core_graphics"
+ description: "Use 10101010 and FP16 formats for HDR-UI when available"
+ bug: "284159488"
+}
+
+flag {
+ name: "gainmap_animations"
+ namespace: "core_graphics"
+ description: "APIs to help enable animations involving gainmaps"
+ bug: "296482289"
+}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 735fc07..30d4612 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -169,7 +169,8 @@
return;
}
mGrContext->flushAndSubmit();
- mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+ mGrContext->performDeferredCleanup(std::chrono::seconds(30),
+ GrPurgeResourceOptions::kAllResources);
}
void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d54c5f5..b00fc69 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -123,7 +123,7 @@
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline))
- , mHintSessionWrapper(uiThreadId, renderThreadId) {
+ , mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) {
mRenderThread.cacheManager().registerCanvasContext(this);
mRenderThread.renderState().registerContextCallback(this);
rootRenderNode->makeRoot();
@@ -162,6 +162,7 @@
destroyHardwareResources();
mAnimationContext->destroy();
mRenderThread.cacheManager().onContextStopped(this);
+ mHintSessionWrapper->delayedDestroy(mRenderThread, 2_s, mHintSessionWrapper);
}
static void setBufferCount(ANativeWindow* window) {
@@ -766,7 +767,7 @@
int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
- mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+ mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
if (didDraw) {
int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
@@ -774,7 +775,7 @@
int64_t actualDuration = frameDuration -
(std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
dequeueBufferDuration - idleDuration;
- mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ mHintSessionWrapper->reportActualWorkDuration(actualDuration);
}
mLastDequeueBufferDuration = dequeueBufferDuration;
@@ -917,10 +918,10 @@
}
void CanvasContext::prepareAndDraw(RenderNode* node) {
- ATRACE_CALL();
+ int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
+ ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
- int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline();
int64_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
@@ -1112,11 +1113,11 @@
}
void CanvasContext::sendLoadResetHint() {
- mHintSessionWrapper.sendLoadResetHint();
+ mHintSessionWrapper->sendLoadResetHint();
}
void CanvasContext::sendLoadIncreaseHint() {
- mHintSessionWrapper.sendLoadIncreaseHint();
+ mHintSessionWrapper->sendLoadIncreaseHint();
}
void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
@@ -1124,7 +1125,7 @@
}
void CanvasContext::startHintSession() {
- mHintSessionWrapper.init();
+ mHintSessionWrapper->init();
}
bool CanvasContext::shouldDither() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 241f8dd..37e4f7ec 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -363,7 +363,7 @@
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
- HintSessionWrapper mHintSessionWrapper;
+ std::shared_ptr<HintSessionWrapper> mHintSessionWrapper;
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mSyncDelayDuration = 0;
nsecs_t mIdleDuration = 0;
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index b34da51..d1ebe6d 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -24,6 +24,7 @@
#include <vector>
#include "../Properties.h"
+#include "RenderThread.h"
#include "thread/CommonPool.h"
using namespace std::chrono_literals;
@@ -62,8 +63,9 @@
}
void HintSessionWrapper::destroy() {
- if (mHintSessionFuture.valid()) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture.has_value()) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
}
if (mHintSession) {
mBinding->closeSession(mHintSession);
@@ -74,12 +76,12 @@
bool HintSessionWrapper::init() {
if (mHintSession != nullptr) return true;
-
// If we're waiting for the session
- if (mHintSessionFuture.valid()) {
+ if (mHintSessionFuture.has_value()) {
// If the session is here
- if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
if (mHintSession != nullptr) {
mSessionValid = true;
return true;
@@ -136,6 +138,7 @@
actualDurationNanos < kSanityCheckUpperBound) {
mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
}
+ mLastFrameNotification = systemTime();
}
void HintSessionWrapper::sendLoadResetHint() {
@@ -153,6 +156,28 @@
void HintSessionWrapper::sendLoadIncreaseHint() {
if (!init()) return;
mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
+ mLastFrameNotification = systemTime();
+}
+
+bool HintSessionWrapper::alive() {
+ return mHintSession != nullptr;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+ return mLastFrameNotification;
+}
+
+// Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr) {
+ nsecs_t lastUpdate = wrapperPtr->getLastUpdate();
+ rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable {
+ if (wrapper->getLastUpdate() == lastUpdate) {
+ wrapper->destroy();
+ }
+ // Ensure the shared_ptr is killed at the end of the method
+ wrapper = nullptr;
+ });
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index f8b876e..36e91ea 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -19,6 +19,7 @@
#include <android/performance_hint.h>
#include <future>
+#include <optional>
#include "utils/TimeUtils.h"
@@ -27,6 +28,8 @@
namespace renderthread {
+class RenderThread;
+
class HintSessionWrapper {
public:
friend class HintSessionWrapperTests;
@@ -40,10 +43,15 @@
void sendLoadIncreaseHint();
bool init();
void destroy();
+ bool alive();
+ nsecs_t getLastUpdate();
+ void delayedDestroy(renderthread::RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr);
private:
APerformanceHintSession* mHintSession = nullptr;
- std::future<APerformanceHintSession*> mHintSessionFuture;
+ // This needs to work concurrently for testing
+ std::optional<std::shared_future<APerformanceHintSession*>> mHintSessionFuture;
int mResetsSinceLastReport = 0;
nsecs_t mLastFrameNotification = 0;
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
index 623be1e..a14ae1c 100644
--- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -23,9 +23,11 @@
#include <chrono>
#include "Properties.h"
+#include "tests/common/TestUtils.h"
using namespace testing;
using namespace std::chrono_literals;
+using namespace android::uirenderer::renderthread;
APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
@@ -42,6 +44,9 @@
protected:
std::shared_ptr<HintSessionWrapper> mWrapper;
+ std::promise<int> blockDestroyCallUntil;
+ std::promise<int> waitForDestroyFinished;
+
class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
public:
void init() override;
@@ -53,11 +58,17 @@
MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t));
+ // Needs to be on the binding so it can be accessed from static methods
+ std::promise<int> allowCreationToFinish;
};
// Must be static so it can have function pointers we can point to with static methods
static std::shared_ptr<MockHintSessionBinding> sMockBinding;
+ static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
+ void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
+ void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
+
// Must be static so we can point to them as normal fn pointers with HintSessionBinding
static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
@@ -65,6 +76,12 @@
int64_t initialTarget) {
return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
}
+ static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ sMockBinding->allowCreationToFinish.get_future().wait();
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
const int32_t* ids, size_t idsSize,
int64_t initialTarget) {
@@ -85,7 +102,21 @@
static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
sMockBinding->fakeSendHint(session, hintId);
};
- void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); }
+ void waitForWrapperReady() {
+ if (mWrapper->mHintSessionFuture.has_value()) {
+ mWrapper->mHintSessionFuture->wait();
+ }
+ }
+ void scheduleDelayedDestroyManaged() {
+ TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
+ // Guaranteed to be scheduled first, allows destruction to start
+ rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
+ // Guaranteed to be scheduled second, destroys the session
+ mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
+ // This is guaranteed to be queued after the destroy, signals that destruction is done
+ rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
+ });
+ }
};
std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
@@ -113,6 +144,7 @@
}
void HintSessionWrapperTests::TearDown() {
+ // Ensure that anything running on RT is completely finished
mWrapper = nullptr;
sMockBinding = nullptr;
}
@@ -122,6 +154,7 @@
sMockBinding->createSession = stubSlowCreateSession;
mWrapper->init();
mWrapper = nullptr;
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
}
TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
@@ -148,4 +181,107 @@
mWrapper->sendLoadResetHint();
}
+TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to ensure the wrapper grabs the promise value
+ mWrapper->init();
+
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // Schedule delayed destruction, allow it to run, and check when it's done
+ scheduleDelayedDestroyManaged();
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+ // If we then delete the wrapper, it shouldn't close the session again
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
+ mWrapper = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens after
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow destruction to happen first
+ allowDelayedDestructionToStart();
+
+ // Make sure destruction has had time to happen
+ std::this_thread::sleep_for(50ms);
+
+ // Then, allow creation to finish after delayed destroy runs
+ allowCreationToFinish();
+
+ // Wait for destruction to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens before
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow creation to happen first
+ allowCreationToFinish();
+
+ // Make sure creation has had time to happen
+ waitForWrapperReady();
+
+ // Then allow destruction to happen after creation is done
+ allowDelayedDestructionToStart();
+
+ // Wait for it to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+ .Times(1);
+
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to grab the wrapper from the promise
+ mWrapper->init();
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // First schedule the deletion
+ scheduleDelayedDestroyManaged();
+
+ // Then, send a hint to update the timestamp
+ mWrapper->sendLoadIncreaseHint();
+
+ // Then, run the delayed deletion after sending the update
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it didn't close within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), true);
+}
+
} // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 842542f..adc0e16 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -941,27 +941,15 @@
}
/**
- * Sends a simulated key event for a media button.
- * To simulate a key press, you must first send a KeyEvent built with a
- * {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
- * action.
+ * Sends a simulated key event for a media button. To simulate a key press, you must first send
+ * a KeyEvent built with a {@link KeyEvent#ACTION_DOWN} action, then another event with the
+ * {@link KeyEvent#ACTION_UP} action.
+ *
* <p>The key event will be sent to the current media key event consumer which registered with
* {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}.
- * @param keyEvent a {@link KeyEvent} instance whose key code is one of
- * {@link KeyEvent#KEYCODE_MUTE},
- * {@link KeyEvent#KEYCODE_HEADSETHOOK},
- * {@link KeyEvent#KEYCODE_MEDIA_PLAY},
- * {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
- * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
- * {@link KeyEvent#KEYCODE_MEDIA_STOP},
- * {@link KeyEvent#KEYCODE_MEDIA_NEXT},
- * {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
- * {@link KeyEvent#KEYCODE_MEDIA_REWIND},
- * {@link KeyEvent#KEYCODE_MEDIA_RECORD},
- * {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
- * {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
- * {@link KeyEvent#KEYCODE_MEDIA_EJECT},
- * or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
+ *
+ * @param keyEvent a media session {@link KeyEvent}, as defined by {@link
+ * KeyEvent#isMediaSessionKey}.
*/
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index a6e8fa0..aaaf25f 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -220,33 +220,21 @@
return -1;
}
-
/**
- * Send a simulated key event for a media button to be received by the current client.
- * To simulate a key press, you must first send a KeyEvent built with
- * a {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
- * action.
- * <p>The key event will be sent to the registered receiver
- * (see {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}) whose associated
- * {@link RemoteControlClient}'s metadata and playback state is published (there may be
- * none under some circumstances).
- * @param keyEvent a {@link KeyEvent} instance whose key code is one of
- * {@link KeyEvent#KEYCODE_MUTE},
- * {@link KeyEvent#KEYCODE_HEADSETHOOK},
- * {@link KeyEvent#KEYCODE_MEDIA_PLAY},
- * {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
- * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
- * {@link KeyEvent#KEYCODE_MEDIA_STOP},
- * {@link KeyEvent#KEYCODE_MEDIA_NEXT},
- * {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
- * {@link KeyEvent#KEYCODE_MEDIA_REWIND},
- * {@link KeyEvent#KEYCODE_MEDIA_RECORD},
- * {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
- * {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
- * {@link KeyEvent#KEYCODE_MEDIA_EJECT},
- * or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
+ * Send a simulated key event for a media button to be received by the current client. To
+ * simulate a key press, you must first send a KeyEvent built with a {@link
+ * KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP} action.
+ *
+ * <p>The key event will be sent to the registered receiver (see {@link
+ * AudioManager#registerMediaButtonEventReceiver(PendingIntent)}) whose associated {@link
+ * RemoteControlClient}'s metadata and playback state is published (there may be none under some
+ * circumstances).
+ *
+ * @param keyEvent a media session {@link KeyEvent}, as defined by {@link
+ * KeyEvent#isMediaSessionKey}.
* @return true if the event was successfully sent, false otherwise.
- * @throws IllegalArgumentException
+ * @throws IllegalArgumentException If the provided {@link KeyEvent} is not a media session key,
+ * as defined by {@link KeyEvent#isMediaSessionKey}.
*/
public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {
if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 71d8261..161fd2f 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -194,6 +194,7 @@
String indent = prefix + " ";
+ pw.println(indent + "mShouldPerformActiveScan=" + mShouldPerformActiveScan);
pw.println(indent + "mPreferredFeatures=" + mPreferredFeatures);
pw.println(indent + "mPackageOrder=" + mPackageOrder);
pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 3b70890..f9eaabd 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -25,6 +25,8 @@
import android.hardware.tv.tuner.LnbTone;
import android.hardware.tv.tuner.LnbVoltage;
import android.media.tv.tuner.Tuner.Result;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -147,10 +149,13 @@
public static final int EVENT_TYPE_LNB_OVERLOAD = LnbEventType.LNB_OVERLOAD;
private static final String TAG = "Lnb";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Map<LnbCallback, Executor> mCallbackMap =
new HashMap<LnbCallback, Executor>();
Tuner mOwner;
+ TunerResourceManager mTunerResourceManager;
+ int mClientId;
private final Object mCallbackLock = new Object();
@@ -174,6 +179,10 @@
}
}
setOwner(tuner);
+ if (mOwner != null) {
+ mTunerResourceManager = mOwner.getTunerResourceManager();
+ mClientId = mOwner.getClientId();
+ }
}
/**
@@ -210,6 +219,8 @@
Objects.requireNonNull(newOwner, "newOwner must not be null");
synchronized (mLock) {
mOwner = newOwner;
+ mTunerResourceManager = newOwner.getTunerResourceManager();
+ mClientId = newOwner.getClientId();
}
}
@@ -317,21 +328,42 @@
* Releases the LNB instance.
*/
public void close() {
- synchronized (mLock) {
- if (mIsClosed) {
- return;
- }
- int res = nativeClose();
- if (res != Tuner.RESULT_SUCCESS) {
- TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
- } else {
- mIsClosed = true;
- if (mOwner != null) {
- mOwner.releaseLnb();
- mOwner = null;
+ acquireTRMSLock("close()");
+ try {
+ synchronized (mLock) {
+ if (mIsClosed) {
+ return;
}
- mCallbackMap.clear();
+ int res = nativeClose();
+ if (res != Tuner.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
+ } else {
+ mIsClosed = true;
+ if (mOwner != null) {
+ mOwner.releaseLnb();
+ mOwner = null;
+ }
+ mCallbackMap.clear();
+ }
}
+ } finally {
+ releaseTRMSLock();
}
}
+
+ private void acquireTRMSLock(String functionNameForLog) {
+ if (DEBUG) {
+ Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
+ + "for clientId:" + mClientId);
+ }
+ if (!mTunerResourceManager.acquireLock(mClientId)) {
+ Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog
+ + " for clientId:" + mClientId + " - this can cause deadlock between"
+ + " Tuner API calls and onReclaimResources()");
+ }
+ }
+
+ private void releaseTRMSLock() {
+ mTunerResourceManager.releaseLock(mClientId);
+ }
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f87e47e..9924fae 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -903,6 +903,9 @@
}
}
+ /**
+ * Releases Lnb resource if held. TRMS lock must be acquired prior to calling this function.
+ */
private void closeLnb() {
mLnbLock.lock();
try {
@@ -2806,6 +2809,10 @@
return mClientId;
}
+ /* package */ TunerResourceManager getTunerResourceManager() {
+ return mTunerResourceManager;
+ }
+
private void acquireTRMSLock(String functionNameForLog) {
if (DEBUG) {
Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
diff --git a/omapi/java/android/se/omapi/SeFrameworkInitializer.java b/omapi/java/android/se/omapi/SeFrameworkInitializer.java
new file mode 100644
index 0000000..d09a35d
--- /dev/null
+++ b/omapi/java/android/se/omapi/SeFrameworkInitializer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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.se.omapi;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.nfc.Flags;
+
+/**
+ * Class for performing registration for SE service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+public class SeFrameworkInitializer {
+ private SeFrameworkInitializer() {}
+
+ private static volatile SeServiceManager sSeServiceManager;
+
+ /**
+ * Sets an instance of {@link SeServiceManager} that allows
+ * the se mainline module to register/obtain se binder services. This is called
+ * by the platform during the system initialization.
+ *
+ * @param seServiceManager instance of {@link SeServiceManager} that allows
+ * the se/nfc mainline module to register/obtain se binder services.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static void setSeServiceManager(
+ @NonNull SeServiceManager seServiceManager) {
+ if (sSeServiceManager != null) {
+ throw new IllegalStateException("setSeServiceManager called twice!");
+ }
+
+ if (seServiceManager == null) {
+ throw new IllegalArgumentException("seServiceManager must not be null");
+ }
+
+ sSeServiceManager = seServiceManager;
+ }
+
+ /**
+ * Gets an instance of {@link SeServiceManager} that allows
+ * the se mainline module to register/obtain se binder services.
+ *
+ * @return instance of {@link SeServiceManager} that allows
+ * the se/nfc mainline module to register/obtain se binder services.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @Nullable
+ public static SeServiceManager getSeServiceManager() {
+ return sSeServiceManager;
+ }
+}
diff --git a/omapi/java/android/se/omapi/SeServiceManager.java b/omapi/java/android/se/omapi/SeServiceManager.java
new file mode 100644
index 0000000..79107370
--- /dev/null
+++ b/omapi/java/android/se/omapi/SeServiceManager.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 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.
+ */
+
+
+/**********************************************************************
+ * This file is not a part of the SE mainline module *
+ * *******************************************************************/
+
+package android.se.omapi;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.content.Context;
+import android.nfc.Flags;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the
+ * SecureElement service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+public class SeServiceManager {
+
+ /**
+ * @hide
+ */
+ public SeServiceManager() {
+ }
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ * @hide
+ */
+ @SystemApi(client = Client.MODULE_LIBRARIES)
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Register a system server binding object for a service.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void register(@NonNull IBinder service) {
+ ServiceManager.addService(mServiceName, service);
+ }
+
+ /**
+ * Get the system server binding object for a service.
+ *
+ * <p>This blocks until the service instance is ready,
+ * or a timeout happens, in which case it returns null.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+
+ /**
+ * Get the system server binding object for a service.
+ *
+ * <p>This blocks until the service instance is ready,
+ * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public IBinder getOrThrow() throws ServiceNotFoundException {
+ try {
+ return ServiceManager.getServiceOrThrow(mServiceName);
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ throw new ServiceNotFoundException(mServiceName);
+ }
+ }
+
+ /**
+ * Get the system server binding object for a service. If the specified service is
+ * not available, it returns null.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @Nullable
+ public IBinder tryGet() {
+ return ServiceManager.checkService(mServiceName);
+ }
+ }
+
+ /**
+ * See {@link ServiceRegisterer#getOrThrow}.
+ * @hide
+ */
+ @SystemApi(client = Client.MODULE_LIBRARIES)
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+ /**
+ * Constructor.
+ *
+ * @param name the name of the binder service that cannot be found.
+ *
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public ServiceNotFoundException(@NonNull String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "secure_element" service.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public ServiceRegisterer getSeManagerServiceRegisterer() {
+ return new ServiceRegisterer(Context.SECURE_ELEMENT_SERVICE);
+ }
+}
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 233aee2..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -50,46 +50,3 @@
proguard_compatibility: false,
},
}
-
-android_app {
- name: "ClockworkCredentialManager",
- defaults: ["platform_app_defaults"],
- certificate: "platform",
- manifest: "wear/AndroidManifest.xml",
- srcs: ["wear/src/**/*.kt"],
- resource_dirs: ["wear/res"],
-
- dex_preopt: {
- profile_guided: true,
- profile: "wear/profile.txt.prof",
- },
-
- static_libs: [
- "PlatformComposeCore",
- "androidx.activity_activity-compose",
- "androidx.appcompat_appcompat",
- "androidx.compose.foundation_foundation",
- "androidx.compose.foundation_foundation-layout",
- "androidx.compose.material_material-icons-core",
- "androidx.compose.material_material-icons-extended",
- "androidx.compose.ui_ui",
- "androidx.core_core-ktx",
- "androidx.credentials_credentials",
- "androidx.lifecycle_lifecycle-extensions",
- "androidx.lifecycle_lifecycle-livedata",
- "androidx.lifecycle_lifecycle-runtime-ktx",
- "androidx.lifecycle_lifecycle-viewmodel-compose",
- "androidx.wear.compose_compose-foundation",
- "androidx.wear.compose_compose-material",
- "kotlinx-coroutines-core",
- ],
-
- platform_apis: true,
- privileged: true,
-
- kotlincflags: ["-Xjvm-default=all"],
-
- optimize: {
- proguard_compatibility: false,
- },
-}
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index 4161601..a5ccdb6 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -17,31 +17,43 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.credentialmanager">
+ package="com.android.credentialmanager">
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+ <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
<application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.CredentialSelector">
-
- <activity
- android:name=".CredentialSelectorActivity"
- android:exported="true"
- android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
- android:launchMode="singleTop"
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:excludeFromRecents="true"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
android:theme="@style/Theme.CredentialSelector">
- </activity>
- </application>
+
+ <activity
+ android:name=".CredentialSelectorActivity"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
+ android:launchMode="singleTop"
+ android:label="@string/app_name"
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.CredentialSelector">
+ </activity>
+ <service
+ android:name=".autofill.CredentialAutofillService"
+ android:exported="false"
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE">
+ <meta-data
+ android:name="android.autofill"
+ android:resource="@xml/autofill_service_configuration"/>
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService"/>
+ </intent-filter>
+ </service>
+ </application>
</manifest>
diff --git a/packages/CredentialManager/horologist/Android.bp b/packages/CredentialManager/horologist/Android.bp
new file mode 100644
index 0000000..bb255bd
--- /dev/null
+++ b/packages/CredentialManager/horologist/Android.bp
@@ -0,0 +1,28 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// TODO: ag/24733147 - Remove this project once it is imported.
+android_library {
+ name: "Horologist",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.compose.ui_ui-tooling",
+ "androidx.navigation_navigation-compose",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-compose",
+ "androidx.wear.compose_compose-foundation",
+ "androidx.wear.compose_compose-material",
+ "androidx.wear.compose_compose-navigation",
+ ],
+}
diff --git a/packages/CredentialManager/horologist/AndroidManifest.xml b/packages/CredentialManager/horologist/AndroidManifest.xml
new file mode 100644
index 0000000..e386ce2
--- /dev/null
+++ b/packages/CredentialManager/horologist/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2023 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.horologist">
+
+ <uses-feature android:name="android.hardware.type.watch" />
+
+</manifest>
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
new file mode 100644
index 0000000..ae77605
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.annotations
+
+@RequiresOptIn(
+ message = "Horologist API is experimental. The API may be changed in the future.",
+)
+@Retention(AnnotationRetention.BINARY)
+public annotation class ExperimentalHorologistApi
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/BelowTimeTextPreview.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/BelowTimeTextPreview.kt
new file mode 100644
index 0000000..e6025fc
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/BelowTimeTextPreview.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.layout
+
+import androidx.compose.runtime.Composable
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+
+@OptIn(ExperimentalHorologistApi::class)
+@Composable
+public fun belowTimeTextPreview(): ScalingLazyColumnState {
+ return ScalingLazyColumnDefaults.belowTimeText().create()
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
new file mode 100644
index 0000000..c88bbd8
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+
+/**
+ * Default layouts for ScalingLazyColumnState, based on UX guidance.
+ */
+public object ScalingLazyColumnDefaults {
+ /**
+ * Layout the first item, directly under the time text.
+ * This is positioned from the top of the screen instead of the
+ * center.
+ */
+ @ExperimentalHorologistApi
+ public fun belowTimeText(
+ rotaryMode: RotaryMode = RotaryMode.Scroll,
+ firstItemIsFullWidth: Boolean = false,
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ topPaddingDp: Dp = 32.dp + (if (firstItemIsFullWidth) 20.dp else 0.dp),
+ ): ScalingLazyColumnState.Factory {
+ return object : ScalingLazyColumnState.Factory {
+ @Composable
+ override fun create(): ScalingLazyColumnState {
+ val density = LocalDensity.current
+ val configuration = LocalConfiguration.current
+
+ return remember {
+ val screenHeightPx =
+ with(density) { configuration.screenHeightDp.dp.roundToPx() }
+ val topPaddingPx = with(density) { topPaddingDp.roundToPx() }
+ val topScreenOffsetPx = screenHeightPx / 2 - topPaddingPx
+
+ ScalingLazyColumnState(
+ initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+ index = 0,
+ offsetPx = topScreenOffsetPx,
+ ),
+ anchorType = ScalingLazyListAnchorType.ItemStart,
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = horizontalAlignment,
+ contentPadding = contentPadding,
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Layout the item [initialCenterIndex] at [initialCenterOffset] from the
+ * center of the screen.
+ */
+ @ExperimentalHorologistApi
+ public fun scalingLazyColumnDefaults(
+ rotaryMode: RotaryMode = RotaryMode.Scroll,
+ initialCenterIndex: Int = 1,
+ initialCenterOffset: Int = 0,
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ autoCentering: AutoCenteringParams? = AutoCenteringParams(
+ initialCenterIndex,
+ initialCenterOffset,
+ ),
+ anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+ hapticsEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ ): ScalingLazyColumnState.Factory {
+ return object : ScalingLazyColumnState.Factory {
+ @Composable
+ override fun create(): ScalingLazyColumnState {
+ return remember {
+ ScalingLazyColumnState(
+ initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+ index = initialCenterIndex,
+ offsetPx = initialCenterOffset,
+ ),
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = horizontalAlignment,
+ contentPadding = contentPadding,
+ autoCentering = autoCentering,
+ anchorType = anchorType,
+ hapticsEnabled = hapticsEnabled,
+ reverseLayout = reverseLayout,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
new file mode 100644
index 0000000..3a12b9f
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+@file:OptIn(ExperimentalHorologistApi::class, ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.ScalingParams
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+import com.google.android.horologist.compose.rotaryinput.rememberDisabledHaptic
+import com.google.android.horologist.compose.rotaryinput.rememberRotaryHapticHandler
+import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll
+import com.google.android.horologist.compose.rotaryinput.rotaryWithSnap
+import com.google.android.horologist.compose.rotaryinput.toRotaryScrollAdapter
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults as WearScalingLazyColumnDefaults
+
+/**
+ * A Config and State object wrapping up all configuration for a [ScalingLazyColumn].
+ * This allows defaults such as [ScalingLazyColumnDefaults.belowTimeText].
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnState(
+ public val initialScrollPosition: ScrollPosition = ScrollPosition(1, 0),
+ public val autoCentering: AutoCenteringParams? = AutoCenteringParams(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ ),
+ public val anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+ public val contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ public val rotaryMode: RotaryMode = RotaryMode.Scroll,
+ public val reverseLayout: Boolean = false,
+ public val verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = if (!reverseLayout) Alignment.Top else Alignment.Bottom,
+ ),
+ public val horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ public val flingBehavior: FlingBehavior? = null,
+ public val userScrollEnabled: Boolean = true,
+ public val scalingParams: ScalingParams = WearScalingLazyColumnDefaults.scalingParams(),
+ public val hapticsEnabled: Boolean = true,
+) {
+ private var _state: ScalingLazyListState? = null
+ public var state: ScalingLazyListState
+ get() {
+ if (_state == null) {
+ _state = ScalingLazyListState(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ )
+ }
+ return _state!!
+ }
+ set(value) {
+ _state = value
+ }
+
+ public sealed interface RotaryMode {
+ public object Snap : RotaryMode
+ public object Scroll : RotaryMode
+
+ @Deprecated(
+ "Use RotaryMode.Scroll instead",
+ replaceWith = ReplaceWith("RotaryMode.Scroll"),
+ )
+ public object Fling : RotaryMode
+ }
+
+ public data class ScrollPosition(
+ val index: Int,
+ val offsetPx: Int,
+ )
+
+ public fun interface Factory {
+ @Composable
+ public fun create(): ScalingLazyColumnState
+ }
+}
+
+@Composable
+public fun rememberColumnState(
+ factory: ScalingLazyColumnState.Factory = ScalingLazyColumnDefaults.belowTimeText(),
+): ScalingLazyColumnState {
+ val columnState = factory.create()
+
+ columnState.state = rememberSaveable(saver = ScalingLazyListState.Saver) {
+ columnState.state
+ }
+
+ return columnState
+}
+
+@ExperimentalHorologistApi
+@Composable
+public fun ScalingLazyColumn(
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ val focusRequester = rememberActiveFocusRequester()
+
+ val rotaryHaptics = if (columnState.hapticsEnabled) {
+ rememberRotaryHapticHandler(columnState.state)
+ } else {
+ rememberDisabledHaptic()
+ }
+ val modifierWithRotary = when (columnState.rotaryMode) {
+ RotaryMode.Snap -> modifier.rotaryWithSnap(
+ focusRequester = focusRequester,
+ rotaryScrollAdapter = columnState.state.toRotaryScrollAdapter(),
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+
+ else -> modifier.rotaryWithScroll(
+ focusRequester = focusRequester,
+ scrollableState = columnState.state,
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+ }
+
+ ScalingLazyColumn(
+ modifier = modifierWithRotary,
+ state = columnState.state,
+ contentPadding = columnState.contentPadding,
+ reverseLayout = columnState.reverseLayout,
+ verticalArrangement = columnState.verticalArrangement,
+ horizontalAlignment = columnState.horizontalAlignment,
+ flingBehavior = columnState.flingBehavior ?: ScrollableDefaults.flingBehavior(),
+ userScrollEnabled = columnState.userScrollEnabled,
+ scalingParams = columnState.scalingParams,
+ anchorType = columnState.anchorType,
+ autoCentering = columnState.autoCentering,
+ content = content,
+ )
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
new file mode 100644
index 0000000..623ae1a
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.scrollAway
+import com.google.android.horologist.compose.navscaffold.ScalingLazyColumnScrollableState
+
+internal fun Modifier.scrollAway(
+ scrollState: State<ScrollableState?>,
+): Modifier = composed {
+ when (val state = scrollState.value) {
+ is ScalingLazyColumnScrollableState -> {
+ val offsetDp = with(LocalDensity.current) {
+ state.initialOffsetPx.toDp()
+ }
+ this.scrollAway(state.scalingLazyListState, state.initialIndex, offsetDp)
+ }
+ is ScalingLazyListState -> this.scrollAway(state)
+ is LazyListState -> this.scrollAway(state)
+ is ScrollState -> this.scrollAway(state)
+ // Disabled
+ null -> this.hidden()
+ // Enabled but no scroll state
+ else -> this
+ }
+}
+
+internal fun Modifier.hidden(): Modifier = layout { _, _ -> layout(0, 0) {} }
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Button.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Button.kt
new file mode 100644
index 0000000..57e0c10
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Button.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.material
+
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.Dp
+import androidx.wear.compose.material.Button
+import androidx.wear.compose.material.ButtonColors
+import androidx.wear.compose.material.ButtonDefaults
+import androidx.wear.compose.material.ButtonDefaults.DefaultButtonSize
+import androidx.wear.compose.material.ButtonDefaults.DefaultIconSize
+import androidx.wear.compose.material.ButtonDefaults.LargeButtonSize
+import androidx.wear.compose.material.ButtonDefaults.LargeIconSize
+import androidx.wear.compose.material.ButtonDefaults.SmallButtonSize
+import androidx.wear.compose.material.ButtonDefaults.SmallIconSize
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+
+/**
+ * This component is an alternative to [Button], providing the following:
+ * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
+ * by the Wear guidelines;
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun Button(
+ imageVector: ImageVector,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true,
+) {
+ Button(
+ icon = imageVector,
+ contentDescription = contentDescription,
+ onClick = onClick,
+ modifier = modifier,
+ colors = colors,
+ buttonSize = buttonSize,
+ iconRtlMode = iconRtlMode,
+ enabled = enabled,
+ )
+}
+
+/**
+ * This component is an alternative to [Button], providing the following:
+ * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
+ * by the Wear guidelines;
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun Button(
+ @DrawableRes id: Int,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true,
+) {
+ Button(
+ icon = id,
+ contentDescription = contentDescription,
+ onClick = onClick,
+ modifier = modifier,
+ colors = colors,
+ buttonSize = buttonSize,
+ iconRtlMode = iconRtlMode,
+ enabled = enabled,
+ )
+}
+
+@OptIn(ExperimentalHorologistApi::class)
+@Composable
+internal fun Button(
+ icon: Any,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true,
+) {
+ Button(
+ onClick = onClick,
+ modifier = modifier.size(buttonSize.tapTargetSize),
+ enabled = enabled,
+ colors = colors,
+ ) {
+ val iconModifier = Modifier
+ .size(buttonSize.iconSize)
+ .align(Alignment.Center)
+
+ Icon(
+ icon = icon,
+ contentDescription = contentDescription,
+ modifier = iconModifier,
+ rtlMode = iconRtlMode,
+ )
+ }
+}
+
+@ExperimentalHorologistApi
+public sealed class ButtonSize(
+ public val iconSize: Dp,
+ public val tapTargetSize: Dp,
+) {
+ public object Default :
+ ButtonSize(iconSize = DefaultIconSize, tapTargetSize = DefaultButtonSize)
+
+ public object Large : ButtonSize(iconSize = LargeIconSize, tapTargetSize = LargeButtonSize)
+ public object Small : ButtonSize(iconSize = SmallIconSize, tapTargetSize = SmallButtonSize)
+
+ /**
+ * Custom sizes should follow the [accessibility principles and guidance for touch targets](https://developer.android.com/training/wearables/accessibility#set-minimum).
+ */
+ public data class Custom(val customIconSize: Dp, val customTapTargetSize: Dp) :
+ ButtonSize(iconSize = customIconSize, tapTargetSize = customTapTargetSize)
+}
+
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Icon.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Icon.kt
new file mode 100644
index 0000000..74e54c0
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/Icon.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.material
+
+import androidx.annotation.DrawableRes
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.LocalContentAlpha
+import androidx.wear.compose.material.LocalContentColor
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+
+/**
+ * This component is an alternative to [Icon], providing the following:
+ * - a convenient way of setting the icon to be mirrored in RTL mode;
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun Icon(
+ imageVector: ImageVector,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default,
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+ Icon(
+ modifier = modifier.scale(
+ scaleX = if (shouldMirror) -1f else 1f,
+ scaleY = 1f,
+ ),
+ imageVector = imageVector,
+ contentDescription = contentDescription,
+ tint = tint,
+ )
+}
+
+/**
+ * This component is an alternative to [Icon], providing the following:
+ * - a convenient way of setting the icon to be mirrored in RTL mode;
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun Icon(
+ @DrawableRes id: Int,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default,
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+
+ Icon(
+ painter = painterResource(id = id),
+ contentDescription = contentDescription,
+ modifier = modifier.scale(
+ scaleX = if (shouldMirror) -1f else 1f,
+ scaleY = 1f,
+ ),
+ tint = tint,
+ )
+}
+
+@OptIn(ExperimentalHorologistApi::class)
+@Composable
+internal fun Icon(
+ icon: Any,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default,
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+
+ val iconModifier = modifier.scale(
+ scaleX = if (shouldMirror) -1f else 1f,
+ scaleY = 1f,
+ )
+ when (icon) {
+ is ImageVector -> {
+ Icon(
+ imageVector = icon,
+ modifier = iconModifier,
+ contentDescription = contentDescription,
+ tint = tint,
+ )
+ }
+
+ is Int -> {
+ Icon(
+ painter = painterResource(id = icon),
+ contentDescription = contentDescription,
+ modifier = iconModifier,
+ tint = tint,
+ )
+ }
+
+ else -> throw IllegalArgumentException("Type not supported.")
+ }
+}
+
+@ExperimentalHorologistApi
+public enum class IconRtlMode {
+ Default,
+ Mirrored,
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/util/A11y.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/util/A11y.kt
new file mode 100644
index 0000000..39de2e1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/material/util/A11y.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.material.util
+
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+
+/**
+ * Make explicit that a conscious decision was made to mark an element as decorative, so it does not
+ * have associated actions or state.
+ *
+ * https://developer.android.com/jetpack/compose/accessibility#describe-visual
+ */
+@ExperimentalHorologistApi
+public val DECORATIVE_ELEMENT_CONTENT_DESCRIPTION: String? = null
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
new file mode 100644
index 0000000..14c0ba1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class, SavedStateHandleSaveableApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import android.os.Bundle
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
+import androidx.lifecycle.viewmodel.compose.saveable
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.material.VignettePosition
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.PositionIndicatorMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode.ScrollAway
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.Off
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.On
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.WhenScrollable
+
+/**
+ * A ViewModel that backs the WearNavScaffold to allow each composable to interact and effect
+ * the [Scaffold] positionIndicator, vignette and timeText.
+ *
+ * A ViewModel is used to allow the same current instance to be shared between the WearNavScaffold
+ * and the composable screen via [NavHostController.currentBackStackEntry].
+ */
+public open class NavScaffoldViewModel(
+ private val savedStateHandle: SavedStateHandle,
+) : ViewModel() {
+ internal var initialIndex: Int? = null
+ internal var initialOffsetPx: Int? = null
+ internal var scrollType by mutableStateOf<ScrollType?>(null)
+
+ private lateinit var _scrollableState: ScrollableState
+
+ /**
+ * Returns the scrollable state for this composable or null if the scaffold should
+ * not consider this element to be scrollable.
+ */
+ public val scrollableState: ScrollableState?
+ get() = if (scrollType == null || scrollType == ScrollType.None) {
+ null
+ } else {
+ _scrollableState
+ }
+
+ /**
+ * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+ * bottom. Defaults to on for scrollable screens.
+ */
+ public var vignettePosition: VignetteMode by mutableStateOf(WhenScrollable)
+
+ /**
+ * The configuration of [TimeText], defaults to [TimeTextMode.ScrollAway] which will move the
+ * time text above the screen to avoid overlapping with the content moving up.
+ */
+ public var timeTextMode: TimeTextMode by mutableStateOf(ScrollAway)
+
+ /**
+ * The configuration of [PositionIndicator]. The default is to show a scroll bar while the
+ * scroll is in progress.
+ */
+ public var positionIndicatorMode: PositionIndicatorMode
+ by mutableStateOf(PositionIndicatorMode.On)
+
+ internal fun initializeScrollState(scrollStateBuilder: () -> ScrollState): ScrollState {
+ check(scrollType == null || scrollType == ScrollType.ScrollState)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScrollState
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScrollState",
+ saver = ScrollState.Saver,
+ ) {
+ scrollStateBuilder()
+ }
+ }
+
+ return _scrollableState as ScrollState
+ }
+
+ internal fun initializeScalingLazyListState(
+ scrollableStateBuilder: () -> ScalingLazyListState,
+ ): ScalingLazyListState {
+ check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScalingLazyColumn
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScalingLazyListState",
+ saver = ScalingLazyListState.Saver,
+ ) {
+ scrollableStateBuilder().also {
+ initialIndex = it.centerItemIndex
+ initialOffsetPx = it.centerItemScrollOffset
+ }
+ }
+ }
+
+ return _scrollableState as ScalingLazyListState
+ }
+
+ internal fun initializeScalingLazyListState(
+ columnState: ScalingLazyColumnState,
+ ) {
+ check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.ScalingLazyColumn
+
+ initialIndex = columnState.initialScrollPosition.index
+ initialOffsetPx = columnState.initialScrollPosition.offsetPx
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.ScalingLazyListState",
+ saver = ScalingLazyListState.Saver,
+ ) {
+ columnState.state
+ }
+ }
+
+ columnState.state = _scrollableState as ScalingLazyListState
+ }
+
+ internal fun initializeLazyList(
+ scrollableStateBuilder: () -> LazyListState,
+ ): LazyListState {
+ check(scrollType == null || scrollType == ScrollType.LazyList)
+
+ if (scrollType == null) {
+ scrollType = ScrollType.LazyList
+
+ _scrollableState = savedStateHandle.saveable(
+ key = "navScaffold.LazyListState",
+ saver = LazyListState.Saver,
+ ) {
+ scrollableStateBuilder()
+ }
+ }
+
+ return _scrollableState as LazyListState
+ }
+
+ internal enum class ScrollType {
+ None, ScalingLazyColumn, ScrollState, LazyList
+ }
+
+ /**
+ * The configuration of [TimeText], defaults to [ScrollAway] which will move the time text above the
+ * screen to avoid overlapping with the content moving up.
+ */
+ public enum class TimeTextMode {
+ On, Off, ScrollAway
+ }
+
+ /**
+ * The configuration of [PositionIndicator]. The default is to show a scroll bar while the
+ * scroll is in progress.
+ */
+ public enum class PositionIndicatorMode {
+ On, Off
+ }
+
+ /**
+ * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+ * bottom. Defaults to on for scrollable screens.
+ */
+ public sealed interface VignetteMode {
+ public object WhenScrollable : VignetteMode
+ public object Off : VignetteMode
+ public data class On(val position: VignettePosition) : VignetteMode
+ }
+
+ internal fun timeTextScrollableState(): ScrollableState? {
+ return when (timeTextMode) {
+ ScrollAway -> {
+ when (this.scrollType) {
+ ScrollType.ScrollState -> {
+ this.scrollableState as ScrollState
+ }
+
+ ScrollType.ScalingLazyColumn -> {
+ val scalingLazyListState =
+ this.scrollableState as ScalingLazyListState
+
+ ScalingLazyColumnScrollableState(scalingLazyListState, initialIndex
+ ?: 1, initialOffsetPx ?: 0)
+ }
+
+ ScrollType.LazyList -> {
+ this.scrollableState as LazyListState
+ }
+
+ else -> {
+ ScrollState(0)
+ }
+ }
+ }
+
+ TimeTextMode.On -> {
+ ScrollState(0)
+ }
+
+ else -> {
+ null
+ }
+ }
+ }
+}
+
+internal class ScalingLazyColumnScrollableState(
+ val scalingLazyListState: ScalingLazyListState,
+ val initialIndex: Int,
+ val initialOffsetPx: Int,
+) : ScrollableState by scalingLazyListState
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScaffoldContext<T : ScrollableState>(
+ val backStackEntry: NavBackStackEntry,
+ val scrollableState: T,
+ val viewModel: NavScaffoldViewModel,
+) {
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
+
+public data class NonScrollableScaffoldContext(
+ val backStackEntry: NavBackStackEntry,
+ val viewModel: NavScaffoldViewModel,
+) {
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScrollableScaffoldContext(
+ val backStackEntry: NavBackStackEntry,
+ val columnState: ScalingLazyColumnState,
+ val viewModel: NavScaffoldViewModel,
+) {
+ val scrollableState: ScalingLazyListState
+ get() = columnState.state
+
+ var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+ var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+ val arguments: Bundle?
+ get() = backStackEntry.arguments
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
new file mode 100644
index 0000000..315d822
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavDeepLink
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.navigation.SwipeDismissableNavHost
+import androidx.wear.compose.navigation.SwipeDismissableNavHostState
+import androidx.wear.compose.navigation.composable
+import androidx.wear.compose.navigation.currentBackStackEntryAsState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.scrollAway
+
+/**
+ * A Navigation and Scroll aware [Scaffold].
+ *
+ * In addition to [NavGraphBuilder.scrollable], 3 additional extensions are supported
+ * [scalingLazyColumnComposable], [scrollStateComposable] and
+ * [lazyListComposable].
+ *
+ * These should be used to build the [ScrollableState] or [FocusRequester] as well as
+ * configure the behaviour of [TimeText], [PositionIndicator] or [Vignette].
+ */
+@Composable
+public fun WearNavScaffold(
+ startDestination: String,
+ navController: NavHostController,
+ modifier: Modifier = Modifier,
+ snackbar: @Composable () -> Unit = {},
+ timeText: @Composable (Modifier) -> Unit = {
+ TimeText(
+ modifier = it,
+ )
+ },
+ state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
+ builder: NavGraphBuilder.() -> Unit,
+) {
+ val currentBackStackEntry: NavBackStackEntry? by navController.currentBackStackEntryAsState()
+
+ val viewModel: NavScaffoldViewModel? = currentBackStackEntry?.let {
+ viewModel(viewModelStoreOwner = it)
+ }
+
+ val scrollState: State<ScrollableState?> = remember(viewModel) {
+ derivedStateOf {
+ viewModel?.timeTextScrollableState()
+ }
+ }
+
+ Scaffold(
+ modifier = modifier.fillMaxSize(),
+ timeText = {
+ timeText(Modifier.scrollAway(scrollState))
+ },
+ positionIndicator = {
+ key(currentBackStackEntry?.destination?.route) {
+ val mode = viewModel?.positionIndicatorMode
+
+ if (mode == NavScaffoldViewModel.PositionIndicatorMode.On) {
+ NavPositionIndicator(viewModel)
+ }
+ }
+ },
+ vignette = {
+ key(currentBackStackEntry?.destination?.route) {
+ val vignettePosition = viewModel?.vignettePosition
+ if (vignettePosition is NavScaffoldViewModel.VignetteMode.On) {
+ Vignette(vignettePosition = vignettePosition.position)
+ }
+ }
+ },
+ ) {
+ Box {
+ SwipeDismissableNavHost(
+ navController = navController,
+ startDestination = startDestination,
+ state = state,
+ ) {
+ builder()
+ }
+
+ snackbar()
+ }
+ }
+}
+
+@Composable
+private fun NavPositionIndicator(viewModel: NavScaffoldViewModel) {
+ when (viewModel.scrollType) {
+ NavScaffoldViewModel.ScrollType.ScrollState ->
+ PositionIndicator(
+ scrollState = viewModel.scrollableState as ScrollState,
+ )
+
+ NavScaffoldViewModel.ScrollType.ScalingLazyColumn -> {
+ PositionIndicator(
+ scalingLazyListState = viewModel.scrollableState as ScalingLazyListState,
+ )
+ }
+
+ NavScaffoldViewModel.ScrollType.LazyList ->
+ PositionIndicator(
+ lazyListState = viewModel.scrollableState as LazyListState,
+ )
+
+ else -> {}
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The scalingLazyListState must be taken from the [ScaffoldContext].
+ */
+@Deprecated(
+ "Use listComposable",
+)
+public fun NavGraphBuilder.scalingLazyColumnComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ scrollStateBuilder: () -> ScalingLazyListState,
+ content: @Composable (ScaffoldContext<ScalingLazyListState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeScalingLazyListState(scrollStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The [ScalingLazyColumnState] must be taken from the [ScrollableScaffoldContext].
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.scrollable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ columnStateFactory: ScalingLazyColumnState.Factory =
+ ScalingLazyColumnDefaults.belowTimeText(),
+ content: @Composable (ScrollableScaffoldContext) -> Unit,
+) {
+ this@scrollable.composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val columnState = columnStateFactory.create()
+
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ viewModel.initializeScalingLazyListState(columnState)
+
+ content(ScrollableScaffoldContext(it, columnState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Scrollable item.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.scrollStateComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ scrollStateBuilder: () -> ScrollState = { ScrollState(0) },
+ content: @Composable (ScaffoldContext<ScrollState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeScrollState(scrollStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Lazy list such as LazyColumn.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.lazyListComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ lazyListStateBuilder: () -> LazyListState = { LazyListState() },
+ content: @Composable (ScaffoldContext<LazyListState>) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel(it)
+
+ val scrollState = viewModel.initializeLazyList(lazyListStateBuilder)
+
+ content(ScaffoldContext(it, scrollState, viewModel))
+ }
+ }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@Deprecated(
+ "Use composable",
+ ReplaceWith("composable(route, arguments, deepLinks, lazyListStateBuilder, content)"),
+)
+public fun NavGraphBuilder.wearNavComposable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ content: @Composable (NavBackStackEntry, NavScaffoldViewModel) -> Unit,
+) {
+ composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel()
+
+ content(it, viewModel)
+ }
+ }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.composable(
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ content: @Composable (NonScrollableScaffoldContext) -> Unit,
+) {
+ this@composable.composable(route, arguments, deepLinks) {
+ FocusedDestination {
+ val viewModel: NavScaffoldViewModel = viewModel()
+
+ content(NonScrollableScaffoldContext(it, viewModel))
+ }
+ }
+}
+
+@Composable
+internal fun FocusedDestination(content: @Composable () -> Unit) {
+ val lifecycle = LocalLifecycleOwner.current.lifecycle
+ val focused =
+ remember { mutableStateOf(lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) }
+
+ DisposableEffect(lifecycle) {
+ val listener = LifecycleEventObserver { _, _ ->
+ focused.value = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
+ }
+ lifecycle.addObserver(listener)
+ onDispose {
+ lifecycle.removeObserver(listener)
+ }
+ }
+
+ HierarchicalFocusCoordinator(requiresFocus = { focused.value }) {
+ content()
+ }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
new file mode 100644
index 0000000..c4af4a6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.os.Build
+import android.view.HapticFeedbackConstants
+import android.view.View
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalView
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.withContext
+import kotlin.math.abs
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryHaptics: ${generateMsg()}")
+ }
+}
+
+/**
+ * Throttling events within specified timeframe. Only first and last events will be received.
+ * For a flow emitting elements 1 to 30, with a 100ms delay between them:
+ * ```
+ * val flow = flow {
+ * for (i in 1..30) {
+ * delay(100)
+ * emit(i)
+ * }
+ * }
+ * ```
+ * With timeframe=1000 only those integers will be received: 1, 10, 20, 30 .
+ */
+internal fun <T> Flow<T>.throttleLatest(timeframe: Long): Flow<T> =
+ flow {
+ conflate().collect {
+ emit(it)
+ delay(timeframe)
+ }
+ }
+
+/**
+ * Handles haptics for rotary usage
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticHandler {
+
+ /**
+ * Handles haptics when scroll is used
+ */
+ @ExperimentalHorologistApi
+ public fun handleScrollHaptic(scrollDelta: Float)
+
+ /**
+ * Handles haptics when scroll with snap is used
+ */
+ @ExperimentalHorologistApi
+ public fun handleSnapHaptic(scrollDelta: Float)
+}
+
+/**
+ * Default implementation of [RotaryHapticHandler]. It handles haptic feedback based
+ * on the [scrollableState], scrolled pixels and [hapticsThresholdPx].
+ * Haptic is not fired in this class, instead it's sent to [hapticsChannel]
+ * where it'll performed later.
+ *
+ * @param scrollableState Haptic performed based on this state
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ */
+public class DefaultRotaryHapticHandler(
+ private val scrollableState: ScrollableState,
+ private val hapticsChannel: Channel<RotaryHapticsType>,
+ private val hapticsThresholdPx: Long = 50,
+) : RotaryHapticHandler {
+
+ private var overscrollHapticTriggered = false
+ private var currScrollPosition = 0f
+ private var prevHapticsPosition = 0f
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ currScrollPosition += scrollDelta
+ val diff = abs(currScrollPosition - prevHapticsPosition)
+
+ if (diff >= hapticsThresholdPx) {
+ trySendHaptic(RotaryHapticsType.ScrollTick)
+ prevHapticsPosition = currScrollPosition
+ }
+ }
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ trySendHaptic(RotaryHapticsType.ScrollItemFocus)
+ }
+ }
+
+ private fun trySendHaptic(rotaryHapticsType: RotaryHapticsType) {
+ // Ok to ignore the ChannelResult because we default to capacity = 2 and DROP_OLDEST
+ @Suppress("UNUSED_VARIABLE")
+ val unused = hapticsChannel.trySend(rotaryHapticsType)
+ }
+}
+
+/**
+ * Interface for Rotary haptic feedback
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticFeedback {
+ @ExperimentalHorologistApi
+ public fun performHapticFeedback(type: RotaryHapticsType)
+}
+
+/**
+ * Rotary haptic types
+ */
+@ExperimentalHorologistApi
+@JvmInline
+public value class RotaryHapticsType(private val type: Int) {
+ public companion object {
+ /**
+ * A scroll ticking haptic. Similar to texture haptic - performed each time when
+ * a scrollable content is scrolled by a certain distance
+ */
+ @ExperimentalHorologistApi
+ public val ScrollTick: RotaryHapticsType = RotaryHapticsType(1)
+
+ /**
+ * An item focus (snap) haptic. Performed when a scrollable content is snapped
+ * to a specific item.
+ */
+ @ExperimentalHorologistApi
+ public val ScrollItemFocus: RotaryHapticsType = RotaryHapticsType(2)
+
+ /**
+ * A limit(overscroll) haptic. Performed when a list reaches the limit
+ * (start or end) and can't scroll further
+ */
+ @ExperimentalHorologistApi
+ public val ScrollLimit: RotaryHapticsType = RotaryHapticsType(3)
+ }
+}
+
+/**
+ * Remember disabled haptics handler
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDisabledHaptic(): RotaryHapticHandler = remember {
+ object : RotaryHapticHandler {
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+ }
+}
+
+/**
+ * Remember rotary haptic handler.
+ * @param scrollableState A scrollableState, used to determine whether the end of the scrollable
+ * was reached or not.
+ * @param throttleThresholdMs Throttling events within specified timeframe.
+ * Only first and last events will be received. Check [throttleLatest] for more info.
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param rotaryHaptics Interface for Rotary haptic feedback which performs haptics
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberRotaryHapticHandler(
+ scrollableState: ScrollableState,
+ throttleThresholdMs: Long = 30,
+ hapticsThresholdPx: Long = 50,
+ hapticsChannel: Channel<RotaryHapticsType> = rememberHapticChannel(),
+ rotaryHaptics: RotaryHapticFeedback = rememberDefaultRotaryHapticFeedback(),
+): RotaryHapticHandler {
+ return remember(scrollableState, hapticsChannel, rotaryHaptics) {
+ DefaultRotaryHapticHandler(scrollableState, hapticsChannel, hapticsThresholdPx)
+ }.apply {
+ LaunchedEffect(hapticsChannel) {
+ hapticsChannel.receiveAsFlow()
+ .throttleLatest(throttleThresholdMs)
+ .collect { hapticType ->
+ // 'withContext' launches performHapticFeedback in a separate thread,
+ // as otherwise it produces a visible lag (b/219776664)
+ val currentTime = System.currentTimeMillis()
+ debugLog { "Haptics started" }
+ withContext(Dispatchers.Default) {
+ debugLog {
+ "Performing haptics, delay: " +
+ "${System.currentTimeMillis() - currentTime}"
+ }
+ rotaryHaptics.performHapticFeedback(hapticType)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun rememberHapticChannel() =
+ remember {
+ Channel<RotaryHapticsType>(
+ capacity = 2,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+ }
+
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDefaultRotaryHapticFeedback(): RotaryHapticFeedback =
+ LocalView.current.let { view -> remember { findDeviceSpecificHapticFeedback(view) } }
+
+internal fun findDeviceSpecificHapticFeedback(view: View): RotaryHapticFeedback =
+ if (isGooglePixelWatch()) {
+ PixelWatchRotaryHapticFeedback(view)
+ } else if (isGalaxyWatchClassic()) {
+ GalaxyWatchClassicHapticFeedback(view)
+ } else {
+ DefaultRotaryHapticFeedback(view)
+ }
+
+/**
+ * Default Rotary implementation for [RotaryHapticFeedback]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Pixel Watch
+ */
+@ExperimentalHorologistApi
+private class PixelWatchRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) {
+ ROTARY_SCROLL_ITEM_FOCUS
+ } else {
+ WEAR_SCROLL_ITEM_FOCUS
+ },
+ )
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_TICK else WEAR_SCROLL_TICK,
+ )
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(
+ if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_LIMIT else WEAR_SCROLL_LIMIT,
+ )
+ }
+ }
+ }
+
+ private companion object {
+ // Hidden constants from HapticFeedbackConstants.java specific for Pixel Watch
+ // API 33
+ public const val ROTARY_SCROLL_TICK: Int = 18
+ public const val ROTARY_SCROLL_ITEM_FOCUS: Int = 19
+ public const val ROTARY_SCROLL_LIMIT: Int = 20
+
+ // API 30
+ public const val WEAR_SCROLL_TICK: Int = 10002
+ public const val WEAR_SCROLL_ITEM_FOCUS: Int = 10003
+ public const val WEAR_SCROLL_LIMIT: Int = 10003
+ }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Galaxy Watch 4 Classic
+ */
+@ExperimentalHorologistApi
+private class GalaxyWatchClassicHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ @ExperimentalHorologistApi
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ // No haptic for scroll snap ( we have physical bezel)
+ }
+
+ RotaryHapticsType.ScrollTick -> {
+ // No haptic for scroll tick ( we have physical bezel)
+ }
+
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+ }
+}
+
+private fun isGalaxyWatchClassic(): Boolean =
+ Build.MODEL.matches("SM-R8[89]5.".toRegex())
+
+private fun isGooglePixelWatch(): Boolean =
+ Build.MODEL.startsWith("Google Pixel Watch")
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
new file mode 100644
index 0000000..3ca16c1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
@@ -0,0 +1,1301 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.view.ViewConfiguration
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.copy
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.rotary.onRotaryScrollEvent
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.util.fastSumBy
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.transformLatest
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.math.sign
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryScroll: ${generateMsg()}")
+ }
+}
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports fling.
+ *
+ * Fling algorithm:
+ * - A scroll with RSB/ Bezel happens.
+ * - If this is a first rotary event after the threshold ( by default 200ms), a new scroll
+ * session starts by resetting all necessary parameters
+ * - A delta value is added into VelocityTracker and a new speed is calculated.
+ * - If the current speed is bigger than the previous one, this value is remembered as
+ * a latest fling speed with a timestamp
+ * - After each scroll event a fling countdown starts ( by default 70ms) which
+ * resets if new scroll event is received
+ * - If fling countdown is finished - it means that the finger was probably raised from RSB, there will be no other events and probably
+ * this is the last event during this session. After it a fling is triggered.
+ * - Fling is stopped when a new scroll event happens
+ *
+ * The screen containing the scrollable item should request the focus
+ * by calling [requestFocus] method
+ *
+ * ```
+ * LaunchedEffect(Unit) {
+ * focusRequester.requestFocus()
+ * }
+ * ```
+ * @param focusRequester Requests the focus for rotary input
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing fling behavior.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Deprecated(
+ "Use rotaryWithScroll instead",
+ ReplaceWith(
+ "this.rotaryWithScroll(scrollableState, focusRequester, " +
+ "flingBehavior, rotaryHaptics, reverseDirection)",
+ ),
+)
+@Composable
+public fun Modifier.rotaryWithFling(
+ focusRequester: FocusRequester,
+ scrollableState: ScrollableState,
+ flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+ rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports scroll with fling.
+ *
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param flingBehavior Logic describing fling behavior. If null fling will not happen.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithScroll(
+ scrollableState: ScrollableState,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
+ rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports snap.
+ *
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithSnap(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ snapParameters: SnapParameters = RotaryDefaults.snapParametersDefault(),
+ rotaryHaptics: RotaryHapticHandler =
+ rememberRotaryHapticHandler(rotaryScrollAdapter.scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+ rotaryScrollHandler =
+ RotaryDefaults.rememberSnapHandler(rotaryScrollAdapter, snapParameters),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+)
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * An extension function for creating [RotaryScrollAdapter] from [ScalingLazyListState]
+ */
+@ExperimentalHorologistApi
+public fun ScalingLazyListState.toRotaryScrollAdapter(): RotaryScrollAdapter =
+ ScalingLazyColumnRotaryScrollAdapter(this)
+
+/**
+ * An implementation of rotary scroll adapter for [ScalingLazyColumn]
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnRotaryScrollAdapter(
+ override val scrollableState: ScalingLazyListState,
+) : RotaryScrollAdapter {
+
+ /**
+ * Calculates an average height of an item by taking an average from visible items height.
+ */
+ override fun averageItemSize(): Float {
+ val visibleItems = scrollableState.layoutInfo.visibleItemsInfo
+ return (visibleItems.fastSumBy { it.unadjustedSize } / visibleItems.size).toFloat()
+ }
+
+ /**
+ * Current (centred) item index
+ */
+ override fun currentItemIndex(): Int = scrollableState.centerItemIndex
+
+ /**
+ * An offset from the item centre
+ */
+ override fun currentItemOffset(): Float = scrollableState.centerItemScrollOffset.toFloat()
+
+ /**
+ * The total count of items in ScalingLazyColumn
+ */
+ override fun totalItemsCount(): Int = scrollableState.layoutInfo.totalItemsCount
+}
+
+/**
+ * An adapter which connects scrollableState to Rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollAdapter {
+
+ /**
+ * A scrollable state. Used for performing scroll when Rotary events received
+ */
+ @ExperimentalHorologistApi
+ public val scrollableState: ScrollableState
+
+ /**
+ * Average size of an item. Used for estimating the scrollable distance
+ */
+ @ExperimentalHorologistApi
+ public fun averageItemSize(): Float
+
+ /**
+ * A current item index. Used for scrolling
+ */
+ @ExperimentalHorologistApi
+ public fun currentItemIndex(): Int
+
+ /**
+ * An offset from the centre or the border of the current item.
+ */
+ @ExperimentalHorologistApi
+ public fun currentItemOffset(): Float
+
+ /**
+ * The total count of items in [scrollableState]
+ */
+ @ExperimentalHorologistApi
+ public fun totalItemsCount(): Int
+}
+
+/**
+ * Defaults for rotary modifiers
+ */
+@ExperimentalHorologistApi
+public object RotaryDefaults {
+
+ /**
+ * Handles scroll with fling.
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing Fling behavior. If null - fling will not happen
+ * @param isLowRes Whether the input is Low-res (a bezel) or high-res(a crown/rsb)
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun rememberFlingHandler(
+ scrollableState: ScrollableState,
+ flingBehavior: FlingBehavior? = null,
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ val viewConfiguration = ViewConfiguration.get(LocalContext.current)
+
+ return remember(scrollableState, flingBehavior, isLowRes) {
+ debugLog { "isLowRes : $isLowRes" }
+ fun rotaryFlingBehavior() = flingBehavior?.run {
+ DefaultRotaryFlingBehavior(
+ scrollableState,
+ flingBehavior,
+ viewConfiguration,
+ flingTimeframe =
+ if (isLowRes) lowResFlingTimeframe else highResFlingTimeframe,
+ )
+ }
+
+ fun scrollBehavior() = AnimationScrollBehavior(scrollableState)
+
+ if (isLowRes) {
+ LowResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ } else {
+ HighResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ }
+ }
+ }
+
+ /**
+ * Handles scroll with snap
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param snapParameters Snap parameters
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun rememberSnapHandler(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ snapParameters: SnapParameters = snapParametersDefault(),
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ return remember(rotaryScrollAdapter, snapParameters) {
+ if (isLowRes) {
+ LowResSnapHandler(
+ snapBehaviourFactory = {
+ DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ )
+ } else {
+ HighResSnapHandler(
+ resistanceFactor = snapParameters.resistanceFactor,
+ thresholdBehaviorFactory = {
+ ThresholdBehavior(
+ rotaryScrollAdapter,
+ snapParameters.thresholdDivider,
+ )
+ },
+ snapBehaviourFactory = {
+ DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ scrollBehaviourFactory = {
+ AnimationScrollBehavior(rotaryScrollAdapter.scrollableState)
+ },
+ )
+ }
+ }
+ }
+
+ /**
+ * Returns default [SnapParameters]
+ */
+ @ExperimentalHorologistApi
+ public fun snapParametersDefault(): SnapParameters =
+ SnapParameters(
+ snapOffset = 0,
+ thresholdDivider = 1.5f,
+ resistanceFactor = 3f,
+ )
+
+ /**
+ * Returns whether the input is Low-res (a bezel) or high-res(a crown/rsb).
+ */
+ @ExperimentalHorologistApi
+ @Composable
+ public fun isLowResInput(): Boolean = LocalContext.current.packageManager
+ .hasSystemFeature("android.hardware.rotaryencoder.lowres")
+
+ private val lowResFlingTimeframe: Long = 100L
+ private val highResFlingTimeframe: Long = 30L
+}
+
+/**
+ * Parameters used for snapping
+ *
+ * @param snapOffset an optional offset to be applied when snapping the item. After the snap the
+ * snapped items offset will be [snapOffset].
+ */
+public class SnapParameters(
+ public val snapOffset: Int,
+ public val thresholdDivider: Float,
+ public val resistanceFactor: Float,
+) {
+ /**
+ * Returns a snapping offset in [Dp]
+ */
+ @Composable
+ public fun snapOffsetDp(): Dp {
+ return with(LocalDensity.current) {
+ snapOffset.toDp()
+ }
+ }
+}
+
+/**
+ * An interface for handling scroll events
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollHandler {
+ /**
+ * Handles scrolling events
+ * @param coroutineScope A scope for performing async actions
+ * @param event A scrollable event from rotary input, containing scrollable delta and timestamp
+ * @param rotaryHaptics
+ */
+ @ExperimentalHorologistApi
+ public suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ )
+}
+
+/**
+ * An interface for scrolling behavior
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollBehavior {
+ /**
+ * Handles scroll event to [targetValue]
+ */
+ @ExperimentalHorologistApi
+ public suspend fun handleEvent(targetValue: Float)
+}
+
+/**
+ * Default implementation of [RotaryFlingBehavior]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryFlingBehavior(
+ private val scrollableState: ScrollableState,
+ private val flingBehavior: FlingBehavior,
+ viewConfiguration: ViewConfiguration,
+ private val flingTimeframe: Long,
+) : RotaryFlingBehavior {
+
+ // A time range during which the fling is valid.
+ // For simplicity it's twice as long as [flingTimeframe]
+ private val timeRangeToFling = flingTimeframe * 2
+
+ // A default fling factor for making fling slower
+ private val flingScaleFactor = 0.7f
+
+ private var previousVelocity = 0f
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private val minFlingSpeed = viewConfiguration.scaledMinimumFlingVelocity.toFloat()
+ private val maxFlingSpeed = viewConfiguration.scaledMaximumFlingVelocity.toFloat()
+ private var latestEventTimestamp: Long = 0
+
+ private var flingVelocity: Float = 0f
+ private var flingTimestamp: Long = 0
+
+ @ExperimentalHorologistApi
+ override fun startFlingTracking(timestamp: Long) {
+ rotaryVelocityTracker.start(timestamp)
+ latestEventTimestamp = timestamp
+ previousVelocity = 0f
+ }
+
+ @ExperimentalHorologistApi
+ override fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ latestEventTimestamp = timestamp
+ }
+
+ @ExperimentalHorologistApi
+ override suspend fun trackFling(beforeFling: () -> Unit) {
+ val currentVelocity = rotaryVelocityTracker.velocity
+ debugLog { "currentVelocity: $currentVelocity" }
+
+ if (abs(currentVelocity) >= abs(previousVelocity)) {
+ flingTimestamp = latestEventTimestamp
+ flingVelocity = currentVelocity * flingScaleFactor
+ }
+ previousVelocity = currentVelocity
+
+ // Waiting for a fixed amount of time before checking the fling
+ delay(flingTimeframe)
+
+ // For making a fling 2 criteria should be met:
+ // 1) no more than
+ // `rangeToFling` ms should pass between last fling detection
+ // and the time of last motion event
+ // 2) flingVelocity should exceed the minFlingSpeed
+ debugLog {
+ "Check fling: flingVelocity: $flingVelocity " +
+ "minFlingSpeed: $minFlingSpeed, maxFlingSpeed: $maxFlingSpeed"
+ }
+ if (latestEventTimestamp - flingTimestamp < timeRangeToFling &&
+ abs(flingVelocity) > minFlingSpeed
+ ) {
+ // Stops scrollAnimationCoroutine because a fling will be performed
+ beforeFling()
+ val velocity = flingVelocity.coerceIn(-maxFlingSpeed, maxFlingSpeed)
+ scrollableState.scroll(MutatePriority.UserInput) {
+ with(flingBehavior) {
+ debugLog { "Flinging with velocity $velocity" }
+ performFling(velocity)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * An interface for flinging with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryFlingBehavior {
+
+ /**
+ * Observing new event within a fling tracking session with new timestamp and delta
+ */
+ @ExperimentalHorologistApi
+ public fun observeEvent(timestamp: Long, delta: Float)
+
+ /**
+ * Performing fling if necessary and calling [beforeFling] lambda before it is triggered
+ */
+ @ExperimentalHorologistApi
+ public suspend fun trackFling(beforeFling: () -> Unit)
+
+ /**
+ * Starts a new fling tracking session
+ * with specified timestamp
+ */
+ @ExperimentalHorologistApi
+ public fun startFlingTracking(timestamp: Long)
+}
+
+/**
+ * An interface for snapping with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotarySnapBehavior {
+
+ /**
+ * Preparing snapping. This method should be called before [snapToTargetItem] is called.
+ *
+ * Snapping is done for current + [moveForElements] items.
+ *
+ * If [sequentialSnap] is true, items are summed up together.
+ * For example, if [prepareSnapForItems] is called with
+ * [moveForElements] = 2, 3, 5 -> then the snapping will happen to current + 10 items
+ *
+ * If [sequentialSnap] is false, then [moveForElements] are not summed up together.
+ */
+ public fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean)
+
+ /**
+ * Performs snapping to the closest item.
+ */
+ public suspend fun snapToClosestItem()
+
+ /**
+ * Returns true if top edge was reached
+ */
+ public fun topEdgeReached(): Boolean
+
+ /**
+ * Returns true if bottom edge was reached
+ */
+ public fun bottomEdgeReached(): Boolean
+
+ /**
+ * Performs snapping to the specified in [prepareSnapForItems] element
+ */
+ public suspend fun snapToTargetItem()
+}
+
+/**
+ * A rotary event object which contains a [timestamp] of the rotary event and a scrolled [delta].
+ */
+@ExperimentalHorologistApi
+public data class TimestampedDelta(val timestamp: Long, val delta: Float)
+
+/** Animation implementation of [RotaryScrollBehavior].
+ * This class does a smooth animation when the scroll by N pixels is done.
+ * This animation works well on Rsb(high-res) and Bezel(low-res) devices.
+ */
+@ExperimentalHorologistApi
+public class AnimationScrollBehavior(
+ private val scrollableState: ScrollableState,
+) : RotaryScrollBehavior {
+ private var sequentialAnimation = false
+ private var scrollAnimation = AnimationState(0f)
+ private var prevPosition = 0f
+
+ @ExperimentalHorologistApi
+ override suspend fun handleEvent(targetValue: Float) {
+ scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "ScrollAnimation value before start: ${scrollAnimation.value}" }
+
+ scrollAnimation.animateTo(
+ targetValue,
+ animationSpec = spring(),
+ sequentialAnimation = sequentialAnimation,
+ ) {
+ val delta = value - prevPosition
+ debugLog { "Animated by $delta, value: $value" }
+ scrollBy(delta)
+ prevPosition = value
+ sequentialAnimation = value != this.targetValue
+ }
+ }
+ }
+}
+
+/**
+ * An animated implementation of [RotarySnapBehavior]. Uses animateScrollToItem
+ * method for snapping to the Nth item
+ */
+@ExperimentalHorologistApi
+public class DefaultSnapBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val snapParameters: SnapParameters,
+) : RotarySnapBehavior {
+ private var snapTarget: Int = rotaryScrollAdapter.currentItemIndex()
+ private var sequentialSnap: Boolean = false
+
+ private var anim = AnimationState(0f)
+ private var expectedDistance = 0f
+
+ private val defaultStiffness = 200f
+ private var snapTargetUpdated = true
+
+ @ExperimentalHorologistApi
+ override fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean) {
+ this.sequentialSnap = sequentialSnap
+ if (sequentialSnap) {
+ snapTarget += moveForElements
+ } else {
+ snapTarget = rotaryScrollAdapter.currentItemIndex() + moveForElements
+ }
+ snapTargetUpdated = true
+ snapTarget = snapTarget.coerceIn(0 until rotaryScrollAdapter.totalItemsCount())
+ }
+
+ override suspend fun snapToClosestItem() {
+ // Snapping to the closest item by using performFling method with 0 speed
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "snap to closest item" }
+ var prevPosition = 0f
+ AnimationState(0f).animateTo(
+ targetValue = -rotaryScrollAdapter.currentItemOffset(),
+ animationSpec = tween(durationMillis = 100, easing = FastOutSlowInEasing),
+ ) {
+ val animDelta = value - prevPosition
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ snapTarget = rotaryScrollAdapter.currentItemIndex()
+ }
+ }
+
+ override fun topEdgeReached(): Boolean = snapTarget <= 0
+
+ override fun bottomEdgeReached(): Boolean =
+ snapTarget >= rotaryScrollAdapter.totalItemsCount() - 1
+
+ override suspend fun snapToTargetItem() {
+ if (sequentialSnap) {
+ anim = anim.copy(0f)
+ } else {
+ anim = AnimationState(0f)
+ }
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ // If snapTargetUpdated is true - then the target was updated so we
+ // need to do snap again
+ while (snapTargetUpdated) {
+ snapTargetUpdated = false
+ var latestCenterItem: Int
+ var continueFirstScroll = true
+ debugLog { "snapTarget $snapTarget" }
+ while (continueFirstScroll) {
+ latestCenterItem = rotaryScrollAdapter.currentItemIndex()
+ anim = anim.copy(0f)
+ expectedDistance = expectedDistanceTo(snapTarget, snapParameters.snapOffset)
+ debugLog {
+ "expectedDistance = $expectedDistance, " +
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ continueFirstScroll = false
+ var prevPosition = 0f
+
+ anim.animateTo(
+ expectedDistance,
+ animationSpec = SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ val animDelta = value - prevPosition
+ debugLog {
+ "First animation, value:$value, velocity:$velocity, " +
+ "animDelta:$animDelta"
+ }
+
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ scrollBy(animDelta)
+ prevPosition = value
+
+ if (latestCenterItem != rotaryScrollAdapter.currentItemIndex()) {
+ continueFirstScroll = true
+ cancelAnimation()
+ return@animateTo
+ }
+
+ debugLog { "centerItemIndex = ${rotaryScrollAdapter.currentItemIndex()}" }
+ if (rotaryScrollAdapter.currentItemIndex() == snapTarget) {
+ debugLog { "Target is visible. Cancelling first animation" }
+ debugLog {
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ expectedDistance = -rotaryScrollAdapter.currentItemOffset()
+ continueFirstScroll = false
+ cancelAnimation()
+ return@animateTo
+ }
+ }
+ }
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) continue
+
+ anim = anim.copy(0f)
+ var prevPosition = 0f
+ anim.animateTo(
+ expectedDistance,
+ animationSpec = SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ val animDelta = value - prevPosition
+ debugLog { "Final animation. velocity:$velocity, animDelta:$animDelta" }
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ }
+ }
+ }
+
+ private fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
+ val averageSize = rotaryScrollAdapter.averageItemSize()
+ val indexesDiff = index - rotaryScrollAdapter.currentItemIndex()
+ debugLog { "Average size $averageSize" }
+ return (averageSize * indexesDiff) +
+ targetScrollOffset - rotaryScrollAdapter.currentItemOffset()
+ }
+}
+
+/**
+ * A modifier which handles rotary events.
+ * It accepts ScrollHandler as the input - a class where main logic about how
+ * scroll should be handled is lying
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalComposeUiApi::class)
+public fun Modifier.rotaryHandler(
+ rotaryScrollHandler: RotaryScrollHandler,
+ // TODO: batching causes additional delays. Return once it's clear that
+ // we will use it
+ /* batchTimeframe: Long = 0L,*/
+ reverseDirection: Boolean,
+ rotaryHaptics: RotaryHapticHandler,
+): Modifier = composed {
+ val channel = rememberTimestampChannel()
+ val eventsFlow = remember(channel) { channel.receiveAsFlow() }
+
+ composed {
+ LaunchedEffect(eventsFlow) {
+ eventsFlow
+ // TODO: batching causes additional delays. Return once it's clear that
+ // we will use it
+ // Do we really need to do this on this level?
+// .batchRequestsWithinTimeframe(batchTimeframe)
+ .collectLatest {
+ debugLog {
+ "Scroll event received: " +
+ "delta:${it.delta}, timestamp:${it.timestamp}"
+ }
+ rotaryScrollHandler.handleScrollEvent(this, it, rotaryHaptics)
+ }
+ }
+ this
+ .onRotaryScrollEvent {
+ // Okay to ignore the ChannelResult returned from trySend because it is conflated
+ // (see rememberTimestampChannel()).
+ @Suppress("UNUSED_VARIABLE")
+ val unused = channel.trySend(
+ TimestampedDelta(
+ it.uptimeMillis,
+ it.verticalScrollPixels * if (reverseDirection) -1f else 1f,
+ ),
+ )
+ true
+ }
+ }
+}
+
+/**
+ * Batching requests for scrolling events. This function combines all events together
+ * (except first) within specified timeframe. Should help with performance on high-res devices.
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalCoroutinesApi::class)
+public fun Flow<TimestampedDelta>.batchRequestsWithinTimeframe(
+ timeframe: Long
+): Flow<TimestampedDelta> {
+ var delta = 0f
+ var lastTimestamp = -timeframe
+ return if (timeframe == 0L) {
+ this
+ } else {
+ this.transformLatest {
+ delta += it.delta
+ debugLog { "Batching requests. delta:$delta" }
+ if (lastTimestamp + timeframe <= it.timestamp) {
+ lastTimestamp = it.timestamp
+ debugLog { "No events before, delta= $delta" }
+ emit(TimestampedDelta(it.timestamp, delta))
+ } else {
+ delay(timeframe)
+ debugLog { "After delay, delta= $delta" }
+ if (delta > 0f) {
+ emit(TimestampedDelta(it.timestamp, delta))
+ }
+ }
+ delta = 0f
+ }
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) without snapping and with or without fling
+ * A list is scrolled by the number of pixels received from the rotary device.
+ *
+ * This class is a little bit different from LowResScrollHandler class - it has a filtering
+ * for events which are coming with wrong sign ( this happens to rsb devices,
+ * especially at the end of the scroll)
+ *
+ * This scroll handler supports fling. It can be set with [RotaryFlingBehavior].
+ */
+internal class HighResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+ private val hapticsThreshold: Long = 50,
+) : RotaryScrollHandler {
+
+ // This constant is specific for high-res devices. Because that input values
+ // can sometimes come with different sign, we have to filter them in this threshold
+ private val gestureThresholdTime = 200L
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+ val isOppositeScrollValue = isOppositeValueAfterScroll(event.delta)
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ // Due to the physics of Rotary side button, some events might come
+ // with an opposite axis value - either at the start or at the end of the motion.
+ // We don't want to use these values for fling calculations.
+ if (!isOppositeScrollValue) {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async {
+ scrollBehavior.handleEvent(rotaryScrollDistance)
+ }
+
+ if (rotaryFlingBehavior != null) {
+ flingJob.cancel()
+ flingJob = coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ })
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ sign(rotaryScrollDistance) * sign(delta) == -1f &&
+ (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for Bezel(low-res) without snapping.
+ * This scroll handler supports fling. It can be set with RotaryFlingBehavior.
+ */
+internal class LowResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+
+ private val gestureThresholdTime = 200L
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+ flingJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async {
+ scrollBehavior.handleEvent(rotaryScrollDistance)
+ }
+
+ flingJob = coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(
+ beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ },
+ )
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ debugLog { "Velocity tracker reset" }
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class HighResSnapHandler(
+ private val resistanceFactor: Float,
+ private val thresholdBehaviorFactory: () -> ThresholdBehavior,
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+ private val scrollBehaviourFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+ private val snapDelay = 100L
+ private val maxSnapsPerEvent = 2
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var rotaryScrollDistance = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+ private var scrollBehaviour = scrollBehaviourFactory()
+ private var thresholdBehavior = thresholdBehaviorFactory()
+
+ private val scrollEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.5f, 1.0f)
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ scrollBehaviour = scrollBehaviourFactory()
+ thresholdBehavior = thresholdBehaviorFactory()
+ thresholdBehavior.startThresholdTracking(time)
+ snapAccumulator = 0f
+ rotaryScrollDistance = 0f
+ }
+
+ if (!isOppositeValueAfterScroll(event.delta)) {
+ thresholdBehavior.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+
+ thresholdBehavior.applySmoothing()
+ val snapThreshold = thresholdBehavior.snapThreshold()
+
+ snapAccumulator += event.delta
+ if (!snapJob.isActive) {
+ val resistanceCoeff =
+ 1 - scrollEasing.transform(rotaryScrollDistance.absoluteValue / snapThreshold)
+ rotaryScrollDistance += event.delta * resistanceCoeff
+ }
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ debugLog { "snapThreshold: $snapThreshold" }
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > snapThreshold) {
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ scrollJob.cancel()
+
+ val snapDistance = (snapAccumulator / snapThreshold).toInt()
+ .coerceIn(-maxSnapsPerEvent..maxSnapsPerEvent)
+ snapAccumulator -= snapThreshold * snapDistance
+ val sequentialSnap = snapJob.isActive
+
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator remaining: $snapAccumulator"
+ }
+ if ((!snapBehaviour.topEdgeReached() && snapDistance < 0) ||
+ (!snapBehaviour.bottomEdgeReached() && snapDistance > 0)
+ ) {
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ rotaryScrollDistance = 0f
+ } else {
+ if (!snapJob.isActive) {
+ scrollJob.cancel()
+ debugLog { "Scrolling for $rotaryScrollDistance/$resistanceFactor px" }
+ scrollJob = coroutineScope.async {
+ scrollBehaviour.handleEvent(rotaryScrollDistance / resistanceFactor)
+ }
+ delay(snapDelay)
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ rotaryScrollDistance = 0f
+ snapAccumulator = 0f
+ snapBehaviour.prepareSnapForItems(0, false)
+
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ snapBehaviour.snapToClosestItem()
+ }
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ sign(rotaryScrollDistance) * sign(delta) == -1f &&
+ (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class LowResSnapHandler(
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ snapAccumulator = 0f
+ }
+
+ snapAccumulator += event.delta
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > 1f) {
+ scrollInProgress = false
+
+ val snapDistance = sign(snapAccumulator).toInt()
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ val sequentialSnap = snapJob.isActive
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator: $snapAccumulator"
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob = coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ snapAccumulator = 0f
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+internal class ThresholdBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val thresholdDivider: Float,
+ private val minVelocity: Float = 300f,
+ private val maxVelocity: Float = 3000f,
+ private val smoothingConstant: Float = 0.4f,
+) {
+ private val thresholdDividerEasing: Easing = CubicBezierEasing(0.5f, 0.0f, 0.5f, 1.0f)
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private var smoothedVelocity = 0f
+ fun startThresholdTracking(time: Long) {
+ rotaryVelocityTracker.start(time)
+ smoothedVelocity = 0f
+ }
+
+ fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ }
+
+ fun applySmoothing() {
+ if (rotaryVelocityTracker.velocity != 0.0f) {
+ // smooth the velocity
+ smoothedVelocity = exponentialSmoothing(
+ currentVelocity = rotaryVelocityTracker.velocity.absoluteValue,
+ prevVelocity = smoothedVelocity,
+ smoothingConstant = smoothingConstant,
+ )
+ }
+ debugLog { "rotaryVelocityTracker velocity: ${rotaryVelocityTracker.velocity}" }
+ debugLog { "SmoothedVelocity: $smoothedVelocity" }
+ }
+
+ fun snapThreshold(): Float {
+ val thresholdDividerFraction =
+ thresholdDividerEasing.transform(
+ inverseLerp(
+ minVelocity,
+ maxVelocity,
+ smoothedVelocity,
+ ),
+ )
+ return rotaryScrollAdapter.averageItemSize() / lerp(
+ 1f,
+ thresholdDivider,
+ thresholdDividerFraction,
+ )
+ }
+
+ private fun exponentialSmoothing(
+ currentVelocity: Float,
+ prevVelocity: Float,
+ smoothingConstant: Float,
+ ): Float =
+ smoothingConstant * currentVelocity + (1 - smoothingConstant) * prevVelocity
+}
+
+@Composable
+private fun rememberTimestampChannel() = remember {
+ Channel<TimestampedDelta>(capacity = Channel.CONFLATED)
+}
+
+private fun inverseLerp(start: Float, stop: Float, value: Float): Float {
+ return ((value - start) / (stop - start)).coerceIn(0f, 1f)
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
new file mode 100644
index 0000000..6e627c6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.rotaryinput
+
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+
+/**
+ * A wrapper around VelocityTracker1D to provide support for rotary input.
+ */
+public class RotaryVelocityTracker {
+ private var velocityTracker: VelocityTracker1D = VelocityTracker1D(true)
+
+ /**
+ * Retrieve the last computed velocity.
+ */
+ public val velocity: Float
+ get() = velocityTracker.calculateVelocity()
+
+ /**
+ * Start tracking motion.
+ */
+ public fun start(currentTime: Long) {
+ velocityTracker.resetTracking()
+ velocityTracker.addDataPoint(currentTime, 0f)
+ }
+
+ /**
+ * Continue tracking motion as the input rotates.
+ */
+ public fun move(currentTime: Long, delta: Float) {
+ velocityTracker.addDataPoint(currentTime, delta)
+ }
+
+ /**
+ * Stop tracking motion.
+ */
+ public fun end() {
+ velocityTracker.resetTracking()
+ }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/tools/WearPreview.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/tools/WearPreview.kt
new file mode 100644
index 0000000..0bfceee
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/tools/WearPreview.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.horologist.compose.tools
+
+import androidx.compose.ui.tooling.preview.Preview
+
+@Preview(
+ backgroundColor = 0xff000000,
+ showBackground = true,
+)
+public annotation class WearPreview
diff --git a/packages/CredentialManager/res/xml/autofill_service_configuration.xml b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
new file mode 100644
index 0000000..25cc094
--- /dev/null
+++ b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<autofill-service-configuration
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsInlineSuggestions="true"/>
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
new file mode 100644
index 0000000..38d98a9
--- /dev/null
+++ b/packages/CredentialManager/shared/Android.bp
@@ -0,0 +1,19 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "CredentialManagerShared",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.core_core-ktx",
+ "androidx.credentials_credentials",
+ "guava",
+ ],
+}
diff --git a/packages/CredentialManager/shared/AndroidManifest.xml b/packages/CredentialManager/shared/AndroidManifest.xml
new file mode 100644
index 0000000..a460887
--- /dev/null
+++ b/packages/CredentialManager/shared/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2023 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.credentialmanager">
+
+</manifest>
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
new file mode 100644
index 0000000..defba8d
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager
+
+import android.content.Intent
+import android.credentials.ui.RequestInfo
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.mapper.toGet
+import com.android.credentialmanager.mapper.toRequestCancel
+import com.android.credentialmanager.model.Request
+
+fun Intent.parse(): Request {
+ this.toRequestCancel()?.let { return it }
+
+ return when (requestInfo?.type) {
+ RequestInfo.TYPE_CREATE -> {
+ Request.Create
+ }
+
+ RequestInfo.TYPE_GET -> {
+ this.toGet()
+ }
+
+ else -> {
+ throw IllegalStateException("Unrecognized request type: ${requestInfo?.type}")
+ }
+ }
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/LogConstants.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/LogConstants.kt
new file mode 100644
index 0000000..44d33ff
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/LogConstants.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager
+
+const val TAG = "CredentialSelector"
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/factory/CredentialEntryFactory.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/factory/CredentialEntryFactory.kt
new file mode 100644
index 0000000..a2c1f03
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/factory/CredentialEntryFactory.kt
@@ -0,0 +1,25 @@
+package com.android.credentialmanager.factory
+
+import android.app.slice.Slice
+import android.credentials.Credential
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+
+fun fromSlice(slice: Slice): CredentialEntry? =
+ try {
+ when (slice.spec?.type) {
+ Credential.TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(slice)!!
+
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+ PublicKeyCredentialEntry.fromSlice(slice)!!
+
+ else -> CustomCredentialEntry.fromSlice(slice)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ CustomCredentialEntry.fromSlice(slice)
+ }
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
new file mode 100644
index 0000000..a4c20bf
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ktx
+
+import android.content.Intent
+import android.credentials.ui.CancelUiRequest
+import android.credentials.ui.CreateCredentialProviderData
+import android.credentials.ui.GetCredentialProviderData
+import android.credentials.ui.ProviderData
+import android.credentials.ui.RequestInfo
+
+val Intent.cancelUiRequest: CancelUiRequest?
+ get() = this.extras?.getParcelable(
+ CancelUiRequest.EXTRA_CANCEL_UI_REQUEST,
+ CancelUiRequest::class.java
+ )
+
+val Intent.requestInfo: RequestInfo?
+ get() = this.extras?.getParcelable(
+ RequestInfo.EXTRA_REQUEST_INFO,
+ RequestInfo::class.java
+ )
+
+val Intent.getCredentialProviderDataList: List<ProviderData>
+ get() = this.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ GetCredentialProviderData::class.java
+ ) ?: emptyList()
+
+val Intent.createCredentialProviderDataList: List<ProviderData>
+ get() = this.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ CreateCredentialProviderData::class.java
+ ) ?: emptyList()
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PackageManagerKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PackageManagerKtx.kt
new file mode 100644
index 0000000..5f4f298
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PackageManagerKtx.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ktx
+
+import android.content.pm.PackageManager
+import android.text.TextUtils
+import android.util.Log
+import com.android.credentialmanager.TAG
+
+fun PackageManager.appLabel(appPackageName: String): String? =
+ try {
+ val pkgInfo = this.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ applicationInfo.loadSafeLabel(
+ this, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ } catch (e: Exception) {
+ Log.e(TAG, "Caller app not found", e)
+ null
+ }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
new file mode 100644
index 0000000..86a6d23
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.mapper
+
+import android.content.Intent
+import com.android.credentialmanager.ktx.cancelUiRequest
+import com.android.credentialmanager.model.Request
+
+fun Intent.toRequestCancel(): Request.Cancel? =
+ this.cancelUiRequest?.let { cancelUiRequest ->
+ Request.Cancel(
+ showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
+ appPackageName = cancelUiRequest.appPackageName
+ )
+ }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
new file mode 100644
index 0000000..ed9d563
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -0,0 +1,22 @@
+package com.android.credentialmanager.mapper
+
+import android.content.Intent
+import android.credentials.ui.GetCredentialProviderData
+import com.android.credentialmanager.ktx.getCredentialProviderDataList
+import com.android.credentialmanager.model.Request
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableMap
+
+fun Intent.toGet() = Request.Get(
+ providers = ImmutableMap.copyOf(
+ getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
+ ),
+ entries = ImmutableList.copyOf(
+ getCredentialProviderDataList.map { providerData ->
+ check(providerData is GetCredentialProviderData) {
+ "Invalid provider data type for GetCredentialRequest"
+ }
+ providerData
+ }.flatMap { it.credentialEntries }
+ )
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
new file mode 100644
index 0000000..bc07310
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.model
+
+import android.credentials.ui.Entry
+import android.credentials.ui.ProviderData
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableMap
+
+/**
+ * Represents the request made by the CredentialManager API.
+ */
+sealed class Request {
+ data class Cancel(
+ val showCancellationUi: Boolean,
+ val appPackageName: String?
+ ) : Request()
+
+ data class Get(
+ val providers: ImmutableMap<String, ProviderData>,
+ val entries: ImmutableList<Entry>,
+ ) : Request()
+
+ data object Create : Request()
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 8361877..473d7b6f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -19,6 +19,7 @@
import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.credentials.ui.AuthenticationEntry
@@ -67,7 +68,7 @@
appPackageName: String
): String? {
return try {
- val pkgInfo = pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ val pkgInfo = getPackageInfo(pm, appPackageName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
applicationInfo.loadSafeLabel(
pm, 0f,
@@ -90,10 +91,7 @@
// Test data has only package name not component name.
// For test data usage only.
try {
- val pkgInfo = pm.getPackageInfo(
- providerFlattenedComponentName,
- PackageManager.PackageInfoFlags.of(0)
- )
+ val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -117,10 +115,7 @@
// Added for mdoc use case where the provider may not need to register a service and
// instead only relies on the registration api.
try {
- val pkgInfo = pm.getPackageInfo(
- component.packageName,
- PackageManager.PackageInfoFlags.of(0)
- )
+ val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -144,6 +139,19 @@
}
}
+private fun getPackageInfo(
+ pm: PackageManager,
+ packageName: String
+): PackageInfo {
+ val flags = PackageManager.MATCH_INSTANT
+
+ return pm.getPackageInfo(
+ packageName,
+ PackageManager.PackageInfoFlags.of(
+ (flags).toLong())
+ )
+}
+
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
companion object {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
new file mode 100644
index 0000000..943c2b4
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.credentialmanager.autofill
+
+import android.os.CancellationSignal
+import android.service.autofill.AutofillService
+import android.service.autofill.FillCallback
+import android.service.autofill.FillRequest
+import android.service.autofill.SaveRequest
+import android.service.autofill.SaveCallback
+
+class CredentialAutofillService : AutofillService() {
+ override fun onFillRequest(
+ request: FillRequest,
+ cancellationSignal: CancellationSignal,
+ callback: FillCallback
+ ) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
+ TODO("Not yet implemented")
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
new file mode 100644
index 0000000..c0dff16
--- /dev/null
+++ b/packages/CredentialManager/wear/Android.bp
@@ -0,0 +1,55 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "ClockworkCredentialManager",
+ defaults: ["platform_app_defaults"],
+ certificate: "platform",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.kt"],
+ resource_dirs: ["res"],
+
+ dex_preopt: {
+ profile_guided: true,
+ profile: "profile.txt.prof",
+ },
+
+ static_libs: [
+ "CredentialManagerShared",
+ "Horologist",
+ "PlatformComposeCore",
+ "androidx.activity_activity-compose",
+ "androidx.appcompat_appcompat",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.foundation_foundation-layout",
+ "androidx.compose.material_material-icons-core",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.compose.ui_ui-tooling",
+ "androidx.core_core-ktx",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-compose",
+ "androidx.wear.compose_compose-foundation",
+ "androidx.wear.compose_compose-material",
+ "androidx.wear.compose_compose-navigation",
+ "kotlinx-coroutines-core",
+ ],
+
+ platform_apis: true,
+ privileged: true,
+
+ kotlincflags: ["-Xjvm-default=all"],
+
+ optimize: {
+ proguard_compatibility: false,
+ },
+}
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 001a56d..90248734 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
- * Copyright (c) 2017 Google Inc.
+ * Copyright (c) 2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.credentialmanager">
+ <uses-feature android:name="android.hardware.type.watch" />
+
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
@@ -28,17 +30,15 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@style/Theme.CredentialSelector">
+ android:supportsRtl="true">
<activity
- android:name=".CredentialSelectorActivity"
+ android:name=".ui.CredentialSelectorActivity"
android:exported="true"
android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
android:launchMode="singleTop"
android:label="@string/app_name"
- android:excludeFromRecents="true"
- android:theme="@style/Theme.CredentialSelector">
+ android:excludeFromRecents="true">
</activity>
</application>
diff --git a/packages/CredentialManager/wear/res/drawable/passkey_icon.xml b/packages/CredentialManager/wear/res/drawable/passkey_icon.xml
new file mode 100644
index 0000000..be366bf
--- /dev/null
+++ b/packages/CredentialManager/wear/res/drawable/passkey_icon.xml
@@ -0,0 +1,21 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M23,10.5H17V13.5H23V10.5Z"
+ android:fillColor="#188038"/>
+ <path
+ android:pathData="M6.5,17.5C3.5,17.5 1,15 1,12C1,9 3.5,6.5 6.5,6.5C9.5,6.5 12,9 12,12C12,15 9.5,17.5 6.5,17.5ZM6.5,9.5C5.1,9.5 4,10.6 4,12C4,13.4 5.1,14.5 6.5,14.5C7.9,14.5 9,13.4 9,12C9,10.6 7.9,9.5 6.5,9.5Z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M21,13.5H19H17V16.5H19V15.5C19,14.9 19.4,14.5 20,14.5C20.6,14.5 21,14.9 21,15.5V16.5H23V13.5H21Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M11.8,10.5H8.5C8.8,10.9 9,11.4 9,12C9,12.6 8.8,13.1 8.5,13.5H11.8C11.9,13 12,12.5 12,12C12,11.5 11.9,11 11.8,10.5Z"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M17,10.5H11.8C11.9,11 12,11.5 12,12C12,12.5 11.9,13 11.8,13.5H17V10.5Z"
+ android:fillColor="#FBBC04"/>
+</vector>
diff --git a/packages/CredentialManager/wear/res/values/strings.xml b/packages/CredentialManager/wear/res/values/strings.xml
index 10ea918..109644f 100644
--- a/packages/CredentialManager/wear/res/values/strings.xml
+++ b/packages/CredentialManager/wear/res/values/strings.xml
@@ -18,4 +18,14 @@
<!-- The name of this application. Credential Manager is a service that centralizes and provides
access to a user's credentials used to sign in to various apps. [CHAR LIMIT=80] -->
<string name="app_name">Credential Manager</string>
+ <!-- Title of a screen prompting if the user would like to use their saved passkey.
+ [CHAR LIMIT=80] -->
+ <string name="use_passkey_title">Use passkey?</string>
+ <!-- Title of a screen prompting if the user would like to use their saved password.
+ [CHAR LIMIT=80] -->
+ <string name="use_password_title">Use password?</string>
+ <!-- Content description for the cancel button of a screen. [CHAR LIMIT=NONE] -->
+ <string name="dialog_cancel_button_cd">Cancel</string>
+ <!-- Content description for the OK button of a screen. [CHAR LIMIT=NONE] -->
+ <string name="dialog_ok_button_cd">OK</string>
</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
deleted file mode 100644
index f7b2499..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2022 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.0N
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.os.Bundle
-import androidx.activity.compose.setContent
-import androidx.activity.ComponentActivity
-import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
-
-class CredentialSelectorActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContent {
- MaterialTheme {
- Text("Credential Manager entry point")
- }
- }
- }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
new file mode 100644
index 0000000..53122ba
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.material.MaterialTheme
+import kotlinx.coroutines.launch
+
+class CredentialSelectorActivity : ComponentActivity() {
+
+ private val viewModel: CredentialSelectorViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setTheme(android.R.style.Theme_DeviceDefault)
+
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.uiState.collect { uiState ->
+ when (uiState) {
+ CredentialSelectorUiState.Idle -> {
+ // Don't display anything, assuming that there should be minimal latency
+ // to parse the Credential Manager intent and define the state of the
+ // app. If latency is big, then a "loading" screen should be displayed
+ // to the user.
+ }
+
+ is CredentialSelectorUiState.Get -> {
+ setContent {
+ MaterialTheme {
+ WearApp()
+ }
+ }
+ }
+
+ CredentialSelectorUiState.Create -> {
+ // TODO: b/301206624 - Implement create flow
+ finish()
+ }
+
+ is CredentialSelectorUiState.Cancel -> {
+ // TODO: b/300422310 - Implement cancel with message flow
+ finish()
+ }
+
+ CredentialSelectorUiState.Finish -> {
+ finish()
+ }
+ }
+ }
+ }
+ }
+
+ viewModel.onNewIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+
+ val previousIntent = getIntent()
+ setIntent(intent)
+
+ viewModel.onNewIntent(intent, previousIntent)
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
new file mode 100644
index 0000000..d22d5d1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui
+
+import android.app.Application
+import android.content.Intent
+import android.util.Log
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.parse
+import com.android.credentialmanager.ktx.appLabel
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.mapper.toGet
+import com.android.credentialmanager.ui.model.PasskeyUiModel
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.ui.mapper.toGet
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class CredentialSelectorViewModel(
+ private val application: Application
+) : AndroidViewModel(application = application) {
+
+ private val _uiState =
+ MutableStateFlow<CredentialSelectorUiState>(CredentialSelectorUiState.Idle)
+ val uiState: StateFlow<CredentialSelectorUiState> = _uiState
+
+ fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
+ viewModelScope.launch {
+ val request = intent.parse()
+ if (shouldFinishActivity(request = request, previousIntent = previousIntent)) {
+ _uiState.value = CredentialSelectorUiState.Finish
+ } else {
+ when (request) {
+ is Request.Cancel -> {
+ request.appPackageName?.let { appPackageName ->
+ application.packageManager.appLabel(appPackageName)?.let { appLabel ->
+ _uiState.value = CredentialSelectorUiState.Cancel(appLabel)
+ } ?: run {
+ Log.d(TAG,
+ "Received UI cancel request with an invalid package name.")
+ _uiState.value = CredentialSelectorUiState.Finish
+ }
+ } ?: run {
+ Log.d(TAG, "Received UI cancel request with an invalid package name.")
+ _uiState.value = CredentialSelectorUiState.Finish
+ }
+ }
+
+ Request.Create -> {
+ _uiState.value = CredentialSelectorUiState.Create
+ }
+
+ is Request.Get -> {
+ _uiState.value = request.toGet()
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if backend requested the UI activity to be cancelled. Different from the other
+ * finishing flows, this one does not report anything back to the Credential Manager service
+ * backend.
+ */
+ private fun shouldFinishActivity(request: Request, previousIntent: Intent? = null): Boolean {
+ if (request !is Request.Cancel) {
+ return false
+ } else {
+ Log.d(
+ TAG, "Received UI cancellation intent. Should show cancellation" +
+ " ui = ${request.showCancellationUi}")
+
+ previousIntent?.let {
+ val previousUiRequest = previousIntent.parse()
+
+ if (previousUiRequest is Request.Cancel) {
+ val previousToken = previousIntent.requestInfo?.token
+ val currentToken = previousIntent.requestInfo?.token
+
+ if (previousToken != currentToken) {
+ // Cancellation was for a different request, don't cancel the current UI.
+ return false
+ }
+ }
+ }
+
+ return !request.showCancellationUi
+ }
+ }
+}
+
+sealed class CredentialSelectorUiState {
+ data object Idle : CredentialSelectorUiState()
+ sealed class Get : CredentialSelectorUiState() {
+ data class SingleProviderSinglePasskey(val passkeyUiModel: PasskeyUiModel) : Get()
+ data class SingleProviderSinglePassword(val passwordUiModel: PasswordUiModel) : Get()
+
+ // TODO: b/301206470 add the remaining states
+ }
+
+ data object Create : CredentialSelectorUiState()
+ data class Cancel(val appName: String) : CredentialSelectorUiState()
+ data object Finish : CredentialSelectorUiState()
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
new file mode 100644
index 0000000..7d1a49b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui
+
+sealed class Screen(
+ val route: String,
+) {
+ data object Main : Screen("main")
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
new file mode 100644
index 0000000..19ea9ed
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui
+
+import androidx.compose.runtime.Composable
+import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.android.credentialmanager.ui.screens.MainScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.navscaffold.WearNavScaffold
+import com.google.android.horologist.compose.navscaffold.composable
+
+@Composable
+fun WearApp() {
+ val navController = rememberSwipeDismissableNavController()
+ val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
+ val navHostState =
+ rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+
+ WearNavScaffold(
+ startDestination = Screen.Main.route,
+ navController = navController,
+ state = navHostState,
+ ) {
+ composable(Screen.Main.route) {
+ MainScreen()
+ }
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt
new file mode 100644
index 0000000..c20ee0c
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun AccountRow(
+ name: String,
+ email: String,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ text = name,
+ color = Color(0xFFE6FF7B),
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ style = MaterialTheme.typography.title2
+ )
+ Text(
+ text = email,
+ modifier = Modifier.padding(top = 7.dp),
+ color = Color(0xFFCAC5BC),
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 2,
+ style = MaterialTheme.typography.body1,
+ )
+ }
+}
+
+@WearPreview
+@Composable
+fun AccountRowPreview() {
+ AccountRow(
+ name = "Elisa Beckett",
+ email = "beckett_bakery@gmail.com",
+ )
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/DialogButtonsRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/DialogButtonsRow.kt
new file mode 100644
index 0000000..5cb3c15
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/DialogButtonsRow.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.components
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.ButtonDefaults
+import com.google.android.horologist.compose.material.Button
+import com.google.android.horologist.compose.tools.WearPreview
+import com.android.credentialmanager.R
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+
+@OptIn(ExperimentalHorologistApi::class)
+@Composable
+fun DialogButtonsRow(
+ onCancelClick: () -> Unit,
+ onOKClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ cancelButtonIcon: ImageVector = Icons.Default.Close,
+ okButtonIcon: ImageVector = Icons.Default.Check,
+ cancelButtonContentDescription: String = stringResource(R.string.dialog_cancel_button_cd),
+ okButtonContentDescription: String = stringResource(R.string.dialog_ok_button_cd),
+) {
+ Row(
+ modifier = modifier,
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ Button(
+ imageVector = cancelButtonIcon,
+ contentDescription = cancelButtonContentDescription,
+ onClick = onCancelClick,
+ colors = ButtonDefaults.secondaryButtonColors(),
+ )
+ Button(
+ imageVector = okButtonIcon,
+ contentDescription = okButtonContentDescription,
+ onClick = onOKClick,
+ modifier = Modifier.padding(start = 20.dp)
+ )
+ }
+}
+
+@WearPreview
+@Composable
+fun DialogButtonsRowPreview() {
+ DialogButtonsRow(onCancelClick = {}, onOKClick = {})
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt
new file mode 100644
index 0000000..97900b7
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun PasswordRow(
+ email: String,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ text = email,
+ color = Color(0xFFE6FF7B),
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 2,
+ style = MaterialTheme.typography.title2
+ )
+ Text(
+ text = "••••••••••••••",
+ modifier = Modifier.padding(top = 7.dp),
+ color = Color(0xFFCAC5BC),
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ style = MaterialTheme.typography.body1,
+ )
+ }
+}
+
+@WearPreview
+@Composable
+fun PasswordRowPreview() {
+ PasswordRow(
+ email = "beckett_bakery@gmail.com",
+ )
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
new file mode 100644
index 0000000..956c56b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.components
+
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import com.android.credentialmanager.R
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.material.Icon
+import com.google.android.horologist.compose.material.util.DECORATIVE_ELEMENT_CONTENT_DESCRIPTION
+import com.google.android.horologist.compose.tools.WearPreview
+
+@OptIn(ExperimentalHorologistApi::class)
+@Composable
+fun SignInHeader(
+ @DrawableRes icon: Int,
+ title: String,
+ modifier: Modifier = Modifier,
+) {
+ SignInHeader(
+ iconContent = {
+ Icon(
+ id = icon,
+ contentDescription = DECORATIVE_ELEMENT_CONTENT_DESCRIPTION
+ )
+ },
+ title = title,
+ modifier = modifier,
+ )
+}
+
+@Composable
+fun SignInHeader(
+ iconContent: @Composable ColumnScope.() -> Unit,
+ title: String,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ iconContent()
+ Text(
+ text = title,
+ modifier = Modifier
+ .padding(top = 6.dp)
+ .padding(horizontal = 10.dp),
+ style = MaterialTheme.typography.title3
+ )
+ }
+}
+
+@WearPreview
+@Composable
+fun SignInHeaderPreview() {
+ SignInHeader(
+ icon = R.drawable.passkey_icon,
+ title = stringResource(R.string.use_passkey_title)
+ )
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
new file mode 100644
index 0000000..5ceec178
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
@@ -0,0 +1,51 @@
+package com.android.credentialmanager.ui.mapper
+
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import com.android.credentialmanager.ui.CredentialSelectorUiState
+import com.android.credentialmanager.factory.fromSlice
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import com.android.credentialmanager.model.Request
+
+fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+ if (this.providers.isEmpty()) {
+ throw IllegalStateException("Invalid GetCredential request with empty list of providers.")
+ }
+
+ if (this.entries.isEmpty()) {
+ throw IllegalStateException("Invalid GetCredential request with empty list of entries.")
+ }
+
+ if (this.providers.size == 1) {
+ if (this.entries.size == 1) {
+ val slice = this.entries.first().slice
+ when (val credentialEntry = fromSlice(slice)) {
+ is PasswordCredentialEntry -> {
+ return CredentialSelectorUiState.Get.SingleProviderSinglePassword(
+ PasswordUiModel(credentialEntry.displayName.toString())
+ )
+ }
+
+ is PublicKeyCredentialEntry -> {
+ TODO("b/301206470 - to be implemented")
+ }
+
+ is CustomCredentialEntry -> {
+ TODO("b/301206470 - to be implemented")
+ }
+
+ else -> {
+ throw IllegalStateException(
+ "Encountered unrecognized credential entry (${slice.spec?.type}) for " +
+ "GetCredential request with single account"
+ )
+ }
+ }
+ } else {
+ TODO("b/301206470 - to be implemented")
+ }
+ } else {
+ TODO("b/301206470 - to be implemented")
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt
new file mode 100644
index 0000000..a368de2
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasskeyUiModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.model
+
+data class PasskeyUiModel(
+ val name: String,
+ val email: String,
+)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasswordUiModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasswordUiModel.kt
new file mode 100644
index 0000000..52bbfaa
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/model/PasswordUiModel.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.model
+
+data class PasswordUiModel(val email: String)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
new file mode 100644
index 0000000..94a671e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material.Text
+
+@Composable
+fun MainScreen(
+ modifier: Modifier = Modifier
+) {
+ Box(modifier = modifier, contentAlignment = Alignment.Center) {
+ Text("This is a placeholder for the main screen.")
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
new file mode 100644
index 0000000..f344ad0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import com.android.credentialmanager.R
+import com.android.credentialmanager.ui.components.AccountRow
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumn
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SingleAccountScreen(
+ headerContent: @Composable () -> Unit,
+ accountContent: @Composable () -> Unit,
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ ScalingLazyColumn(
+ columnState = columnState,
+ modifier = modifier.fillMaxSize(),
+ ) {
+ item { headerContent() }
+ item { accountContent() }
+ content()
+ }
+}
+
+@WearPreview
+@Composable
+fun SingleAccountScreenPreview() {
+ SingleAccountScreen(
+ headerContent = {
+ SignInHeader(
+ icon = R.drawable.passkey_icon,
+ title = stringResource(R.string.use_passkey_title),
+ )
+ },
+ accountContent = {
+ AccountRow(
+ name = "Elisa Beckett",
+ email = "beckett_bakery@gmail.com",
+ modifier = Modifier.padding(top = 10.dp)
+ )
+ },
+ columnState = belowTimeTextPreview(),
+ ) {
+ item {
+ DialogButtonsRow(
+ onCancelClick = {},
+ onOKClick = {},
+ modifier = Modifier.padding(top = 10.dp)
+ )
+ }
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
new file mode 100644
index 0000000..c8f871e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.R
+import com.android.credentialmanager.ui.components.AccountRow
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SinglePasskeyScreen(
+ name: String,
+ email: String,
+ onCancelClick: () -> Unit,
+ onOKClick: () -> Unit,
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+) {
+ SingleAccountScreen(
+ headerContent = {
+ SignInHeader(
+ icon = R.drawable.passkey_icon,
+ title = stringResource(R.string.use_passkey_title),
+ )
+ },
+ accountContent = {
+ AccountRow(
+ name = name,
+ email = email,
+ modifier = Modifier.padding(top = 10.dp),
+ )
+ },
+ columnState = columnState,
+ modifier = modifier.padding(horizontal = 10.dp)
+ ) {
+ item {
+ DialogButtonsRow(
+ onCancelClick = onCancelClick,
+ onOKClick = onOKClick,
+ modifier = Modifier.padding(top = 10.dp)
+ )
+ }
+ }
+}
+
+@WearPreview
+@Composable
+fun SinglePasskeyScreenPreview() {
+ SinglePasskeyScreen(
+ name = "Elisa Beckett",
+ email = "beckett_bakery@gmail.com",
+ onCancelClick = {},
+ onOKClick = {},
+ columnState = belowTimeTextPreview(),
+ )
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
new file mode 100644
index 0000000..d863d3c
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.R
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.PasswordRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SinglePasswordScreen(
+ email: String,
+ onCancelClick: () -> Unit,
+ onOKClick: () -> Unit,
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+) {
+ SingleAccountScreen(
+ headerContent = {
+ SignInHeader(
+ icon = R.drawable.passkey_icon,
+ title = stringResource(R.string.use_password_title),
+ )
+ },
+ accountContent = {
+ PasswordRow(
+ email = email,
+ modifier = Modifier.padding(top = 10.dp),
+ )
+ },
+ columnState = columnState,
+ modifier = modifier.padding(horizontal = 10.dp)
+ ) {
+ item {
+ DialogButtonsRow(
+ onCancelClick = onCancelClick,
+ onOKClick = onOKClick,
+ modifier = Modifier.padding(top = 10.dp)
+ )
+ }
+ }
+}
+
+@WearPreview
+@Composable
+fun SinglePasswordScreenPreview() {
+ SinglePasswordScreen(
+ email = "beckett_bakery@gmail.com",
+ onCancelClick = {},
+ onOKClick = {},
+ columnState = belowTimeTextPreview(),
+ )
+}
+
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 5bd422b..a16f9f5 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -70,15 +70,18 @@
</intent-filter>
</activity>
+ <!-- NOTE: the workaround to fix the screen flash problem. Remember to check the problem
+ is resolved for new implementation -->
<activity android:name=".InstallStaging"
- android:exported="false" />
+ android:theme="@style/Theme.AlertDialogActivity.NoDim"
+ android:exported="false" />
<activity android:name=".DeleteStagedFileOnResult"
android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
- android:exported="false" />
+ android:exported="false" />
<activity android:name=".InstallInstalling"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index 9a06229..aa1fa16 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -32,4 +32,9 @@
<item name="android:windowNoTitle">true</item>
</style>
+ <style name="Theme.AlertDialogActivity.NoDim">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimAmount">0</item>
+ </style>
+
</resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 80e8761..d97fb54 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -306,6 +306,7 @@
}
private void initiateInstall() {
+ bindUi();
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
@@ -445,7 +446,6 @@
if (mAppSnippet != null) {
// load placeholder layout with OK button disabled until we override this layout in
// startInstallConfirm
- bindUi();
checkIfAllowedAndInitiateInstall();
}
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index bf4c858..6f94458 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibActionBarShadow",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/ActionBarShadow/AndroidManifest.xml b/packages/SettingsLib/ActionBarShadow/AndroidManifest.xml
index 98c4cb4..4ec6870 100644
--- a/packages/SettingsLib/ActionBarShadow/AndroidManifest.xml
+++ b/packages/SettingsLib/ActionBarShadow/AndroidManifest.xml
@@ -16,6 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.actionbarshadow">
</manifest>
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index b6e1677..1228555 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibActionButtonsPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
index 4b9f1ab..01ad598 100644
--- a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.actionbuttons">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 4d3ca945..3e65d94 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -32,10 +32,11 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.utils.BuildCompatUtils;
-
import java.util.ArrayList;
import java.util.List;
+import com.android.settingslib.widget.preference.actionbuttons.R;
+
/**
* This preference provides a four buttons layout with Settings style.
* It looks like below
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index 0cd9fe3..41de29a 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibActivityEmbedding",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
index 0949e1d..c67f51c 100644
--- a/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
+++ b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.activityembedding">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index 934aacf..df72a92 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibAdaptiveIcon",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
index 256b8f3..7709ad3 100644
--- a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
+++ b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.adaptiveicon">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
index 723caf2..fc41c93 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -32,6 +32,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.widget.adaptiveicon.R;
/**
* Adaptive icon that can set background color
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index e829974..12c234e 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -34,6 +34,8 @@
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import com.android.settingslib.widget.adaptiveicon.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp
index 0ba47a8..69b9d44 100644
--- a/packages/SettingsLib/AppPreference/Android.bp
+++ b/packages/SettingsLib/AppPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibAppPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/AppPreference/AndroidManifest.xml b/packages/SettingsLib/AppPreference/AndroidManifest.xml
index 4b9f1ab..9d01497 100644
--- a/packages/SettingsLib/AppPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/AppPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.app">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
index 60d00da..b1d31108 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
@@ -28,6 +28,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.app.R;
+
/**
* The Preference for the pages need to show big apps icon and name in the header of the page.
*/
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
index cfe7013..f1d162e 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
@@ -24,6 +24,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.app.R;
+
/**
* The Preference for the pages need to show apps icon.
*/
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
index 781bfcd..87bfc81 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -22,7 +22,7 @@
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
-
+import com.android.settingslib.widget.preference.app.R;
/**
* The SwitchPreference for the pages need to show apps icon.
*/
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 0f7a451..da91344 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibBannerMessagePreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml
index dd51ea3..a93a1fe 100644
--- a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml
+++ b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.banner">
<uses-sdk android:minSdkVersion="28"/>
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index afeb24a..33775a6 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -39,7 +39,7 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.utils.BuildCompatUtils;
-
+import com.android.settingslib.widget.preference.banner.R;
/**
* Banner message is a banner displaying important information (permission request, page error etc),
* and provide actions for user to address. It requires a user action to be dismissed.
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
index 5ca6fb6..ff4e79d 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
@@ -25,6 +25,8 @@
import androidx.annotation.Nullable;
+import com.android.settingslib.widget.preference.banner.R;
+
/**
* The view providing {@link BannerMessagePreference}.
*
@@ -75,7 +77,7 @@
int minimum =
getResources()
- .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target);
+ .getDimensionPixelSize(com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target);
int width = dismissButton.getWidth();
int height = dismissButton.getHeight();
int widthIncrease = width < minimum ? minimum - width : 0;
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
index 5c5da98..be1e0cf 100644
--- a/packages/SettingsLib/BarChartPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibBarChartPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/BarChartPreference/AndroidManifest.xml b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
index 4b9f1ab..fda4e1d 100644
--- a/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.barchart">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
index eeaf273..a32a24d 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
@@ -24,7 +24,7 @@
import java.util.ArrayList;
import java.util.List;
-
+import com.android.settingslib.widget.preference.barchart.R;
/**
* BarChartInfo is responsible for storing information about {@link BarChartPreference}.
*/
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index 20e0a3b..d63fc3c 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -28,7 +28,7 @@
import androidx.preference.PreferenceViewHolder;
import java.util.Arrays;
-
+import com.android.settingslib.widget.preference.barchart.R;
/**
* This BarChartPreference shows up to four bar views in this preference at most.
*
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index 93a34c0..7c74971 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -29,7 +29,7 @@
import androidx.annotation.ColorInt;
import androidx.annotation.VisibleForTesting;
-
+import com.android.settingslib.widget.preference.barchart.R;
/**
* {@link View} for a single vertical bar with icon and summary.
*/
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
index 922337a..4b6ca4a 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -23,7 +23,7 @@
import androidx.annotation.Nullable;
import java.util.Comparator;
-
+import com.android.settingslib.widget.preference.barchart.R;
/**
* A class responsible for saving bar view information.
*/
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
index 39f804f..35572fad 100644
--- a/packages/SettingsLib/ButtonPreference/Android.bp
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibButtonPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ButtonPreference/AndroidManifest.xml b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
index 2d35c331..f867707 100644
--- a/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.button">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 5364783..16ba962 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -30,6 +30,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.button.R;
+
/**
* A preference handled a button
*/
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index df43863..70f7554 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibCollapsingToolbarBaseActivity",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
index 51fc7ed..dabba68 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.collapsingtoolbar">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
index 3582897..8ebbac3 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
@@ -20,7 +20,6 @@
import androidx.preference.PreferenceFragmentCompat;
import com.android.settingslib.utils.BuildCompatUtils;
-import com.android.settingslib.widget.R;
import com.google.android.material.appbar.AppBarLayout;
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
index dcc6e5a..04c44e6 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -27,7 +27,6 @@
import androidx.appcompat.app.AppCompatActivity;
import com.android.settingslib.utils.BuildCompatUtils;
-import com.android.settingslib.widget.R;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -70,7 +69,7 @@
if (BuildCompatUtils.isAtLeastS()) {
DynamicColors.applyToActivityIfAvailable(this);
}
- setTheme(R.style.Theme_SubSettingsBase);
+ setTheme(com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase);
if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) {
super.setContentView(mCustomizeLayoutResId);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 19b7e85..143101f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -28,7 +28,6 @@
import androidx.fragment.app.FragmentActivity;
import com.android.settingslib.utils.BuildCompatUtils;
-import com.android.settingslib.widget.R;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 1c2288a..155cfbb 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -35,8 +35,6 @@
import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import com.android.settingslib.widget.R;
-
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index e4e34f8..f70add9 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -38,7 +38,7 @@
import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import com.android.settingslib.widget.R;
+import com.android.settingslib.collapsingtoolbar.R;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
diff --git a/packages/SettingsLib/DeviceStateRotationLock/Android.bp b/packages/SettingsLib/DeviceStateRotationLock/Android.bp
index 103309a..72df6cd 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/Android.bp
+++ b/packages/SettingsLib/DeviceStateRotationLock/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibDeviceStateRotationLock",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
diff --git a/packages/SettingsLib/DisplayUtils/Android.bp b/packages/SettingsLib/DisplayUtils/Android.bp
index 136f883..eab35a1 100644
--- a/packages/SettingsLib/DisplayUtils/Android.bp
+++ b/packages/SettingsLib/DisplayUtils/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibDisplayUtils",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
index 25b4905..986baf7 100644
--- a/packages/SettingsLib/EmergencyNumber/Android.bp
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibEmergencyNumber",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
static_libs: [
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
index bd83cdc..17b662c 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/Android.bp
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibEntityHeaderWidgets",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
index 4b9f1ab..ad4d22d 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
+++ b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.entityheader">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
index 6e95a0e..d802e5b 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -30,6 +30,8 @@
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
+import com.android.settingslib.widget.entityheader.R;
+
/**
* This class is used to initialize view which was inflated
* from {@link R.xml.app_entities_header.xml}.
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntityInfo.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntityInfo.java
index 1e55f2e..0439ff5 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntityInfo.java
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntityInfo.java
@@ -22,6 +22,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.settingslib.widget.entityheader.R;
+
/**
* AppEntityInfo is responsible for storing app information shown in {@link R.xml.app_view.xml}.
*/
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index 8b976bb..b45cd65 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibFooterPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/FooterPreference/AndroidManifest.xml b/packages/SettingsLib/FooterPreference/AndroidManifest.xml
index 96d9e51..9046b10 100644
--- a/packages/SettingsLib/FooterPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/FooterPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.footer">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index bf24c86..5b2fa1d 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -30,6 +30,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.footer.R;
+
/**
* A custom preference acting as "footer" of a page. It has a field for icon and text. It is added
* to screen as the last preference.
@@ -47,7 +49,7 @@
private FooterLearnMoreSpan mLearnMoreSpan;
public FooterPreference(Context context, AttributeSet attrs) {
- super(context, attrs, R.attr.footerPreferenceStyle);
+ super(context, attrs, com.android.settingslib.widget.theme.R.attr.footerPreferenceStyle);
init();
}
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/LinkTextView.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/LinkTextView.java
index 868fead..16d4475 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/LinkTextView.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/LinkTextView.java
@@ -22,6 +22,8 @@
import android.util.AttributeSet;
import android.widget.TextView;
+import com.android.settingslib.widget.preference.footer.R;
+
/**
* Copied from setup wizard. This TextView performed two functions. The first is to make it so the
* link behaves properly and becomes clickable. The second was that it made the link visible to
diff --git a/packages/SettingsLib/HelpUtils/Android.bp b/packages/SettingsLib/HelpUtils/Android.bp
index 13fcf8c..041fce2 100644
--- a/packages/SettingsLib/HelpUtils/Android.bp
+++ b/packages/SettingsLib/HelpUtils/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibHelpUtils",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/HelpUtils/AndroidManifest.xml b/packages/SettingsLib/HelpUtils/AndroidManifest.xml
index ccad6e4..35c7515 100644
--- a/packages/SettingsLib/HelpUtils/AndroidManifest.xml
+++ b/packages/SettingsLib/HelpUtils/AndroidManifest.xml
@@ -16,6 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.help">
</manifest>
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 70c8658..2c13695 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -36,7 +36,7 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.widget.R;
+import com.android.settingslib.widget.help.R;
import java.net.URISyntaxException;
import java.util.Locale;
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 24ccab2..4d4759b 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibIllustrationPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
index 73163fc..a0d10c3 100644
--- a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.illustration">
<uses-sdk android:minSdkVersion="28" />
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 37ae2d4..f4d4dba 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -42,6 +42,7 @@
import java.io.FileNotFoundException;
import java.io.InputStream;
+import com.android.settingslib.widget.preference.illustration.R;
/**
* IllustrationPreference is a preference that can play lottie format animation
@@ -427,10 +428,10 @@
mIsAutoScale = false;
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
- mImageResId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
+ com.airbnb.lottie.R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+ mImageResId = a.getResourceId(com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_rawRes, 0);
mCacheComposition = a.getBoolean(
- R.styleable.LottieAnimationView_lottie_cacheComposition, true);
+ com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_cacheComposition, true);
a = context.obtainStyledAttributes(attrs,
R.styleable.IllustrationPreference, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
index c29e1f7..53ded23 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibLayoutPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/LayoutPreference/AndroidManifest.xml b/packages/SettingsLib/LayoutPreference/AndroidManifest.xml
index 4b9f1ab..a8427a1 100644
--- a/packages/SettingsLib/LayoutPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/LayoutPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.layout">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
index 2a635b0..49f045f 100644
--- a/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
+++ b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -102,16 +102,16 @@
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
- mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
- R.styleable.Preference_allowDividerAbove, false);
- mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
- R.styleable.Preference_allowDividerBelow, false);
+ TypedArray a = context.obtainStyledAttributes(attrs, androidx.preference.R.styleable.Preference);
+ mAllowDividerAbove = TypedArrayUtils.getBoolean(a, androidx.preference.R.styleable.Preference_allowDividerAbove,
+ androidx.preference.R.styleable.Preference_allowDividerAbove, false);
+ mAllowDividerBelow = TypedArrayUtils.getBoolean(a, androidx.preference.R.styleable.Preference_allowDividerBelow,
+ androidx.preference.R.styleable.Preference_allowDividerBelow, false);
a.recycle();
a = context.obtainStyledAttributes(
- attrs, R.styleable.Preference, defStyleAttr, 0);
- int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+ attrs, androidx.preference.R.styleable.Preference, defStyleAttr, 0);
+ int layoutResource = a.getResourceId(androidx.preference.R.styleable.Preference_android_layout, 0);
if (layoutResource == 0) {
throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
}
@@ -124,7 +124,7 @@
}
private void setView(View view) {
- setLayoutResource(R.layout.layout_preference_frame);
+ setLayoutResource(com.android.settingslib.widget.preference.layout.R.layout.layout_preference_frame);
mRootView = view;
setShouldDisableView(false);
}
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 6e0d827..4b3acbf 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -16,6 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.mainswitch">
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index d1703c3..56b3eac 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -35,6 +35,8 @@
import java.util.ArrayList;
import java.util.List;
+import com.android.settingslib.widget.mainswitch.R;
+
/**
* MainSwitchBar is a View with a customized Switch.
* This component is used as the main switch of the page
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index 53cc268..11a6804 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -27,6 +27,8 @@
import java.util.ArrayList;
import java.util.List;
+import com.android.settingslib.widget.mainswitch.R;
+
/**
* MainSwitchPreference is a Preference with a customized Switch.
* This component is used as the main switch of the page
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java
index 1c610d9..03868f9 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java
@@ -18,6 +18,8 @@
import android.widget.Switch;
+import com.android.settingslib.widget.mainswitch.R;
+
/**
* Called when the checked state of the Switch has changed.
*/
diff --git a/packages/SettingsLib/ProfileSelector/Android.bp b/packages/SettingsLib/ProfileSelector/Android.bp
index 250cd75..155ed2e 100644
--- a/packages/SettingsLib/ProfileSelector/Android.bp
+++ b/packages/SettingsLib/ProfileSelector/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibProfileSelector",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ProfileSelector/AndroidManifest.xml b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml
index a57469e..80f6b76 100644
--- a/packages/SettingsLib/ProfileSelector/AndroidManifest.xml
+++ b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.profileselector">
<uses-sdk android:minSdkVersion="23" />
</manifest>
diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
index ac426ed..be5753be 100644
--- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
+++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
@@ -27,6 +27,7 @@
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
+import com.android.settingslib.widget.profileselector.R;
/**
* Base fragment class for profile settings.
diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java
index daf2564..f5ab647 100644
--- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java
+++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java
@@ -18,6 +18,7 @@
import androidx.fragment.app.Fragment;
import androidx.viewpager2.adapter.FragmentStateAdapter;
+import com.android.settingslib.widget.profileselector.R;
/**
* ViewPager Adapter to handle between TabLayout and ViewPager2
diff --git a/packages/SettingsLib/ProgressBar/Android.bp b/packages/SettingsLib/ProgressBar/Android.bp
index fb3c4e6..d876573 100644
--- a/packages/SettingsLib/ProgressBar/Android.bp
+++ b/packages/SettingsLib/ProgressBar/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibProgressBar",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ProgressBar/AndroidManifest.xml b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
index 256b8f3..fbd6eb0 100644
--- a/packages/SettingsLib/ProgressBar/AndroidManifest.xml
+++ b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.progressbar">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
index 1387daa..505ba05 100644
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ b/packages/SettingsLib/RadioButtonPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibRadioButtonPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml b/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml
index fda7fde..8b5c3b1 100644
--- a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.radio">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
index 02d3c06..fc4b714 100644
--- a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
+++ b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
@@ -25,6 +25,8 @@
import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.radio.R;
+
/**
* DEPRECATED. Please use SelectorWithWidgetPreference instead.
*
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index 6a8fef3..028489d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -15,6 +15,7 @@
android_library {
name: "SettingsLibRestrictedLockUtils",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/RestrictedLockUtils/AndroidManifest.xml b/packages/SettingsLib/RestrictedLockUtils/AndroidManifest.xml
index 0975640..50a55a7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/AndroidManifest.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/AndroidManifest.xml
@@ -16,6 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.restricted">
</manifest>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
index c4373bb..2d93e4e 100644
--- a/packages/SettingsLib/SchedulesProvider/Android.bp
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSchedulesProvider",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
index f96011a..c07a802 100644
--- a/packages/SettingsLib/SearchProvider/Android.bp
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSearchProvider",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/SearchProvider/AndroidManifest.xml b/packages/SettingsLib/SearchProvider/AndroidManifest.xml
index 2c06673..7e2c47f 100644
--- a/packages/SettingsLib/SearchProvider/AndroidManifest.xml
+++ b/packages/SettingsLib/SearchProvider/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.search">
+ package="com.android.settingslib.search.provider">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp
index 5aaee2a..ad9e167 100644
--- a/packages/SettingsLib/SearchWidget/Android.bp
+++ b/packages/SettingsLib/SearchWidget/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSearchWidget",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SearchWidget/AndroidManifest.xml b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
index 5798801..5982b02 100644
--- a/packages/SettingsLib/SearchWidget/AndroidManifest.xml
+++ b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
@@ -16,6 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.search">
+ package="com.android.settingslib.search.widget">
</manifest>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index b5a21bd..702387e 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSelectorWithWidgetPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml b/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml
index 51fc7ed..919d1a9 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.selector">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index 1d1316a..f2ce8a9 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -25,6 +25,8 @@
import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.selector.R;
+
/**
* Selector preference (checkbox or radio button) with an optional additional widget.
*
diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp
index cbb570e..0eec505 100644
--- a/packages/SettingsLib/SettingsSpinner/Android.bp
+++ b/packages/SettingsLib/SettingsSpinner/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSettingsSpinner",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsSpinner/AndroidManifest.xml b/packages/SettingsLib/SettingsSpinner/AndroidManifest.xml
index 4b9f1ab..123175c 100644
--- a/packages/SettingsLib/SettingsSpinner/AndroidManifest.xml
+++ b/packages/SettingsLib/SettingsSpinner/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.spinner">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
index 7288494..f33cacd 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-
+import com.android.settingslib.widget.spinner.R;
/**
* An ArrayAdapter which was used by Spinner with settings style.
* @param <T> the data type to be loaded.
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 6952875..63fe1b5 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -25,6 +25,7 @@
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.spinner.R;
/**
* This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index e6fb720..996477c 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSettingsTheme",
+ use_resource_processor: true,
resource_dirs: ["res"],
static_libs: ["androidx.preference_preference"],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/SettingsTheme/AndroidManifest.xml b/packages/SettingsLib/SettingsTheme/AndroidManifest.xml
index fda7fde..56d137a 100644
--- a/packages/SettingsLib/SettingsTheme/AndroidManifest.xml
+++ b/packages/SettingsLib/SettingsTheme/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.theme">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp
index 48cc75d..06493c0 100644
--- a/packages/SettingsLib/SettingsTransition/Android.bp
+++ b/packages/SettingsLib/SettingsTransition/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibSettingsTransition",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/SettingsTransition/AndroidManifest.xml b/packages/SettingsLib/SettingsTransition/AndroidManifest.xml
index 244b367..3e64d56 100644
--- a/packages/SettingsLib/SettingsTransition/AndroidManifest.xml
+++ b/packages/SettingsLib/SettingsTransition/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.transition">
<uses-sdk android:minSdkVersion="29" />
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 471f3b9..01596d2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -32,6 +32,7 @@
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsTextFieldPasswordPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
@@ -44,6 +45,7 @@
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -92,6 +94,8 @@
SettingsOutlinedTextFieldPageProvider,
SettingsExposedDropdownMenuBoxPageProvider,
SettingsExposedDropdownMenuCheckBoxProvider,
+ SettingsTextFieldPasswordPageProvider,
+ SearchScaffoldPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index b74af21..4875ea9 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -39,6 +39,8 @@
.build(),
SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
+ SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ .build(),
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
index 6d22e6a..5ffbe8ba 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
@@ -19,7 +19,7 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
@@ -45,13 +45,13 @@
@Composable
override fun Page(arguments: Bundle?) {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(-1) }
val options = listOf("item1", "item2", "item3")
RegularScaffold(title = TITLE) {
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
onselectedOptionTextChange = { selectedItem = it })
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
index 37c8eef..d289646 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
@@ -37,7 +37,7 @@
override val name = "SettingsExposedDropdownMenuCheckBox"
private const val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
private val options = listOf("item1", "item2", "item3")
- private val selectedOptionsState1 = mutableStateListOf("item1", "item2")
+ private val selectedOptionsState1 = mutableStateListOf(0, 1)
override fun getTitle(arguments: Bundle?): String {
return TITLE
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
new file mode 100644
index 0000000..fc51466e
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.settingslib.spa.gallery.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsTextFieldPassword"
+
+object SettingsTextFieldPasswordPageProvider : SettingsPageProvider {
+ override val name = "SettingsTextFieldPassword"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ var value by remember { mutableStateOf("value") }
+ RegularScaffold(title = TITLE) {
+ SettingsTextFieldPassword(
+ value = value,
+ label = "label",
+ onTextChange = { value = it })
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsTextFieldPasswordPagePreview() {
+ SettingsTheme {
+ SettingsTextFieldPasswordPageProvider.Page(null)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6cac220..b339b44 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -41,6 +41,7 @@
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
@@ -55,6 +56,7 @@
PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
+ SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
new file mode 100644
index 0000000..a1ab35b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.SearchScaffold
+import com.android.settingslib.spa.widget.ui.PlaceholderTitle
+
+private const val TITLE = "Sample SearchScaffold"
+
+object SearchScaffoldPageProvider : SettingsPageProvider {
+ override val name = "SearchScaffold"
+
+ private val owner = createSettingsPage()
+
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ Page()
+ }
+}
+
+@Composable
+private fun Page() {
+ SearchScaffold(title = TITLE) { bottomPadding, searchQuery ->
+ PlaceholderTitle("Search query: ${searchQuery.value}")
+ }
+}
diff --git a/packages/SettingsLib/Spa/screenshot/Android.bp b/packages/SettingsLib/Spa/screenshot/Android.bp
index 4e6b646..bd508cb 100644
--- a/packages/SettingsLib/Spa/screenshot/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/Android.bp
@@ -20,6 +20,7 @@
android_test {
name: "SpaScreenshotTests",
+ use_resource_processor: true,
test_suites: ["device-tests"],
asset_dirs: ["assets"],
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 08e3a27..9f8c868 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,6 +22,7 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -126,18 +127,22 @@
allProvider: Collection<SettingsPageProvider>,
content: @Composable (SettingsPage) -> Unit,
) {
- NavHost(
- navController = navController,
- startDestination = NullPageProvider.name,
- ) {
- composable(NullPageProvider.name) {}
- for (spp in allProvider) {
- animatedComposable(
- route = spp.name + spp.parameter.navRoute(),
- arguments = spp.parameter,
- ) { navBackStackEntry ->
- val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
- content(page)
+ // TODO(b/298520326): Remove Box after the issue is fixed.
+ // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
+ Box {
+ NavHost(
+ navController = navController,
+ startDestination = NullPageProvider.name,
+ ) {
+ composable(NullPageProvider.name) {}
+ for (spp in allProvider) {
+ animatedComposable(
+ route = spp.name + spp.parameter.navRoute(),
+ arguments = spp.parameter,
+ ) { navBackStackEntry ->
+ val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+ content(page)
+ }
}
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
index ec43aab..0d6c064 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -41,9 +41,9 @@
fun SettingsExposedDropdownMenuBox(
label: String,
options: List<String>,
- selectedOptionText: String,
+ selectedOptionIndex: Int,
enabled: Boolean,
- onselectedOptionTextChange: (String) -> Unit,
+ onselectedOptionTextChange: (Int) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
@@ -58,8 +58,8 @@
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
- value = selectedOptionText,
- onValueChange = onselectedOptionTextChange,
+ value = options.getOrElse(selectedOptionIndex) { "" },
+ onValueChange = { },
label = { Text(text = label) },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
@@ -81,7 +81,7 @@
DropdownMenuItem(
text = { Text(option) },
onClick = {
- onselectedOptionTextChange(option)
+ onselectedOptionTextChange(options.indexOf(option))
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
@@ -103,7 +103,7 @@
SettingsExposedDropdownMenuBox(
label = "ExposedDropdownMenuBoxLabel",
options = options,
- selectedOptionText = item1,
+ selectedOptionIndex = 0,
enabled = true,
onselectedOptionTextChange = {})
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
index 682b4ea..5d248e1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -31,6 +31,7 @@
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -50,11 +51,12 @@
fun SettingsExposedDropdownMenuCheckBox(
label: String,
options: List<String>,
- selectedOptionsState: SnapshotStateList<String>,
+ selectedOptionsState: SnapshotStateList<Int>,
+ emptyVal: String = "",
enabled: Boolean,
onSelectedOptionStateChange: () -> Unit,
) {
- var dropDownWidth by remember { mutableStateOf(0) }
+ var dropDownWidth by remember { mutableIntStateOf(0) }
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
@@ -69,7 +71,8 @@
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
- value = selectedOptionsState.joinToString(", "),
+ value = if (selectedOptionsState.size == 0) emptyVal
+ else selectedOptionsState.joinToString { options[it] },
onValueChange = {},
label = { Text(text = label) },
trailingIcon = {
@@ -88,23 +91,23 @@
.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
onDismissRequest = { expanded = false },
) {
- options.forEach { option ->
+ options.forEachIndexed { index, option ->
TextButton(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(),
onClick = {
- if (selectedOptionsState.contains(option)) {
+ if (selectedOptionsState.contains(index)) {
selectedOptionsState.remove(
- option
+ index
)
} else {
selectedOptionsState.add(
- option
+ index
)
}
onSelectedOptionStateChange()
- }) {
+ }) {
Row(
modifier = Modifier
.fillMaxHeight()
@@ -113,7 +116,7 @@
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
- checked = selectedOptionsState.contains(option),
+ checked = selectedOptionsState.contains(index),
onCheckedChange = null,
)
Text(text = option)
@@ -129,9 +132,10 @@
@Composable
private fun ActionButtonsPreview() {
val options = listOf("item1", "item2", "item3")
- val selectedOptionsState = remember { mutableStateListOf("item1", "item2") }
+ val selectedOptionsState = remember { mutableStateListOf(0, 1) }
SettingsTheme {
- SettingsExposedDropdownMenuCheckBox(label = "label",
+ SettingsExposedDropdownMenuCheckBox(
+ label = "label",
options = options,
selectedOptionsState = selectedOptionsState,
enabled = true,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
new file mode 100644
index 0000000..d0a6188
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.Icon
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun SettingsTextFieldPassword(
+ value: String,
+ label: String,
+ onTextChange: (String) -> Unit,
+) {
+ var visibility by remember { mutableStateOf(false) }
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(SettingsDimension.itemPadding)
+ .fillMaxWidth(),
+ value = value,
+ onValueChange = onTextChange,
+ label = { Text(text = label) },
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Password,
+ imeAction = ImeAction.Send
+ ),
+ trailingIcon = {
+ Icon(
+ imageVector = if (visibility) Icons.Outlined.VisibilityOff
+ else Icons.Outlined.Visibility,
+ contentDescription = "Visibility Icon",
+ modifier = Modifier
+ .testTag("Visibility Icon")
+ .size(SettingsDimension.itemIconSize)
+ .toggleable(visibility) {
+ visibility = !visibility
+ },
+ )
+ },
+ visualTransformation = if (visibility) VisualTransformation.None
+ else PasswordVisualTransformation()
+ )
+}
+
+@Preview
+@Composable
+private fun SettingsTextFieldPasswordPreview() {
+ var value by remember { mutableStateOf("value") }
+ SettingsTheme {
+ SettingsTextFieldPassword(
+ value = value,
+ label = "label",
+ onTextChange = { value = it },
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index b77368a..3a0e51b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -32,8 +32,8 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -49,10 +49,8 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
@@ -73,7 +71,6 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.compose.horizontalValues
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
import kotlin.math.abs
@@ -129,16 +126,10 @@
private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) {
Text(
text = title,
- modifier = Modifier
- .padding(
- WindowInsets.navigationBars
- .asPaddingValues()
- .horizontalValues()
- )
- .padding(
- start = SettingsDimension.itemPaddingAround,
- end = SettingsDimension.itemPaddingEnd,
- ),
+ modifier = Modifier.padding(
+ start = SettingsDimension.itemPaddingAround,
+ end = SettingsDimension.itemPaddingEnd,
+ ),
overflow = TextOverflow.Ellipsis,
maxLines = maxLines,
)
@@ -157,6 +148,15 @@
* Represents the colors used by a top app bar in different states.
* This implementation animates the container color according to the top app bar scroll state. It
* does not animate the leading, headline, or trailing colors.
+ *
+ * @constructor create an instance with arbitrary colors, see [TopAppBarColors] for a
+ * factory method using the default material3 spec
+ * @param containerColor the color used for the background of this BottomAppBar. Use
+ * [Color.Transparent] to have no color.
+ * @param scrolledContainerColor the container color when content is scrolled behind it
+ * @param navigationIconContentColor the content color used for the navigation icon
+ * @param titleContentColor the content color used for the title
+ * @param actionIconContentColor the content color used for actions
*/
@Stable
private class TopAppBarColors(
@@ -248,7 +248,6 @@
titleTextStyle = titleTextStyle,
titleAlpha = 1f,
titleVerticalArrangement = Arrangement.Center,
- titleHorizontalArrangement = Arrangement.Start,
titleBottomPadding = 0,
hideTitleSemantics = false,
navigationIcon = navigationIcon,
@@ -312,7 +311,7 @@
// This will potentially animate or interpolate a transition between the container color and the
// container's scrolled color according to the app bar's scroll state.
val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
- val appBarContainerColor by rememberUpdatedState(colors.containerColor(colorTransitionFraction))
+ val appBarContainerColor = colors.containerColor(colorTransitionFraction)
// Wrap the given actions in a Row.
val actionsRow = @Composable {
@@ -364,14 +363,17 @@
titleTextStyle = smallTitleTextStyle,
titleAlpha = topTitleAlpha,
titleVerticalArrangement = Arrangement.Center,
- titleHorizontalArrangement = Arrangement.Start,
titleBottomPadding = 0,
hideTitleSemantics = hideTopRowSemantics,
navigationIcon = navigationIcon,
actions = actionsRow,
)
TopAppBarLayout(
- modifier = Modifier.clipToBounds(),
+ modifier = Modifier
+ // only apply the horizontal sides of the window insets padding, since the top
+ // padding will always be applied by the layout above
+ .windowInsetsPadding(windowInsets.only(WindowInsetsSides.Horizontal))
+ .clipToBounds(),
heightPx = maxHeightPx.floatValue - pinnedHeightPx +
(scrollBehavior?.state?.heightOffset ?: 0f),
navigationIconContentColor = colors.navigationIconContentColor,
@@ -392,7 +394,6 @@
titleTextStyle = titleTextStyle,
titleAlpha = bottomTitleAlpha,
titleVerticalArrangement = Arrangement.Bottom,
- titleHorizontalArrangement = Arrangement.Start,
titleBottomPadding = titleBottomPaddingPx,
hideTitleSemantics = hideBottomRowSemantics,
navigationIcon = {},
@@ -419,7 +420,6 @@
* @param modifier a [Modifier]
* @param titleAlpha the title's alpha
* @param titleVerticalArrangement the title's vertical arrangement
- * @param titleHorizontalArrangement the title's horizontal arrangement
* @param titleBottomPadding the title's bottom padding
* @param hideTitleSemantics hides the title node from the semantic tree. Apply this
* boolean when this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics
@@ -440,7 +440,6 @@
titleTextStyle: TextStyle,
titleAlpha: Float,
titleVerticalArrangement: Arrangement.Vertical,
- titleHorizontalArrangement: Arrangement.Horizontal,
titleBottomPadding: Int,
hideTitleSemantics: Boolean,
navigationIcon: @Composable () -> Unit,
@@ -470,10 +469,10 @@
CompositionLocalProvider(
LocalContentColor provides titleContentColor,
LocalDensity provides with(LocalDensity.current) {
- Density(
- density = density,
- fontScale = if (titleScaleDisabled) 1f else fontScale,
- )
+ Density(
+ density = density,
+ fontScale = if (titleScaleDisabled) 1f else fontScale,
+ )
},
content = title
)
@@ -528,15 +527,7 @@
// Title
titlePlaceable.placeRelative(
- x = when (titleHorizontalArrangement) {
- Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
- Arrangement.End ->
- constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width
- // Arrangement.Start.
- // A TopAppBarTitleInset will make sure the title is offset in case the
- // navigation icon is missing.
- else -> max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width)
- },
+ x = max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width),
y = when (titleVerticalArrangement) {
Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
// Apply bottom padding from the title's baseline only when the Arrangement is
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
index 09f5945..bc67e4c 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.widget.editor
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -40,13 +41,13 @@
@Test
fun exposedDropdownMenuBoxs_displayed() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
.assertIsDisplayed()
@@ -55,13 +56,13 @@
@Test
fun exposedDropdownMenuBoxs_expanded() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(item2, substring = true)
.assertDoesNotExist()
@@ -74,13 +75,13 @@
@Test
fun exposedDropdownMenuBoxs_valueChanged() {
composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf("item1") }
+ var selectedItem by remember { mutableIntStateOf(0) }
SettingsExposedDropdownMenuBox(
label = exposedDropdownMenuBoxLabel,
options = options,
- selectedOptionText = selectedItem,
+ selectedOptionIndex = selectedItem,
enabled = true,
- onselectedOptionTextChange = {selectedItem = it})
+ onselectedOptionTextChange = { selectedItem = it })
}
composeTestRule.onNodeWithText(item2, substring = true)
.assertDoesNotExist()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
index b0271ae1..2b78ed7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
@@ -39,7 +39,7 @@
private val item2 = "item2"
private val item3 = "item3"
private val options = listOf(item1, item2, item3)
- private val selectedOptionsState1 = mutableStateListOf(item1, item2)
+ private val selectedOptionsState1 = mutableStateListOf(0, 1)
private val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
@Test
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
new file mode 100644
index 0000000..6f2d1f9
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertTextContains
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTextReplacement
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsTextFieldPasswordTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+ private val label = "label"
+ private val value = "value"
+ private val valueChanged = "Value Changed"
+ private val visibilityIconTag = "Visibility Icon"
+
+ @Test
+ fun textFieldPassword_displayed() {
+ composeTestRule.setContent {
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = {})
+ }
+ composeTestRule.onNodeWithText(label, substring = true)
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun textFieldPassword_invisible() {
+ composeTestRule.setContent {
+ var value by remember { mutableStateOf(value) }
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = { value = it })
+ }
+ composeTestRule.onNodeWithText(value, substring = true)
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun textFieldPassword_visible_inputValue() {
+ composeTestRule.setContent {
+ var value by remember { mutableStateOf(value) }
+ SettingsTextFieldPassword(
+ value = value,
+ label = label,
+ onTextChange = { value = it })
+ }
+ composeTestRule.onNodeWithTag(visibilityIconTag)
+ .performClick()
+ composeTestRule.onNodeWithText(label, substring = true)
+ .performTextReplacement(valueChanged)
+ composeTestRule.onNodeWithText(label, substring = true)
+ .assertTextContains(valueChanged)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
index a6a5ed22..3dac7db 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -307,8 +306,8 @@
}
/**
- * Checks the app bar's components positioning when it's a [CustomizedTopAppBar], a
- * [CenterAlignedTopAppBar], or a larger app bar that is scrolled up and collapsed into a small
+ * Checks the app bar's components positioning when it's a [CustomizedTopAppBar]
+ * or a larger app bar that is scrolled up and collapsed into a small
* configuration and there is no navigation icon.
*/
private fun assertSmallPositioningWithoutNavigation(isCenteredTitle: Boolean = false) {
@@ -335,8 +334,7 @@
}
/**
- * Checks the app bar's components positioning when it's a [CustomizedTopAppBar] or a
- * [CenterAlignedTopAppBar].
+ * Checks the app bar's components positioning when it's a [CustomizedTopAppBar].
*/
private fun assertSmallDefaultPositioning(isCenteredTitle: Boolean = false) {
val appBarBounds = rule.onNodeWithTag(TopAppBarTestTag).getUnclippedBoundsInRoot()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index a370ebf..f54de15 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -26,10 +26,10 @@
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
-import com.android.settingslib.widget.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
+import com.android.settingslib.widget.restricted.R
data class Restrictions(
val userId: Int,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index ea83e1d..fc10a27 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -72,7 +72,7 @@
private fun InstallType(app: ApplicationInfo) {
if (!app.isInstantApp) return
Spacer(modifier = Modifier.height(4.dp))
- SettingsBody(stringResource(com.android.settingslib.widget.R.string.install_type_instant))
+ SettingsBody(stringResource(com.android.settingslib.widget.preference.app.R.string.install_type_instant))
}
@Composable
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
index cc570cc..eb9e329 100644
--- a/packages/SettingsLib/Tile/Android.bp
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibTile",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 5d09a1a..77b7ac1 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibTopIntroPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/TopIntroPreference/AndroidManifest.xml b/packages/SettingsLib/TopIntroPreference/AndroidManifest.xml
index 96d9e51..6cf643f 100644
--- a/packages/SettingsLib/TopIntroPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/TopIntroPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.topintro">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
index 5a5a9b8..1bbd76d 100644
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
@@ -22,6 +22,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.topintro.R;
+
/**
* The TopIntroPreference shows a text which describe a feature. Gernerally, we expect this
* preference always shows on the top of screen.
diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp
index a3e50a9..5aa906e 100644
--- a/packages/SettingsLib/TwoTargetPreference/Android.bp
+++ b/packages/SettingsLib/TwoTargetPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibTwoTargetPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml
index 120b085..15e9368 100644
--- a/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.twotarget">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index 9130662..b125f71 100644
--- a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -26,6 +26,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.twotarget.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index ad6e7ab..4cc90cc 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibUsageProgressBarPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml
index 51fc7ed..1e9df9c 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.widget.preference.usage">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 4eedab2..712f6f0 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -31,6 +31,8 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.usage.R;
+
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index dc2b52d..c7ad24c 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibUtils",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8dc95f4..f9ea773 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1254,7 +1254,6 @@
<!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
<string name="wifi_tether_connected_summary">
{count, plural,
- =0 {0 device connected}
=1 {1 device connected}
other {# devices connected}
}
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 918d696..202e7fe 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -16,6 +16,7 @@
android_library {
name: "SettingsLib-search",
+ use_resource_processor: true,
static_libs: [
"SettingsLib-search-interface",
],
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index b1d1ea5e..758f090 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -126,10 +126,10 @@
switchSummary = isChecked()
? getUpdatableEnterpriseString(
getContext(), ENABLED_BY_ADMIN_SWITCH_SUMMARY,
- com.android.settingslib.widget.R.string.enabled_by_admin)
+ com.android.settingslib.widget.restricted.R.string.enabled_by_admin)
: getUpdatableEnterpriseString(
getContext(), DISABLED_BY_ADMIN_SWITCH_SUMMARY,
- com.android.settingslib.widget.R.string.disabled_by_admin);
+ com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else {
switchSummary = mRestrictedSwitchSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 91b852a..70126ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -198,19 +198,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
boolean isA2dpPlaying() {
if (mService == null) return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index c7a5bd8..38cd04e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -140,19 +140,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
boolean isAudioPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
index f5257b0..8c0dc41 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastDialog.java
@@ -47,7 +47,7 @@
final Window window = getWindow();
window.setContentView(layout);
window.setWindowAnimations(
- com.android.settingslib.widget.R.style.Theme_AlertDialog_SettingsLib);
+ com.android.settingslib.widget.theme.R.style.Theme_AlertDialog_SettingsLib);
TextView title = layout.findViewById(R.id.dialog_title);
TextView subTitle = layout.findViewById(R.id.dialog_subtitle);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
index c3f845c..45cafc6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -178,19 +178,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 6b7f733..660090d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -258,19 +258,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 7e5c124..b79f147 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -165,19 +165,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 5fbb4c3..14fab16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -231,19 +231,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 66225a2..b0e0049 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -150,19 +150,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8a2c4f8..5468efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -123,13 +123,13 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
// if set preferred to false, then disconnect to the current device
if (!enabled) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 3c24b4a..5b91ac9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -126,19 +126,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index e781f13..a93524f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -24,24 +24,18 @@
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothCodecConfig;
-import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.os.Build;
-import android.os.ParcelUuid;
import android.util.Log;
import androidx.annotation.RequiresApi;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
public class LeAudioProfile implements LocalBluetoothProfile {
@@ -233,19 +227,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null || device == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 4881a86..0a803bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -137,19 +137,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 75c1926..db1ba5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -138,19 +138,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 767df35..dec862b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -105,7 +105,7 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
@@ -117,12 +117,12 @@
mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN);
}
}
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 0d11293..65b89ba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -151,19 +151,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 9e2e4a1..784b821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -108,16 +108,16 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (!enabled) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 104f1d7..0f8892f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -136,19 +136,19 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
- boolean isEnabled = false;
+ boolean isSuccessful = false;
if (mService == null) {
return false;
}
if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
- return isEnabled;
+ return isSuccessful;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index d53c3a7..2999c83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -153,7 +153,7 @@
}
final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
final int iconSize = getContext().getResources().getDimensionPixelSize(
- com.android.settingslib.widget.R.dimen.secondary_app_icon_size);
+ com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size);
if (icon != null && iconSize > 0) {
ViewGroup.LayoutParams params = icon.getLayoutParams();
params.height = iconSize;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
deleted file mode 100644
index 54d5c3d..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.net;
-
-import android.app.usage.NetworkStats;
-import android.app.usage.NetworkStatsManager;
-import android.content.Context;
-import android.net.NetworkTemplate;
-import android.util.Log;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-/**
- * Loader for retrieving the network stats summary for all UIDs.
- */
-public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
-
- private static final String TAG = "NetworkDetailLoader";
- private final NetworkStatsManager mNetworkStatsManager;
- private final long mStart;
- private final long mEnd;
- private final NetworkTemplate mNetworkTemplate;
-
- private NetworkStatsSummaryLoader(Builder builder) {
- super(builder.mContext);
- mStart = builder.mStart;
- mEnd = builder.mEnd;
- mNetworkTemplate = builder.mNetworkTemplate;
- mNetworkStatsManager = (NetworkStatsManager)
- builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- public NetworkStats loadInBackground() {
- try {
- return mNetworkStatsManager.querySummary(mNetworkTemplate, mStart, mEnd);
- } catch (RuntimeException e) {
- Log.e(TAG, "Exception querying network detail.", e);
- return null;
- }
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- super.onReset();
- cancelLoad();
- }
-
- public static class Builder {
- private final Context mContext;
- private long mStart;
- private long mEnd;
- private NetworkTemplate mNetworkTemplate;
-
- public Builder(Context context) {
- mContext = context;
- }
-
- public Builder setStartTime(long start) {
- mStart = start;
- return this;
- }
-
- public Builder setEndTime(long end) {
- mEnd = end;
- return this;
- }
-
- /**
- * Set {@link NetworkTemplate} for builder
- */
- public Builder setNetworkTemplate(NetworkTemplate template) {
- mNetworkTemplate = template;
- return this;
- }
-
- public NetworkStatsSummaryLoader build() {
- return new NetworkStatsSummaryLoader(this);
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 2d6f058..98272cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -167,8 +167,8 @@
ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
bindFrictionImage(frictionImageView);
- final View divider =
- view.findViewById(com.android.settingslib.widget.R.id.two_target_divider);
+ final View divider = view.findViewById(
+ com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
divider.setVisibility(shouldShowDivider() ? View.VISIBLE : View.INVISIBLE);
}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 8970deb..b03c43c 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -23,6 +23,7 @@
android_test {
name: "SettingsLibTests",
+ use_resource_processor: true,
defaults: [
"SettingsLibDefaults",
"framework-wifi-test-defaults",
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index 2a4dfdd..a95da303 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib">
+ package="com.android.settingslib.tests.integ">
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index b81d13d..c8b20c1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -28,6 +28,8 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import com.android.settingslib.widget.spinner.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index a9ad00d..a66d3fb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -35,6 +35,8 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import com.android.settingslib.widget.preference.usage.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index c037c40..dd9cb9c 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -27,6 +27,7 @@
android_app {
name: "SettingsLibShell",
+ use_resource_processor: true,
defaults: ["SettingsLibDefaults"],
platform_apis: true,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 4811d950..e1577e0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -182,14 +182,14 @@
final Menu item = mock(Menu.class);
when(item.findItem(MENU_HELP)).thenReturn(null);
when(item.add(0, MENU_HELP, 0,
- com.android.settingslib.widget.R.string.help_feedback_label)).thenReturn(
+ com.android.settingslib.widget.help.R.string.help_feedback_label)).thenReturn(
mock(MenuItem.class));
HelpUtils.prepareHelpMenuItem(mActivity, item, TEST_HELP_URL, "backup_url");
HelpUtils.prepareHelpMenuItem(mActivity, item, 0, "backup_url");
verify(item, times(2)).add(0, MENU_HELP, 0,
- com.android.settingslib.widget.R.string.help_feedback_label);
+ com.android.settingslib.widget.help.R.string.help_feedback_label);
}
@Test
@@ -197,13 +197,13 @@
final Menu item = mock(Menu.class);
when(item.findItem(MENU_HELP)).thenReturn(mock(MenuItem.class));
when(item.add(0, MENU_HELP, 0,
- com.android.settingslib.widget.R.string.help_feedback_label)).thenReturn(
+ com.android.settingslib.widget.help.R.string.help_feedback_label)).thenReturn(
mock(MenuItem.class));
HelpUtils.prepareHelpMenuItem(mActivity, item, TEST_HELP_URL, "backup_url");
HelpUtils.prepareHelpMenuItem(mActivity, item, 0, "backup_url");
verify(item, never()).add(0, MENU_HELP, 0,
- com.android.settingslib.widget.R.string.help_feedback_label);
+ com.android.settingslib.widget.help.R.string.help_feedback_label);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index 32a16716..d9cf9f2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -54,7 +54,8 @@
mPreference = new PrimarySwitchPreference(mContext);
LayoutInflater inflater = LayoutInflater.from(mContext);
mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
- com.android.settingslib.widget.R.layout.preference_two_target, null));
+ com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target,
+ null));
mWidgetView = mHolder.itemView.findViewById(android.R.id.widget_frame);
inflater.inflate(R.layout.preference_widget_primary_switch, mWidgetView, true);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
index ff84dc9..2b1e808 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
@@ -24,7 +24,7 @@
import androidx.annotation.Nullable;
-import com.android.settingslib.R;
+import com.android.settingslib.collapsingtoolbar.R;
import org.junit.Before;
import org.junit.Test;
@@ -58,7 +58,7 @@
View contentFrameView =
layout.findViewById(com.android.settingslib.widget.R.id.content_frame);
- TextView textView = contentFrameView.findViewById(R.id.text_hello_world);
+ TextView textView = contentFrameView.findViewById(com.android.settingslib.robotests.R.id.text_hello_world);
assertThat(textView).isNotNull();
assertThat(textView.getText().toString()).isEqualTo(TEXT_HELLO_WORLD);
@@ -80,8 +80,8 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_Light_NoTitleBar);
- setContentView(R.layout.collapsing_test_layout);
- mCollapsingCoordinatorLayout = findViewById(R.id.id_collapsing_test);
+ setContentView(com.android.settingslib.robotests.R.layout.collapsing_test_layout);
+ mCollapsingCoordinatorLayout = findViewById(com.android.settingslib.robotests.R.id.id_collapsing_test);
}
public CollapsingCoordinatorLayout getCollapsingCoordinatorLayout() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
index 9f31cef..6d9261d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -31,6 +31,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.actionbuttons.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,10 +57,10 @@
@Test
public void onBindViewHolder_setTitle_shouldShowButtonByDefault() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton2Text(R.string.install_other_apps);
- mPref.setButton3Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -107,10 +109,10 @@
@Test
public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() {
- mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
- mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
- mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
- mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps).setButton4Visible(false);
mPref.onBindViewHolder(mHolder);
@@ -145,10 +147,10 @@
@Test
public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() {
- mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
- mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
- mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
- mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps).setButton4Visible(false);
mPref.onBindViewHolder(mHolder);
@@ -195,26 +197,26 @@
@Test
public void onBindViewHolder_setText_shouldShowSameText() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton2Text(R.string.install_other_apps);
- mPref.setButton3Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(((Button) mRootView.findViewById(R.id.button1)).getText())
- .isEqualTo(mContext.getText(R.string.install_other_apps));
+ .isEqualTo(mContext.getText(com.android.settingslib.R.string.install_other_apps));
assertThat(((Button) mRootView.findViewById(R.id.button2)).getText())
- .isEqualTo(mContext.getText(R.string.install_other_apps));
+ .isEqualTo(mContext.getText(com.android.settingslib.R.string.install_other_apps));
assertThat(((Button) mRootView.findViewById(R.id.button3)).getText())
- .isEqualTo(mContext.getText(R.string.install_other_apps));
+ .isEqualTo(mContext.getText(com.android.settingslib.R.string.install_other_apps));
assertThat(((Button) mRootView.findViewById(R.id.button4)).getText())
- .isEqualTo(mContext.getText(R.string.install_other_apps));
+ .isEqualTo(mContext.getText(com.android.settingslib.R.string.install_other_apps));
}
@Test
public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() {
- mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
mPref.setButton1Icon(com.android.internal.R.drawable.ic_plus);
mPref.onBindViewHolder(mHolder);
@@ -227,7 +229,7 @@
@Test
public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
- mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
mPref.setButton1Icon(0);
mPref.onBindViewHolder(mHolder);
@@ -240,7 +242,7 @@
@Test
public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() {
- mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
mPref.setButton1Icon(999999999 /* not existing id */);
// Should not crash here
mPref.onBindViewHolder(mHolder);
@@ -253,10 +255,10 @@
@Test
public void onBindViewHolder_setAllButton_shouldShowAllDivider() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton2Text(R.string.install_other_apps);
- mPref.setButton3Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -270,9 +272,9 @@
@Test
public void onBindViewHolder_setAllButtonWithoutButton2_shouldHideDivider1() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton3Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -286,9 +288,9 @@
@Test
public void onBindViewHolder_setAllButtonWithoutButton3_shouldHideDivider2() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton2Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -302,8 +304,8 @@
@Test
public void onBindViewHolder_setButton1And4_shouldShowDivider3Only() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -317,7 +319,7 @@
@Test
public void onBindViewHolder_setOneButtonOnly_noDivider() {
- mPref.setButton4Text(R.string.install_other_apps);
+ mPref.setButton4Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -331,8 +333,8 @@
@Test
public void onBindViewHolder_setButton1And2_shouldShowDivider1Only() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton2Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
@@ -346,8 +348,8 @@
@Test
public void onBindViewHolder_setButton1And3_shouldShowDivider2Only() {
- mPref.setButton1Text(R.string.install_other_apps);
- mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton1Text(com.android.settingslib.R.string.install_other_apps);
+ mPref.setButton3Text(com.android.settingslib.R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
index 10862403..6195d75 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -36,7 +36,7 @@
import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
-import com.android.settingslib.R;
+import com.android.settingslib.widget.adaptiveicon.R;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;
@@ -87,7 +87,7 @@
public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
- doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ doReturn(Icon.createWithResource(mContext, com.android.settingslib.R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
final AdaptiveIcon icon =
new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
@@ -99,7 +99,7 @@
@Test
public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
- doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ doReturn(Icon.createWithResource(mContext, com.android.settingslib.R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
@@ -114,7 +114,7 @@
final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
com.android.settingslib.widget.R.color.bt_outline_color);
- doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ doReturn(Icon.createWithResource(mContext, com.android.settingslib.R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
final AdaptiveIcon icon =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
index b2bc53d..943b994 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
@@ -21,6 +21,8 @@
import android.content.res.Resources;
import android.graphics.Paint;
+import com.android.settingslib.widget.adaptiveicon.R;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
index c9b066a..172c178 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -24,6 +24,8 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.settingslib.widget.entityheader.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -69,18 +71,18 @@
@Test
public void setHeaderTitleRes_setTextRes_shouldSetToTitleView() {
- mController.setHeaderTitleRes(R.string.expand_button_title).apply();
+ mController.setHeaderTitleRes(androidx.preference.R.string.expand_button_title).apply();
final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_title);
- assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ assertThat(view.getText()).isEqualTo(mContext.getText(androidx.preference.R.string.expand_button_title));
}
@Test
public void setHeaderDetailsRes_setTextRes_shouldSetToDetailsView() {
- mController.setHeaderDetailsRes(R.string.expand_button_title).apply();
+ mController.setHeaderDetailsRes(androidx.preference.R.string.expand_button_title).apply();
final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
- assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ assertThat(view.getText()).isEqualTo(mContext.getText(androidx.preference.R.string.expand_button_title));
}
@Test
@@ -93,7 +95,7 @@
@Test
public void setHeaderDetails_detailsTextAndResBothSet_shouldSetTextToDetailsView() {
- mController.setHeaderDetailsRes(R.string.expand_button_title);
+ mController.setHeaderDetailsRes(androidx.preference.R.string.expand_button_title);
mController.setHeaderDetails(TITLE).apply();
final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
@@ -206,7 +208,7 @@
@Test
public void apply_noAppEntitySet_shouldOnlyShowTitleAndEmptyView() {
- mController.setHeaderTitleRes(R.string.expand_button_title)
+ mController.setHeaderTitleRes(androidx.preference.R.string.expand_button_title)
.setAppEntity(0, mAppEntityInfo)
.setAppEntity(1, mAppEntityInfo)
.setAppEntity(2, mAppEntityInfo).apply();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
index e2b242c..9112f6b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
@@ -24,7 +24,7 @@
import androidx.preference.PreferenceViewHolder;
-
+import com.android.settingslib.widget.preference.app.R;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
index 9e265a4..6c8fd50 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppPreferenceTest.java
@@ -23,6 +23,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.app.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +58,7 @@
@Test
public void foobar_testName() {
- float iconSize = mContext.getResources().getDimension(R.dimen.secondary_app_icon_size);
+ float iconSize = mContext.getResources().getDimension(com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size);
assertThat(Float.floatToIntBits(iconSize)).isEqualTo(Float.floatToIntBits(32));
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
index 0d88913..721e69d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
@@ -39,7 +39,7 @@
import androidx.annotation.ColorRes;
import androidx.preference.PreferenceViewHolder;
-import androidx.preference.R;
+import com.android.settingslib.widget.preference.banner.R;
import com.android.settingslib.testutils.OverpoweredReflectionHelper;
@@ -65,7 +65,7 @@
private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true;
private final int mMinimumTargetSize =
RuntimeEnvironment.application.getResources()
- .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target);
+ .getDimensionPixelSize(com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target);
private static final int TEST_STRING_RES_ID =
R.string.accessibility_banner_message_dismiss;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index 567d90f..ae6573e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -26,6 +26,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.barchart.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -71,9 +73,9 @@
mDetailsView = mBarChartView.findViewById(R.id.bar_chart_details);
mBarChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
- .setDetails(R.string.debug_app)
- .setEmptyText(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
+ .setDetails(com.android.settingslib.R.string.debug_app)
+ .setEmptyText(com.android.settingslib.R.string.debug_app)
.setDetailsOnClickListener(v -> {
})
.build();
@@ -82,21 +84,21 @@
@Test
public void initializeBarChart_titleSet_shouldSetTitleInChartView() {
final BarChartInfo barChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
.build();
mPreference.initializeBarChart(barChartInfo);
mPreference.onBindViewHolder(mHolder);
assertThat(mTitleView.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mTitleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ assertThat(mTitleView.getText()).isEqualTo(mContext.getText(com.android.settingslib.R.string.debug_app));
}
@Test
public void initializeBarChart_noBarViewSet_shouldShowTitleAndEmptyView() {
final BarChartInfo barChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
- .setEmptyText(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
+ .setEmptyText(com.android.settingslib.R.string.debug_app)
.build();
mPreference.initializeBarChart(barChartInfo);
@@ -113,11 +115,11 @@
@Test
public void initializeBarChart_detailsSet_shouldShowBarChartDetailsView() {
final BarChartInfo barChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
- .setDetails(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
+ .setDetails(com.android.settingslib.R.string.debug_app)
.addBarViewInfo(
new BarViewInfo(mIcon, 10, null /* title */,
- mContext.getText(R.string.debug_app) /* summary */,
+ mContext.getText(com.android.settingslib.R.string.debug_app) /* summary */,
null /* contentDescription */))
.build();
@@ -125,17 +127,17 @@
mPreference.onBindViewHolder(mHolder);
assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(com.android.settingslib.R.string.debug_app));
}
@Test
public void initializeBarChart_detailsNotSet_shouldHideBarChartDetailsView() {
// We don't call BarChartInfo.Builder#setDetails yet.
final BarChartInfo barChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
.addBarViewInfo(
new BarViewInfo(mIcon, 10, null /* title */,
- mContext.getText(R.string.debug_app) /* summary */,
+ mContext.getText(com.android.settingslib.R.string.debug_app) /* summary */,
null /* contentDescription */))
.build();
@@ -148,13 +150,13 @@
@Test
public void initializeBarChart_clickListenerSet_shouldSetClickListenerOnDetailsView() {
final BarChartInfo barChartInfo = new BarChartInfo.Builder()
- .setTitle(R.string.debug_app)
- .setDetails(R.string.debug_app)
+ .setTitle(com.android.settingslib.R.string.debug_app)
+ .setDetails(com.android.settingslib.R.string.debug_app)
.setDetailsOnClickListener(v -> {
})
.addBarViewInfo(
new BarViewInfo(mIcon, 10, null /* title */,
- mContext.getText(R.string.debug_app) /* summary */,
+ mContext.getText(com.android.settingslib.R.string.debug_app) /* summary */,
null /* contentDescription */))
.build();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
index d78f8e7..f3fe517 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
@@ -68,14 +68,14 @@
@Test
public void onBindViewHolder_whenIconSet_shouldSetIcon() {
- mPreference.setIcon(R.drawable.settingslib_ic_cross);
+ mPreference.setIcon(com.android.settingslib.widget.preference.banner.R.drawable.settingslib_ic_cross);
mPreference.onBindViewHolder(mHolder);
final Button button = mPreference.getButton();
final Drawable icon = button.getCompoundDrawablesRelative()[0];
final ShadowDrawable shadowDrawable = shadowOf(icon);
- assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.settingslib_ic_cross);
+ assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(com.android.settingslib.widget.preference.banner.R.drawable.settingslib_ic_cross);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index a26f200..ccbe4f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -28,7 +28,7 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.R;
+import com.android.settingslib.widget.preference.footer.R;
import org.junit.Before;
import org.junit.Test;
@@ -96,7 +96,7 @@
public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
LayoutInflater.from(mContext)
- .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
+ .inflate(R.layout.preference_footer, null)));
when(viewHolder.findViewById(androidx.core.R.id.title)).thenReturn(null);
Throwable actualThrowable = null;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 21e119a..6590bbd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -39,6 +39,8 @@
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.widget.preference.illustration.R;
+
import com.airbnb.lottie.LottieAnimationView;
import org.junit.Before;
@@ -49,6 +51,7 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+
@RunWith(RobolectricTestRunner.class)
public class IllustrationPreferenceTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
index 99261a3..58817fb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -27,6 +27,8 @@
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.layout.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
index 24037ca..942e915 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
@@ -26,6 +26,8 @@
import android.widget.Switch;
import android.widget.TextView;
+import com.android.settingslib.widget.mainswitch.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
index e58c04a..c2e81bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchPreferenceTest.java
@@ -24,6 +24,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.mainswitch.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
index a5028ff..0f708cd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
@@ -28,6 +28,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.radio.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 34efe82..60885f1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -28,6 +28,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.selector.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
index 23b4c2a..3befdda 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
@@ -32,6 +32,8 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.preference.twotarget.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp
index a4558f1..19ab1c6 100644
--- a/packages/SettingsLib/tests/unit/Android.bp
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -20,6 +20,7 @@
android_test {
name: "SettingsLibUnitTests",
+ use_resource_processor: true,
test_suites: ["device-tests"],
srcs: [
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 0a98032..e9533e5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -137,6 +137,13 @@
private static final String ATTR_VALUE_BASE64 = "valueBase64";
private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64";
+ /**
+ * In the config table, there are special flags of the form {@code staged/namespace*flagName}.
+ * On boot, when the XML file is initially parsed, these transform into
+ * {@code namespace/flagName}, and the special staged flags are deleted.
+ */
+ private static final String CONFIG_STAGED_PREFIX = "staged/";
+
// This was used in version 120 and before.
private static final String NULL_VALUE_OLD_STYLE = "null";
@@ -1191,6 +1198,42 @@
}
}
+ /**
+ * Transforms a staged flag name to its real flag name.
+ *
+ * Staged flags take the form {@code staged/namespace*flagName}. If
+ * {@code stagedFlagName} takes the proper form, returns
+ * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName}
+ * unmodified, and logs an error message.
+ *
+ */
+ @VisibleForTesting
+ public static String createRealFlagName(String stagedFlagName) {
+ int slashIndex = stagedFlagName.indexOf("/");
+ if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1
+ || slashIndex == 0) {
+ Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName);
+ return stagedFlagName;
+ }
+
+ String namespaceAndFlag =
+ stagedFlagName.substring(slashIndex + 1);
+
+ int starIndex = namespaceAndFlag.indexOf("*");
+ if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1
+ || starIndex == 0) {
+ Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName);
+ return stagedFlagName;
+ }
+
+ String namespace =
+ namespaceAndFlag.substring(0, starIndex);
+ String flagName =
+ namespaceAndFlag.substring(starIndex + 1);
+
+ return namespace + "/" + flagName;
+ }
+
@GuardedBy("mLock")
private void parseSettingsLocked(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
@@ -1199,6 +1242,7 @@
final int outerDepth = parser.getDepth();
int type;
+ HashSet<String> flagsWithStagedValueApplied = new HashSet<String>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1221,6 +1265,18 @@
fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false);
tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
}
+
+ if (isConfigSettingsKey(mKey)) {
+ if (flagsWithStagedValueApplied.contains(name)) {
+ continue;
+ }
+
+ if (name.startsWith(CONFIG_STAGED_PREFIX)) {
+ name = createRealFlagName(name);
+ flagsWithStagedValueApplied.add(name);
+ }
+ }
+
mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
fromSystem, id, isPreservedInRestore));
@@ -1229,6 +1285,16 @@
}
}
}
+
+ if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) {
+ // On boot, the config table XML file includes special staged flags. On the initial
+ // boot XML -> HashMap parse, these staged flags get transformed into real flags.
+ // After this, the HashMap contains no special staged flags (only the transformed
+ // real flags), but the XML still does. We then have no need for the special staged
+ // flags in the XML, so we overwrite the XML with the latest contents of the
+ // HashMap.
+ writeStateAsyncLocked();
+ }
}
@GuardedBy("mLock")
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 26cac37..df4d2a1 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -51,6 +51,21 @@
private static final String SYSTEM_PACKAGE = "android";
private static final String SETTING_NAME = "test_setting";
+ private static final String FLAG_NAME_1 = "namespace123/flag456";
+ private static final String FLAG_NAME_1_STAGED = "staged/namespace123*flag456";
+ private static final String FLAG_NAME_2 = "not_staged/flag101";
+
+ private static final String INVALID_STAGED_FLAG_1 = "stagednamespace*flagName";
+ private static final String INVALID_STAGED_FLAG_2 = "staged/";
+ private static final String INVALID_STAGED_FLAG_3 = "staged/namespace*";
+ private static final String INVALID_STAGED_FLAG_4 = "staged/*flagName";
+
+ private static final String VALID_STAGED_FLAG_1 = "staged/namespace*flagName";
+ private static final String VALID_STAGED_FLAG_1_TRANSFORMED = "namespace/flagName";
+
+ private static final String VALUE1 = "5";
+ private static final String VALUE2 = "6";
+
private final Object mLock = new Object();
private File mSettingsFile;
@@ -454,4 +469,68 @@
}
}
}
+
+ public void testApplyStagedConfigValues() {
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+ Object lock = new Object();
+ SettingsState settingsState = new SettingsState(
+ getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ synchronized (lock) {
+ settingsState.insertSettingLocked(
+ FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE);
+ settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE);
+ settingsState.persistSyncLocked();
+
+ assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
+ assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
+ }
+
+ settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ synchronized (lock) {
+ assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1).getValue());
+ assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
+
+ assertEquals(null, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
+ }
+ }
+
+ public void testStagingTransformation() {
+ assertEquals(INVALID_STAGED_FLAG_1,
+ SettingsState.createRealFlagName(INVALID_STAGED_FLAG_1));
+ assertEquals(INVALID_STAGED_FLAG_2,
+ SettingsState.createRealFlagName(INVALID_STAGED_FLAG_2));
+ assertEquals(INVALID_STAGED_FLAG_3,
+ SettingsState.createRealFlagName(INVALID_STAGED_FLAG_3));
+ assertEquals(INVALID_STAGED_FLAG_4,
+ SettingsState.createRealFlagName(INVALID_STAGED_FLAG_4));
+
+ assertEquals(VALID_STAGED_FLAG_1_TRANSFORMED,
+ SettingsState.createRealFlagName(VALID_STAGED_FLAG_1));
+ }
+
+ public void testInvalidStagedFlagsUnaffectedByReboot() {
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+ Object lock = new Object();
+ SettingsState settingsState = new SettingsState(
+ getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ synchronized (lock) {
+ settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1,
+ VALUE2, null, false, TEST_PACKAGE);
+ settingsState.persistSyncLocked();
+ assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
+ }
+
+ settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ synchronized (lock) {
+ assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
+ }
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ffe28a6..11ae9c3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
>
<!-- Standard permissions granted to the shell. -->
+ <uses-permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER" />
<uses-permission android:name="android.permission.MANAGE_HEALTH_PERMISSIONS" />
<uses-permission android:name="android.permission.MANAGE_HEALTH_DATA" />
<uses-permission android:name="android.permission.health.READ_EXERCISE_ROUTE" />
@@ -205,6 +206,7 @@
<uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
<!-- Permission required for processes that don't own the focused window to switch
@@ -852,6 +854,9 @@
<!-- Permission required for accessing all content provider mime types -->
<uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
+ <!-- Permission required for CTS-in-sandbox tests -->
+ <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX" />
+
<!-- Permission required for CTS test - CtsWallpaperTestCases -->
<uses-permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 77925d6..5e7e044 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -116,6 +116,7 @@
//Create a library to expose SystemUI's resources to other modules.
android_library {
name: "SystemUI-res",
+ use_resource_processor: true,
resource_dirs: [
"res-product",
"res-keyguard",
@@ -149,13 +150,9 @@
exclude_srcs: [":ReleaseJavaFiles"],
},
},
- resource_dirs: [
- "res-product",
- "res-keyguard",
- "res",
- ],
use_resource_processor: true,
static_libs: [
+ "SystemUI-res",
"WifiTrackerLib",
"WindowManager-Shell",
"SystemUIAnimationLib",
@@ -203,6 +200,9 @@
"LowLightDreamLib",
"motion_tool_lib",
],
+ libs: [
+ "keepanno-annotations",
+ ],
manifest: "AndroidManifest.xml",
javacflags: ["-Adagger.fastInit=enabled"],
@@ -377,14 +377,13 @@
android_library {
name: "SystemUI-tests-base",
+ use_resource_processor: true,
manifest: "tests/AndroidManifest-base.xml",
resource_dirs: [
"tests/res",
- "res-product",
- "res-keyguard",
- "res",
],
static_libs: [
+ "SystemUI-res",
"WifiTrackerLib",
"SystemUIAnimationLib",
"SystemUIPluginLib",
@@ -439,6 +438,7 @@
android_library {
name: "SystemUI-tests",
+ use_resource_processor: true,
defaults: [
"SystemUI_compose_defaults",
],
@@ -466,6 +466,7 @@
"android.test.runner",
"android.test.base",
"android.test.mock",
+ "keepanno-annotations",
],
kotlincflags: ["-Xjvm-default=all"],
aaptflags: [
@@ -481,6 +482,7 @@
android_app {
name: "SystemUIRobo-stub",
+ use_resource_processor: true,
defaults: [
"platform_app_defaults",
"SystemUI_optimized_defaults",
@@ -497,6 +499,9 @@
static_libs: [
"SystemUI-tests-base",
],
+ libs: [
+ "keepanno-annotations",
+ ],
aaptflags: [
"--extra-packages",
"com.android.systemui",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 78da5a6..8f329b3 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -4,7 +4,6 @@
dsandler@android.com
-aaliomer@google.com
aaronjli@google.com
achalke@google.com
acul@google.com
@@ -36,6 +35,7 @@
gwasserman@google.com
hwwang@google.com
hyunyoungs@google.com
+ikateryna@google.com
jaggies@google.com
jamesoleary@google.com
jbolinger@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cb9e9ee..af6fa86 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -43,7 +43,9 @@
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
- ]
+ ],
+ // The test doesn't run on AOSP Cuttlefish
+ "keywords": ["internal"]
},
{
// TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
@@ -55,7 +57,9 @@
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
- ]
+ ],
+ // The test doesn't run on AOSP Cuttlefish
+ "keywords": ["internal"]
},
{
// Permission indicators
@@ -112,10 +116,8 @@
"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
}
]
- }
- ],
- "postsubmit": [
- {
+ },
+ {
"name": "SystemUIGoogleScreenshotTests",
"options": [
{
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index ff723e3..8b12f3c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -29,6 +29,7 @@
android_app {
name: "AccessibilityMenu",
+ use_resource_processor: true,
static_libs: [
"androidx.coordinatorlayout_coordinatorlayout",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 03cbc16..0f55f35 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -5,4 +5,11 @@
namespace: "accessibility"
description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
bug: "298467628"
+}
+
+flag {
+ name: "a11y_menu_hide_before_taking_action"
+ namespace: "accessibility"
+ description: "Hides the AccessibilityMenuService UI before taking action instead of after."
+ bug: "292020123"
}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 587395d..fd9a9c6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -15,7 +15,7 @@
android:layout_centerHorizontal="true"
android:scaleType="fitCenter"/>
-<TextView
+ <TextView
android:id="@+id/shortcutLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -27,6 +27,6 @@
android:lines="2"
android:textSize="@dimen/label_text_size"
android:textAlignment="center"
- android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.Button"/>
+ android:textColor="@color/grid_item_text_color"/>
</RelativeLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index fbf2b07..f961bdb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -20,5 +20,6 @@
<color name="footer_icon_disabled_color">#5F6368</color>
<color name="colorControlNormal">#202124</color>
<color name="snackbar_bg_color">@android:color/white</color>
+ <color name="grid_item_text_color">@android:color/white</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
index d81d0d5..9722eb0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -21,6 +21,7 @@
<color name="footer_icon_disabled_color">#ddd</color>
<color name="colorControlNormal">@android:color/white</color>
<color name="snackbar_bg_color">#313235</color>
+ <color name="grid_item_text_color">@android:color/black</color>
<color name="colorAccessibilityMenuIcon">#3AA757</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 27aade5..008732e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -260,6 +260,27 @@
// Shortcuts are repeatable in a11y menu rather than unique, so use tag ID to handle.
int viewTag = (int) view.getTag();
+ // First check if this was a shortcut which should keep a11y menu visible. If so,
+ // perform the shortcut and return without hiding the UI.
+ if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
+ adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
+ return;
+ } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
+ adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
+ return;
+ } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
+ adjustVolume(AudioManager.ADJUST_RAISE);
+ return;
+ } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
+ adjustVolume(AudioManager.ADJUST_LOWER);
+ return;
+ }
+
+ if (Flags.a11yMenuHideBeforeTakingAction()) {
+ // Hide the a11y menu UI before performing the following shortcut actions.
+ mA11yMenuLayout.hideMenu();
+ }
+
if (viewTag == ShortcutId.ID_ASSISTANT_VALUE.ordinal()) {
// Always restart the voice command activity, so that the UI is reloaded.
startActivityIfIntentIsSafe(
@@ -281,21 +302,11 @@
performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);
} else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) {
performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
- } else if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
- adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
- return;
- } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
- adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
- return;
- } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
- adjustVolume(AudioManager.ADJUST_RAISE);
- return;
- } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
- adjustVolume(AudioManager.ADJUST_LOWER);
- return;
}
- mA11yMenuLayout.hideMenu();
+ if (!Flags.a11yMenuHideBeforeTakingAction()) {
+ mA11yMenuLayout.hideMenu();
+ }
}
/**
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
index 1757dda..538ecb3 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -20,6 +20,7 @@
android_test {
name: "AccessibilityMenuServiceTests",
+ use_resource_processor: true,
certificate: "platform",
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS
new file mode 100644
index 0000000..e1a7a0f
--- /dev/null
+++ b/packages/SystemUI/aconfig/OWNERS
@@ -0,0 +1 @@
+per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 6f53b42..8438051 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -24,6 +24,7 @@
android_library {
name: "SystemUIAnimationLib",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
@@ -52,6 +53,7 @@
android_library {
name: "SystemUIShaderLib",
+ use_resource_processor: true,
srcs: [
"src/com/android/systemui/surfaceeffects/**/*.java",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 0c07616..0f2e4ba 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -25,6 +25,7 @@
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
+import android.os.Handler
import android.os.Looper
import android.os.RemoteException
import android.util.Log
@@ -58,7 +59,14 @@
/** The animator used when animating a Dialog into an app. */
// TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
// TIMINGS.contentBeforeFadeOutDuration.
- private val dialogToAppAnimator: LaunchAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR
+ private val dialogToAppAnimator: LaunchAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR,
+
+ /**
+ * Whether we should disable the WindowManager timeout. This should be set to true in tests
+ * only.
+ */
+ // TODO(b/301385865): Remove this flag.
+ private val disableWmTimeout: Boolean = false,
) {
companion object {
/** The timings when animating a View into an app. */
@@ -252,7 +260,7 @@
Log.d(
TAG,
"Calling controller.onIntentStarted(willAnimate=$willAnimate) " +
- "[controller=$this]"
+ "[controller=$this]"
)
}
this.onIntentStarted(willAnimate)
@@ -431,7 +439,8 @@
internal val delegate: AnimationDelegate
init {
- delegate = AnimationDelegate(controller, callback, listener, launchAnimator)
+ delegate =
+ AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout)
}
@BinderThread
@@ -461,13 +470,26 @@
/** Listener for animation lifecycle events. */
private val listener: Listener? = null,
/** The animator to use to animate the window launch. */
- private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR
+ private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR,
+
+ /**
+ * Whether we should disable the WindowManager timeout. This should be set to true in tests
+ * only.
+ */
+ // TODO(b/301385865): Remove this flag.
+ disableWmTimeout: Boolean = false,
) : RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> {
private val launchContainer = controller.launchContainer
private val context = launchContainer.context
private val transactionApplierView =
controller.openingWindowSyncView ?: controller.launchContainer
private val transactionApplier = SyncRtSurfaceTransactionApplier(transactionApplierView)
+ private val timeoutHandler =
+ if (!disableWmTimeout) {
+ Handler(Looper.getMainLooper())
+ } else {
+ null
+ }
private val matrix = Matrix()
private val invertMatrix = Matrix()
@@ -487,11 +509,11 @@
@UiThread
internal fun postTimeout() {
- launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+ timeoutHandler?.postDelayed(onTimeout, LAUNCH_TIMEOUT)
}
private fun removeTimeout() {
- launchContainer.removeCallbacks(onTimeout)
+ timeoutHandler?.removeCallbacks(onTimeout)
}
@UiThread
@@ -605,12 +627,28 @@
object : Controller by delegate {
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationStart()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationEnd()
iCallback?.invoke()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
diff --git a/packages/SystemUI/common/Android.bp b/packages/SystemUI/common/Android.bp
index e36ada8..482776a 100644
--- a/packages/SystemUI/common/Android.bp
+++ b/packages/SystemUI/common/Android.bp
@@ -24,6 +24,7 @@
android_library {
name: "SystemUICommon",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index ab3efb8..510fa1e 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -38,4 +38,5 @@
],
kotlincflags: ["-Xjvm-default=all"],
+ use_resource_processor: true,
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 767756e..17c74ba 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -263,9 +263,11 @@
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
+ cujType?.let { InteractionJankMonitor.getInstance().begin(composeViewRoot, it) }
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ cujType?.let { InteractionJankMonitor.getInstance().end(it) }
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
overlay.value = null
}
@@ -320,9 +322,8 @@
}
override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
- // TODO(b/252723237): Add support for jank monitoring when animating from a
- // Composable.
- return null
+ val type = cuj?.cujType ?: return null
+ return InteractionJankMonitor.Configuration.Builder.withView(type, composeViewRoot)
}
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
index 7536728..88944f10 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -107,6 +107,9 @@
reversed: Boolean = false,
) {
val fromScene = layoutImpl.state.transitionState.currentScene
+ val isUserInput =
+ (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+ ?: false
val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
val visibilityThreshold =
@@ -116,9 +119,9 @@
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(target, fromScene, currentScene = target, animatable)
+ OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
} else {
- OneOffTransition(fromScene, target, currentScene = target, animatable)
+ OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
}
// Change the current layout state to use this new transition.
@@ -139,6 +142,7 @@
override val fromScene: SceneKey,
override val toScene: SceneKey,
override val currentScene: SceneKey,
+ override val isUserInputDriven: Boolean,
private val animatable: Animatable<Float, AnimationVector1D>,
) : TransitionState.Transition {
override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index a625250..ccdec6e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -42,6 +42,17 @@
val fromScene: SceneKey,
val toScene: SceneKey,
val progress: Flow<Float>,
+
+ /**
+ * Whether the transition was originally triggered by user input rather than being
+ * programmatic. If this value is initially true, it will remain true until the transition
+ * fully completes, even if the user input that triggered the transition has ended. Any
+ * sub-transitions launched by this one will inherit this value. For example, if the user
+ * drags a pointer but does not exceed the threshold required to transition to another
+ * scene, this value will remain true after the pointer is no longer touching the screen and
+ * will be true in any transition created to animate back to the original position.
+ */
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
@@ -62,6 +73,7 @@
fromScene = state.fromScene,
toScene = state.toScene,
progress = snapshotFlow { state.progress },
+ isUserInputDriven = state.isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 47e3d5a..7a21211 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -68,5 +68,8 @@
* when flinging quickly during a swipe gesture.
*/
val progress: Float
+
+ /** Whether the transition was triggered by user input rather than being programmatic. */
+ val isUserInputDriven: Boolean
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index 2069ebd..790ea08 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -137,6 +137,8 @@
return offset / distance
}
+ override val isUserInputDriven = true
+
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 5a8a374..52c6385 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -47,4 +47,5 @@
],
kotlincflags: ["-Xjvm-default=all"],
+ use_resource_processor: true,
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index cb2607a..2232370 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -122,6 +122,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating back to A (currentScene = SceneA) given
// that 55dp < positional threshold.
@@ -133,6 +134,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene A.
rule.waitForIdle()
@@ -154,6 +156,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating to C (currentScene = SceneC) given
// that 56dp >= positional threshold.
@@ -165,6 +168,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..f80a906
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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.scene
+
+import dagger.Module
+
+@Module interface CommunalSceneModule
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..94b5db2
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.scene
+
+import com.android.systemui.communal.ui.compose.CommunalScene
+import com.android.systemui.scene.shared.model.Scene
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface CommunalSceneModule {
+ @Binds @IntoSet fun communalScene(scene: CommunalScene): Scene
+}
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index c6438c9..e4426fe 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -23,6 +23,7 @@
android_library {
name: "SystemUIComposeFeatures",
+ use_resource_processor: true,
manifest: "AndroidManifest.xml",
srcs: [
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 e06a69b..46a9d42 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
@@ -48,7 +48,7 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -82,7 +82,7 @@
) : ComposableScene {
override val key = SceneKey.Bouncer
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow(
mapOf(
UserAction.Back to SceneModel(SceneKey.Lockscreen),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 8a8557a..df22a70 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -17,8 +17,11 @@
package com.android.systemui.bouncer.ui.composable
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
@@ -29,6 +32,7 @@
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
@@ -44,6 +48,7 @@
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
+@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
@@ -54,6 +59,10 @@
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+ val density = LocalDensity.current
+ val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
+ LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
+
LaunchedEffect(Unit) {
// When the UI comes up, request focus on the TextField to bring up the software keyboard.
focusRequester.requestFocus()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index e5c6977..6491b70 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -55,7 +55,7 @@
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.compose.modifiers.thenIf
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 77065cf..055ece3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -51,7 +51,7 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinInputViewModel
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
new file mode 100644
index 0000000..0d2ba28
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.communal.ui.compose
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.scene.shared.model.UserAction
+import com.android.systemui.scene.ui.composable.ComposableScene
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** The communal scene shows glanceable hub when the device is locked and docked. */
+@SysUISingleton
+class CommunalScene @Inject constructor() : ComposableScene {
+ override val key = SceneKey.Communal
+
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+ MutableStateFlow<Map<UserAction, SceneModel>>(
+ mapOf(
+ UserAction.Swipe(Direction.RIGHT) to SceneModel(SceneKey.Lockscreen),
+ )
+ )
+ .asStateFlow()
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ Box(
+ modifier = modifier.fillMaxSize().background(Color.White),
+ ) {
+ Text(
+ modifier = Modifier.align(Alignment.Center),
+ text = "Hello Communal!",
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 463253b..c3a3752 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -36,7 +36,7 @@
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
@@ -66,13 +66,19 @@
) : ComposableScene {
override val key = SceneKey.Lockscreen
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
viewModel.upDestinationSceneKey
- .map { pageKey -> destinationScenes(up = pageKey) }
+ .map { pageKey ->
+ destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = destinationScenes(up = null)
+ initialValue =
+ destinationScenes(
+ up = viewModel.upDestinationSceneKey.value,
+ left = viewModel.leftDestinationSceneKey,
+ )
)
@Composable
@@ -88,9 +94,11 @@
private fun destinationScenes(
up: SceneKey?,
+ left: SceneKey?,
): Map<UserAction, SceneModel> {
return buildMap {
up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
+ left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 68f010e..e12b7ea 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -47,7 +47,7 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index 1e6f4a2..26cc9b9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -42,7 +42,7 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.R
+import com.android.systemui.res.R
@Composable
internal fun PeopleScreenEmpty(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 4822200..689a0a2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -69,7 +69,7 @@
import com.android.compose.modifiers.background
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.compose.theme.colorAttr
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 7ac3901..1f9c3e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -58,13 +58,14 @@
) : ComposableScene {
override val key = SceneKey.QuickSettings
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ private val _destinationScenes =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
)
)
.asStateFlow()
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = _destinationScenes
@Composable
override fun SceneScope.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 40b0b4a..2ee461f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -38,7 +38,7 @@
class GoneScene @Inject constructor() : ComposableScene {
override val key = SceneKey.Gone
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 6a5a368..ef01266 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -79,7 +79,7 @@
val currentSceneKey = currentSceneModel.key
val currentScene = checkNotNull(sceneByKey[currentSceneKey])
val currentDestinations: Map<UserAction, SceneModel> by
- currentScene.destinationScenes().collectAsState()
+ currentScene.destinationScenes.collectAsState()
val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
val isRibbonEnabled = remember { SystemProperties.getBoolean("flexi.ribbon", false) }
@@ -116,7 +116,7 @@
if (sceneKey == currentSceneKey) {
currentDestinations
} else {
- composableScene.destinationScenes().value
+ composableScene.destinationScenes.value
}
.map { (userAction, destinationSceneModel) ->
toTransitionModels(userAction, destinationSceneModel)
@@ -158,6 +158,7 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
+ isUserInputDriven = isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 404bf81..2848245 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,6 +5,7 @@
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToCommunalTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
@@ -13,7 +14,7 @@
/**
* Comprehensive definition of all transitions between scenes in [SceneContainer].
*
- * Transitions are automatically reversible, so define only one transition per scene pair. By
+ * Transitions are automatically reversible, so define only one transition per scene pair. By\
* convention, use the more common transition direction when defining the pair order, e.g.
* Lockscreen to Bouncer rather than Bouncer to Lockscreen.
*
@@ -27,6 +28,7 @@
from(Gone, to = Shade) { goneToShadeTransition() }
from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() }
from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() }
+ from(Lockscreen, to = Communal) { lockscreenToCommunalTransition() }
from(Lockscreen, to = Shade) { lockscreenToShadeTransition() }
from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() }
from(Lockscreen, to = Gone) { lockscreenToGoneTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
index 8d0d705..5336bf6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
@@ -8,6 +8,7 @@
val Shade = SceneKey.Shade.toTransitionSceneKey()
val QuickSettings = SceneKey.QuickSettings.toTransitionSceneKey()
val Gone = SceneKey.Gone.toTransitionSceneKey()
+val Communal = SceneKey.Communal.toTransitionSceneKey()
// TODO(b/293899074): Remove this file once we can use the scene keys from SceneTransitionLayout.
fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
new file mode 100644
index 0000000..ea8110a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Communal
+import com.android.systemui.scene.ui.composable.Lockscreen
+
+fun TransitionBuilder.lockscreenToCommunalTransition() {
+ spec = tween(durationMillis = 500)
+
+ // Translate lockscreen to the left.
+ translate(Lockscreen.rootElementKey, Edge.Left)
+
+ // Translate communal from the right.
+ translate(Communal.rootElementKey, Edge.Right)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 272e507..8e34008 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -47,7 +47,7 @@
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSharedFloatAsState
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index b105637..8832a11 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -87,7 +87,7 @@
) : ComposableScene {
override val key = SceneKey.Shade
- override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+ override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
viewModel.upDestinationSceneKey
.map { sceneKey -> destinationScenes(up = sceneKey) }
.stateIn(
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp
index c7c9140..c85cd7b 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/packages/SystemUI/compose/features/tests/Android.bp
@@ -24,6 +24,7 @@
// TODO(b/230606318): Make those host tests instead of device tests.
android_test {
name: "SystemUIComposeFeaturesTests",
+ use_resource_processor: true,
manifest: "AndroidManifest.xml",
test_suites: ["device-tests"],
sdk_version: "current",
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index fc37b355..927fd8e 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -23,6 +23,7 @@
android_library {
name: "SystemUICustomizationLib",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 054f9ec..c41dc53 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -148,7 +148,7 @@
override fun onPluginAttached(
manager: PluginLifecycleManager<ClockProviderPlugin>
): Boolean {
- manager.isDebug = true
+ manager.isDebug = !keepAllLoaded
if (keepAllLoaded) {
// Always load new plugins if requested
@@ -511,6 +511,12 @@
fun verifyLoadedProviders() {
val shouldSchedule = isVerifying.compareAndSet(false, true)
if (!shouldSchedule) {
+ logger.tryLog(
+ TAG,
+ LogLevel.VERBOSE,
+ {},
+ { "verifyLoadedProviders: shouldSchedule=false" }
+ )
return
}
@@ -670,6 +676,7 @@
{ str1 = clockId },
{ "Clock $str1 not loaded; using default" }
)
+ verifyLoadedProviders()
} else {
logger.tryLog(
TAG,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 92d2bd2..9d62e38 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -163,9 +163,6 @@
const val TABLE_NAME = "flags"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
- /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
- const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
-
/**
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
*/
diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp
index 627ac4b..2be22a6 100644
--- a/packages/SystemUI/log/Android.bp
+++ b/packages/SystemUI/log/Android.bp
@@ -23,6 +23,7 @@
android_library {
name: "SystemUILogLib",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index bb47a2f..c428952 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -50,6 +50,7 @@
// Dummy to generate .toc files.
name: "PluginDummyLib",
+ use_resource_processor: true,
platform_apis: true,
srcs: ["src/**/*.java"],
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
index 3f0fded..66951b5 100644
--- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
@@ -10,6 +10,7 @@
android_app {
name: "ExamplePlugin",
+ use_resource_processor: true,
libs: ["SystemUIPluginLib"],
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
index cfe3be0..3ac6422 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
@@ -14,8 +14,6 @@
package com.android.systemui.plugins.qs;
-import android.content.Context;
-
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -30,18 +28,7 @@
public interface QSFactory extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_QS_FACTORY";
- int VERSION = 2;
+ int VERSION = 3;
QSTile createTile(String tileSpec);
-
- /**
- * Create a view for a tile.
- *
- * @param context a themed context for inflating the view
- * @param tile the tile for which the view is created
- * @param collapsedView {@code true} if the view will live in QQS and {@code false} otherwise.
- * @return a view for the tile
- */
- QSTileView createTileView(Context context, QSTile tile, boolean collapsedView);
-
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 25f77ea..06e9b10 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -55,8 +55,6 @@
void removeCallback(Callback callback);
void removeCallbacks();
- QSIconView createTileView(Context context);
-
/**
* The tile was clicked.
*
@@ -170,7 +168,6 @@
public boolean dualTarget = false;
public boolean isTransient = false;
public String expandedAccessibilityClassName;
- public SlashState slash;
public boolean handlesLongClick = true;
@Nullable
public Drawable sideViewCustomDrawable;
@@ -214,7 +211,6 @@
|| !Objects.equals(other.state, state)
|| !Objects.equals(other.isTransient, isTransient)
|| !Objects.equals(other.dualTarget, dualTarget)
- || !Objects.equals(other.slash, slash)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|| !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
other.spec = spec;
@@ -230,7 +226,6 @@
other.state = state;
other.dualTarget = dualTarget;
other.isTransient = isTransient;
- other.slash = slash != null ? slash.copy() : null;
other.handlesLongClick = handlesLongClick;
other.sideViewCustomDrawable = sideViewCustomDrawable;
return changed;
@@ -258,7 +253,6 @@
sb.append(",dualTarget=").append(dualTarget);
sb.append(",isTransient=").append(isTransient);
sb.append(",state=").append(state);
- sb.append(",slash=\"").append(slash).append("\"");
sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
return sb.append(']');
}
@@ -302,73 +296,4 @@
return state;
}
}
-
- @ProvidesInterface(version = SignalState.VERSION)
- public static final class SignalState extends BooleanState {
- public static final int VERSION = 1;
- public boolean activityIn;
- public boolean activityOut;
- public boolean isOverlayIconWide;
- public int overlayIconId;
-
- @Override
- public boolean copyTo(State other) {
- final SignalState o = (SignalState) other;
- final boolean changed = o.activityIn != activityIn
- || o.activityOut != activityOut
- || o.isOverlayIconWide != isOverlayIconWide
- || o.overlayIconId != overlayIconId;
- o.activityIn = activityIn;
- o.activityOut = activityOut;
- o.isOverlayIconWide = isOverlayIconWide;
- o.overlayIconId = overlayIconId;
- return super.copyTo(other) || changed;
- }
-
- @Override
- protected StringBuilder toStringBuilder() {
- final StringBuilder rt = super.toStringBuilder();
- rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
- rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
- return rt;
- }
-
- @Override
- public State copy() {
- SignalState state = new SignalState();
- copyTo(state);
- return state;
- }
- }
-
- @ProvidesInterface(version = SlashState.VERSION)
- public static class SlashState {
- public static final int VERSION = 2;
-
- public boolean isSlashed;
- public float rotation;
-
- @Override
- public String toString() {
- return "isSlashed=" + isSlashed + ",rotation=" + rotation;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) return false;
- try {
- return (((SlashState) o).rotation == rotation)
- && (((SlashState) o).isSlashed == isSlashed);
- } catch (ClassCastException e) {
- return false;
- }
- }
-
- public SlashState copy() {
- SlashState state = new SlashState();
- state.rotation = rotation;
- state.isSlashed = isSlashed;
- return state;
- }
- }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index b7088d5..02085b9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -124,13 +124,6 @@
default void onDozeAmountChanged(float linear, float eased) {}
/**
- * Callback to be notified when the fullscreen or immersive state changes.
- *
- * @param isFullscreen if any of the system bar is hidden by the focused window.
- */
- default void onFullscreenStateChanged(boolean isFullscreen) {}
-
- /**
* Callback to be notified when the pulsing state changes
*/
default void onPulsingChanged(boolean pulsing) {}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index e2d8891..be1e655 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -1,8 +1,5 @@
-keep class com.android.systemui.VendorServices
-# the `#inject` methods are accessed via reflection to work on ContentProviders
--keepclassmembers class * extends com.android.systemui.dagger.SysUIComponent { void inject(***); }
-
# Needed to ensure callback field references are kept in their respective
# owning classes when the downstream callback registrars only store weak refs.
# TODO(b/264686688): Handle these cases with more targeted annotations.
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
new file mode 100644
index 0000000..173d57b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This file is needed when flag lockscreen.enable_landscape is on
+ Required for landscape lockscreen on small screens. -->
+<com.android.keyguard.KeyguardPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_password_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:gravity="bottom">
+
+ <!-- Layout here is visually identical to the previous keyguard_password_view.
+ I.E., 'constraints here effectively the same as the previous linear layout' -->
+ <androidx.constraintlayout.motion.widget.MotionLayout
+ android:id="@+id/password_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxWidth="@dimen/keyguard_security_width"
+ android:layout_gravity="center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ androidprv:layoutDescription="@xml/keyguard_password_scene">
+
+ <!-- Guideline need to align password right of centre,
+ when on small screen landscape layout -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/password_center_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ androidprv:layout_constraintGuide_percent="0.5" />
+
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ androidprv:layout_constraintTop_toTopOf="parent">
+
+ <include layout="@layout/keyguard_bouncer_message_area" />
+
+ <com.android.systemui.bouncer.ui.BouncerMessageView
+ android:id="@+id/bouncer_message_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/passwordEntry_container"
+ android:layout_width="280dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:theme="?attr/passwordStyle"
+ androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintHorizontal_bias="0.5"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/keyguard_bouncer_message_container"
+ androidprv:layout_constraintVertical_bias="0.7777">
+
+ <EditText
+ android:id="@+id/passwordEntry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/keyguard_accessibility_password"
+ android:gravity="center_horizontal"
+ android:imeOptions="flagForceAscii|actionDone"
+ android:inputType="textPassword"
+ android:maxLength="500"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textCursorDrawable="@null"
+ android:textSize="16sp"
+ android:textStyle="normal" />
+
+ <ImageView
+ android:id="@+id/switch_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginBottom="12dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:contentDescription="@string/accessibility_ime_switch_button"
+ android:padding="8dip"
+ android:src="@drawable/ic_lockscreen_ime"
+ android:tint="?android:attr/textColorPrimary"
+ android:visibility="gone" />
+ </FrameLayout>
+
+ <include
+ android:id="@+id/keyguard_selector_fade_container"
+ layout="@layout/keyguard_eca"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:orientation="vertical"
+ androidprv:layout_constraintBottom_toBottomOf="parent" />
+
+ </androidx.constraintlayout.motion.widget.MotionLayout>
+
+</com.android.keyguard.KeyguardPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
new file mode 100644
index 0000000..b562d7b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This file is needed when flag lockscreen.enable_landscape is on
+ Required for landscape lockscreen on small screens. -->
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pattern_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <!-- Layout here is visually identical to the previous keyguard_pattern_view.
+ I.E., 'constraints here effectively the same as the previous linear layout' -->
+ <androidx.constraintlayout.motion.widget.MotionLayout
+ android:id="@+id/pattern_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ android:maxWidth = "@dimen/biometric_auth_pattern_view_max_size"
+ androidprv:layoutDescription="@xml/keyguard_pattern_scene">
+
+ <!-- Guideline need to align pattern right of centre,
+ when on small screen landscape layout -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pattern_center_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ androidprv:layout_constraintGuide_percent="0.5" />
+
+ <LinearLayout
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ androidprv:layout_constraintTop_toTopOf="parent">
+
+ <include layout="@layout/keyguard_bouncer_message_area" />
+
+ <com.android.systemui.bouncer.ui.BouncerMessageView
+ android:id="@+id/bouncer_message_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pattern_top_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ androidprv:layout_constraintGuide_percent="0" />
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginBottom="8dp"
+ androidprv:layout_constraintVertical_bias="1.0"
+ androidprv:layout_constraintDimensionRatio="1.0"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintVertical_chainStyle="packed"
+ androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"/>
+
+ <include
+ android:id="@+id/keyguard_selector_fade_container"
+ layout="@layout/keyguard_eca"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:orientation="vertical"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/lockPatternView" />
+
+ </androidx.constraintlayout.motion.widget.MotionLayout>
+
+</com.android.keyguard.KeyguardPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index 6835d59..6c79d5a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -34,6 +34,7 @@
android:id="@+id/pin_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:maxWidth="@dimen/keyguard_security_width"
android:clipChildren="false"
android:clipToPadding="false"
android:layoutDirection="ltr"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 8c81733..d1067a9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -105,6 +105,13 @@
screen. -->
<item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item>
+ <!-- Proportion of the screen height to use to set the maximum height of the bouncer to when
+ the device is in the DEVICE_POSTURE_HALF_OPENED posture.
+
+ This value is only used when motion layout bouncer is used - when flag
+ landscape.enable_lockscreen (b/293252410) is on -->
+ <item name="motion_layout_half_fold_bouncer_height_ratio" type="dimen" format="float">0.55</item>
+
<!-- The actual amount of translation that is applied to the security when it animates from one
side of the screen to the other in one-handed or user switcher mode. Note that it will
always translate from the side of the screen to the other (it will "jump" closer to the
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml
new file mode 100644
index 0000000..092e10d
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<MotionScene
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:motion="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto">
+
+ <Transition
+ motion:constraintSetStart="@id/single_constraints"
+ motion:constraintSetEnd="@+id/split_constraints"
+ motion:duration="0"
+ motion:autoTransition="none" />
+
+ <!-- No changes to default layout -->
+ <ConstraintSet android:id="@+id/single_constraints" />
+
+ <ConstraintSet android:id="@+id/split_constraints">
+
+ <Constraint
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintEnd_toStartOf="@+id/password_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toTopOf="parent"
+ androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+ androidprv:layout_constraintVertical_chainStyle="spread_inside" />
+ <Constraint
+ android:id="@+id/passwordEntry_container"
+ android:layout_width="280dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintVertical_bias="0.5"
+ androidprv:layout_constraintHorizontal_bias="0.5"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintStart_toStartOf="@+id/password_center_guideline"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintTop_toTopOf="parent"/>
+ <Constraint
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toStartOf="@+id/password_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/keyguard_bouncer_message_container" />
+
+ </ConstraintSet>
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
new file mode 100644
index 0000000..751d6d8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<MotionScene
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:motion="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto">
+
+ <Transition
+ motion:constraintSetStart="@id/single_constraints"
+ motion:constraintSetEnd="@+id/split_constraints"
+ motion:duration="0"
+ motion:autoTransition="none"/>
+
+ <Transition
+ motion:constraintSetStart="@id/single_constraints"
+ motion:constraintSetEnd="@+id/half_folded_single_constraints"
+ motion:duration="@integer/material_motion_duration_short_1"
+ motion:autoTransition="none"/>
+
+ <!-- No changes to default layout -->
+ <ConstraintSet android:id="@+id/single_constraints"/>
+
+ <ConstraintSet android:id="@+id/half_folded_single_constraints">
+
+ <Constraint
+ android:id="@+id/pattern_top_guideline"
+ androidprv:layout_constraintGuide_percent=
+ "@dimen/motion_layout_half_fold_bouncer_height_ratio"/>
+
+ <Constraint
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="0dp"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:orientation="vertical"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/flow1"/>
+
+ </ConstraintSet>
+
+ <ConstraintSet android:id="@+id/split_constraints">
+
+ <Constraint
+ android:id="@+id/keyguard_bouncer_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toTopOf="parent" />
+ <Constraint
+ android:id="@+id/lockPatternView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:layout_constraintDimensionRatio="1.0"
+ androidprv:layout_constraintVertical_bias="0.5"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintStart_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintTop_toTopOf="parent"
+ android:layout_marginBottom="0dp" />
+ <Constraint
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin" />
+
+ </ConstraintSet>
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
index 44af9ef..cc498f4 100644
--- a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
@@ -26,11 +26,33 @@
motion:constraintSetStart="@id/single_constraints"
motion:constraintSetEnd="@+id/split_constraints"
motion:duration="0"
- motion:autoTransition="none">
- </Transition>
+ motion:autoTransition="none" />
- <ConstraintSet android:id="@+id/single_constraints">
- <!-- No changes to default layout -->
+ <Transition
+ motion:constraintSetStart="@id/single_constraints"
+ motion:constraintSetEnd="@+id/half_folded_single_constraints"
+ motion:duration="@integer/material_motion_duration_short_1" />
+
+ <!-- No changes to default layout -->
+ <ConstraintSet android:id="@+id/single_constraints"/>
+
+ <ConstraintSet android:id="@+id/half_folded_single_constraints">
+
+ <Constraint
+ android:id="@+id/pin_pad_top_guideline"
+ androidprv:layout_constraintGuide_percent=
+ "@dimen/motion_layout_half_fold_bouncer_height_ratio"/>
+
+ <Constraint
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="0dp"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:orientation="vertical"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@+id/flow1"/>
+
</ConstraintSet>
<ConstraintSet android:id="@+id/split_constraints">
@@ -70,4 +92,5 @@
android:layout_marginTop="@dimen/keyguard_eca_top_margin" />
</ConstraintSet>
+
</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/notification_overlay_color.xml b/packages/SystemUI/res/color/notification_overlay_color.xml
new file mode 100644
index 0000000..c24bff9
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_overlay_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_pressed="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.15" />
+ <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.11" />
+ <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
deleted file mode 100644
index 56223db..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="6.0dp"
- android:height="32dp"
- android:viewportWidth="6.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
deleted file mode 100644
index 12c1a7a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="6.0dp"
- android:height="32dp"
- android:viewportWidth="6.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 61a8e8e..3eaa618 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
<item>
@@ -23,4 +23,9 @@
<solid android:color="?androidprv:attr/colorSurface" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+ <item>
+ <shape>
+ <solid android:color="@color/notification_overlay_color" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
new file mode 100644
index 0000000..6dd44fb
--- /dev/null
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<!-- TODO(b/298124674) remove this root -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/bluetooth_device_list_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginBottom="4dp">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/bluetooth_device_row"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:paddingEnd="24dp"
+ android:paddingStart="20dp"
+ android:baselineAligned="false">
+
+ <ImageView
+ android:id="@+id/bluetooth_device_icon"
+ android:contentDescription="@string/accessibility_bluetooth_device_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_gravity="center_vertical" />
+
+ <View
+ android:id="@+id/bluetooth_device"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintTop_toTopOf="@+id/bluetooth_device_name"
+ app:layout_constraintBottom_toBottomOf="@+id/bluetooth_device_summary"
+ app:layout_constraintStart_toStartOf="@+id/bluetooth_device_name"
+ app:layout_constraintEnd_toEndOf="@+id/bluetooth_device_name" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:id="@+id/bluetooth_device_name"
+ style="@style/BluetoothTileDialog.DeviceName"
+ android:paddingStart="20dp"
+ android:paddingTop="10dp"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
+ app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+ app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary"
+ android:gravity="center_vertical"
+ android:textSize="14sp" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:id="@+id/bluetooth_device_summary"
+ style="@style/BluetoothTileDialog.DeviceSummary"
+ android:paddingStart="20dp"
+ android:paddingBottom="10dp"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
+ app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/gear_icon"
+ android:src="@drawable/ic_settings_24dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
+ android:layout_width="0dp"
+ android:layout_height="24dp"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintWidth_percent="0.3"
+ android:gravity="center_vertical"
+ android:paddingStart="10dp" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
new file mode 100644
index 0000000..16aeb95
--- /dev/null
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/root"
+ style="@style/Widget.SliceView.Panel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/bluetooth_tile_dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="24dp"
+ android:ellipsize="end"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="@string/quick_settings_bluetooth_label"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:textSize="24sp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/bluetooth_tile_dialog_subtitle"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/bluetooth_tile_dialog_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="@dimen/bluetooth_dialog_layout_margin"
+ android:ellipsize="end"
+ android:gravity="center_vertical|center_horizontal"
+ android:maxLines="1"
+ android:text="@string/quick_settings_bluetooth_tile_subtitle"
+ android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/bluetooth_toggle_title"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" />
+
+ <TextView
+ android:id="@+id/bluetooth_toggle_title"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_width="0dp"
+ android:layout_height="64dp"
+ android:gravity="center_vertical"
+ android:layout_marginTop="4dp"
+ android:text="@string/turn_on_bluetooth"
+ android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+ android:textSize="16sp"
+ app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" />
+
+ <Switch
+ android:id="@+id/bluetooth_toggle"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:gravity="center|center_vertical"
+ android:paddingEnd="24dp"
+ android:layout_marginTop="10dp"
+ android:contentDescription="@string/turn_on_bluetooth"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:theme="@style/MainSwitch.Settingslib"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:track="@drawable/settingslib_track_selector"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" />
+
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/pair_new_device_layout_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+
+ <ImageView
+ android:id="@+id/ic_add"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginStart="36dp"
+ android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_add"
+ app:layout_constraintBottom_toTopOf="@id/device_list"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/pair_new_device_text"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:gravity="center_vertical"
+ android:layout_marginStart="0dp"
+ android:paddingStart="20dp"
+ android:text="@string/pair_new_bluetooth_devices"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ app:layout_constraintBottom_toTopOf="@id/device_list"
+ app:layout_constraintStart_toEndOf="@+id/ic_add"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/device_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:nestedScrollingEnabled="false"
+ android:overScrollMode="never"
+ android:scrollbars="vertical"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+ app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
+
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/see_all_layout_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="ic_arrow,see_all_text" />
+
+ <ImageView
+ android:id="@+id/ic_arrow"
+ android:layout_marginStart="36dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:importantForAccessibility="no"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_arrow_forward"
+ app:layout_constraintBottom_toTopOf="@+id/done_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/see_all_text"
+ app:layout_constraintTop_toBottomOf="@id/device_list" />
+
+ <TextView
+ android:id="@+id/see_all_text"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:gravity="center_vertical"
+ android:layout_marginStart="0dp"
+ android:paddingStart="20dp"
+ android:text="@string/see_all_bluetooth_devices"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ app:layout_constraintBottom_toTopOf="@+id/done_button"
+ app:layout_constraintStart_toEndOf="@+id/ic_arrow"
+ app:layout_constraintTop_toBottomOf="@id/device_list"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <Button
+ android:id="@+id/done_button"
+ style="@style/Widget.Dialog.Button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:clickable="true"
+ android:ellipsize="end"
+ android:focusable="true"
+ android:maxLines="1"
+ android:text="@string/inline_done_button"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/see_all_text" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 60a78d6..beb481a 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -130,6 +130,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right|center_vertical"
+ android:gravity="center_vertical"
android:paddingStart="@dimen/hover_system_icons_container_padding_start"
android:paddingEnd="@dimen/hover_system_icons_container_padding_end"
android:paddingTop="@dimen/hover_system_icons_container_padding_top"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index f6ce70d..dc560bf 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -150,19 +150,6 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <!-- Temporary entrypoint for the partial screensharing used for teamfooding -->
- <!-- TODO(b/236838395) remove this and use redesigned dialog -->
- <TextView
- android:id="@+id/button_app"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:layout_marginEnd="8dp"
- android:text="App"
- style="@style/Widget.Dialog.Button.BorderButton" />
-
<TextView
android:id="@+id/button_start"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 356b36f..fa7f9cf 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -30,12 +30,14 @@
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:duplicateParentState="true"/>
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundDimmed"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:duplicateParentState="true"/>
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expanded"
diff --git a/packages/SystemUI/res/values-land/bools.xml b/packages/SystemUI/res/values-land/bools.xml
new file mode 100644
index 0000000..c112edc
--- /dev/null
+++ b/packages/SystemUI/res/values-land/bools.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+ <!-- Only use small clock on lockscreen.
+ True here because only small clock used on small devices in landscape -->
+ <bool name="force_small_clock_on_lockscreen">true</bool>
+
+ <!-- True when small screen (<sw600dp) is landscape. -->
+ <bool name="is_small_screen_landscape">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/bools.xml b/packages/SystemUI/res/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..36926a2
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/bools.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+ <!-- Only use small clock on lockscreen.
+ False here because large clock is allowed on large devices in landscape -->
+ <bool name="force_small_clock_on_lockscreen">false</bool>
+
+ <!-- True when small screen (<sw600dp) is landscape. -->
+ <bool name="is_small_screen_landscape">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
index 91d3a88..234c6df 100644
--- a/packages/SystemUI/res/values/bools.xml
+++ b/packages/SystemUI/res/values/bools.xml
@@ -59,4 +59,11 @@
True here so bouncers constraints are updated when rotating on small screens -->
<bool name="update_bouncer_constraints">true</bool>
+
+ <!-- Only use small clock on lockscreen.
+ False here because large clock used by default, unless otherwise specified -->
+ <bool name="force_small_clock_on_lockscreen">false</bool>
+
+ <!-- True when small screen (<sw600dp) is landscape. -->
+ <bool name="is_small_screen_landscape">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ae3138e..88726af 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -873,12 +873,6 @@
is displayed in the upper left corner. -->
<dimen name="roaming_icon_start_padding">2sp</dimen>
- <!-- Extra padding between the mobile data type icon and the strength indicator when the data
- type icon is wide for the tile in quick settings. -->
- <dimen name="wide_type_icon_start_padding_qs">3dp</dimen>
-
- <dimen name="signal_indicator_to_icon_frame_spacing">3dp</dimen>
-
<!-- Starting margin before the signal cluster -->
<!-- Padding between signal cluster and battery icon -->
@@ -1649,6 +1643,11 @@
<!-- Radius of switch track -->
<dimen name="settingslib_switch_track_radius">35dp</dimen>
+ <!-- Bluetooth dialog related dimensions -->
+ <dimen name="bluetooth_dialog_layout_margin">16dp</dimen>
+ <!-- The height of the bluetooth device in bluetooth dialog. -->
+ <dimen name="bluetooth_dialog_device_height">72dp</dimen>
+
<!-- Height percentage of the parent container occupied by the communal view -->
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index bd251bd..21696fe 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -50,7 +50,6 @@
<item type="id" name="x_animator_tag_start_value"/>
<item type="id" name="y_animator_tag_start_value"/>
<item type="id" name="qs_icon_tag"/>
- <item type="id" name="qs_slash_tag"/>
<item type="id" name="scrim"/>
<item type="id" name="scrim_alpha_start"/>
<item type="id" name="scrim_alpha_end"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 29c9767..5860806 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -459,7 +459,12 @@
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
- <!-- Content description of the bluetooth icon when connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+
+ <!-- Content description of the bluetooth device icon. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_icon">Bluetooth device icon</string>
+
+ <!-- Content description of the bluetooth device settings gear icon. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_settings_gear">Bluetooth device settings gear</string>
<!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_unknown">Battery percentage unknown.</string>
@@ -621,6 +626,19 @@
<!-- QuickSettings: Bluetooth (Off) [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Bluetooth detail panel, text when there are no items [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_detail_empty_text">No paired devices available</string>
+ <!-- QuickSettings: Bluetooth dialog subtitle [CHAR LIMIT=NONE]-->
+ <string name="quick_settings_bluetooth_tile_subtitle">Tap a device to connect</string>
+ <!-- QuickSettings: Bluetooth dialog pair new devices [CHAR LIMIT=NONE]-->
+ <string name="pair_new_bluetooth_devices">Pair new device</string>
+ <!-- QuickSettings: Bluetooth dialog see all devices [CHAR LIMIT=NONE]-->
+ <string name="see_all_bluetooth_devices">See all</string>
+ <!-- QuickSettings: Bluetooth dialog turn on Bluetooth [CHAR LIMIT=NONE]-->
+ <string name="turn_on_bluetooth">Use Bluetooth</string>
+ <!-- QuickSettings: Bluetooth dialog device connected default summary [CHAR LIMIT=NONE]-->
+ <string name="quick_settings_bluetooth_device_connected">Connected</string>
+ <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]-->
+ <string name="quick_settings_bluetooth_device_saved">Saved</string>
+
<!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
<!-- QuickSettings: Bluetooth secondary label for an audio device being connected [CHAR LIMIT=20]-->
@@ -3086,7 +3104,7 @@
configured. This is shown as part of a dialog that explains to the user why they cannot select
this shortcut for their lock screen right now. [CHAR LIMIT=NONE].
-->
- <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device is available</string>
+ <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device or device panel are available</string>
<!---
Explains that the notes app is not available. This is shown as part of a dialog that explains to
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6991b96..084cb88 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
@@ -54,10 +54,10 @@
</style>
<style name="TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
- parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
+ parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
<style name="TextAppearance.StatusBar.Expanded.ChargingInfo"
- parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
+ parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
<style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
<item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
@@ -645,7 +645,7 @@
</style>
<style name="TextAppearance.HeadsUpStatusBarText"
- parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
+ parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
<style name="TextAppearance.QSEdit" >
@@ -699,7 +699,7 @@
</style>
<style name="MediaPlayer.SessionAction"
- parent="@android:style/Widget.Material.Button.Borderless.Small">
+ parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">?android:attr/textColorPrimary</item>
<item name="android:paddingTop">12dp</item>
@@ -929,12 +929,13 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
- <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
- <item name="android:windowFullscreen">false</item>
- <item name="android:windowIsFloating">false</item>
- <item name="android:windowBackground">@color/controls_task_view_bg</item>
- <item name="android:backgroundDimEnabled">false</item>
- <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ <style name="Theme.SystemUI.Dialog.Control.DetailPanel"
+ parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="android:windowFullscreen">false</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowBackground">@color/controls_task_view_bg</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
<style name="Control" />
@@ -1034,17 +1035,17 @@
<style name="Wallet" />
<style name="Wallet.TextAppearance">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:singleLine">true</item>
- <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textSize">14sp</item>
</style>
<style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
- <item name="android:itemBackground">@color/material_dynamic_neutral20</item>
- <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
- <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
+ <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+ <item name="android:itemBackground">@color/material_dynamic_neutral20</item>
+ <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
+ <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
<style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
@@ -1169,7 +1170,7 @@
</style>
<style name="TrimmedHorizontalProgressBar"
- parent="android:Widget.Material.ProgressBar.Horizontal">
+ parent="android:Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminateDrawable">
@drawable/progress_indeterminate_horizontal_material_trimmed
</item>
@@ -1254,6 +1255,36 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
+ <style name="BluetoothTileDialog">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center_vertical|start</item>
+ </style>
+
+ <style name="BluetoothTileDialog.Device">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">88dp</item>
+ <item name="android:layout_gravity">center_vertical|start</item>
+ <item name="android:layout_marginStart">@dimen/bluetooth_dialog_layout_margin</item>
+ <item name="android:layout_marginEnd">@dimen/bluetooth_dialog_layout_margin</item>
+ <item name="android:paddingStart">22dp</item>
+ <item name="android:paddingEnd">22dp</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:focusable">true</item>
+ <item name="android:clickable">true</item>
+ </style>
+
+ <style name="BluetoothTileDialog.DeviceName">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
+ </style>
+
+ <style name="BluetoothTileDialog.DeviceSummary">
+ <item name="android:ellipsize">end</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:textAppearance">@style/TextAppearance.Dialog.Body.Message</item>
+ </style>
+
<style name="BroadcastDialog">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
@@ -1397,7 +1428,7 @@
</style>
<style name="PermissionGrantButtonTop"
- parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">332dp</item>
<item name="android:layout_height">56dp</item>
<item name="android:layout_marginTop">2dp</item>
@@ -1406,7 +1437,7 @@
</style>
<style name="PermissionGrantButtonBottom"
- parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">332dp</item>
<item name="android:layout_height">56dp</item>
<item name="android:layout_marginTop">2dp</item>
@@ -1465,14 +1496,14 @@
</style>
<style name="TextAppearance.PrivacyDialog.Item.Title"
- parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
</style>
<style name="TextAppearance.PrivacyDialog.Item.Summary"
- parent="@android:style/TextAppearance.DeviceDefault.Small">
+ parent="@android:style/TextAppearance.DeviceDefault.Small">
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
@@ -1481,4 +1512,4 @@
<style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog">
<item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item>
</style>
-</resources>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 0415341..9c368eb 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -38,6 +38,7 @@
android_library {
name: "SystemUISharedLib",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 7719e95..f9f2c63 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -73,7 +73,7 @@
mComponentName = componentName;
mPluginFactory = pluginFactory;
mPlugin = plugin;
- mTag = TAG + mComponentName.toShortString()
+ mTag = TAG + "[" + mComponentName.getShortClassName() + "]"
+ '@' + Integer.toHexString(hashCode());
if (mPlugin != null) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 0c2341f..3cb5bc7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -59,9 +59,8 @@
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
- int taskbarSize, boolean isLargeScreen,
- int currentRotation, boolean isRtl) {
+ int canvasWidth, int canvasHeight, boolean isLargeScreen, int currentRotation,
+ boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 8200e5c..9059230 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.rotation;
import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
@@ -37,6 +38,8 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -67,6 +70,7 @@
import java.io.PrintWriter;
import java.util.Optional;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
@@ -119,6 +123,8 @@
private final int mIconCwStart0ResId;
@DrawableRes
private final int mIconCwStart90ResId;
+ /** Defaults to mainExecutor if not set via {@link #setBgExecutor(Executor)}. */
+ private Executor mBgExecutor;
@DrawableRes
private int mIconResId;
@@ -178,6 +184,8 @@
mAccessibilityManager = AccessibilityManager.getInstance(context);
mTaskStackListener = new TaskStackListenerImpl();
mWindowRotationProvider = windowRotationProvider;
+
+ mBgExecutor = context.getMainExecutor();
}
public void setRotationButton(RotationButton rotationButton,
@@ -193,6 +201,10 @@
return mContext;
}
+ public void setBgExecutor(Executor bgExecutor) {
+ mBgExecutor = bgExecutor;
+ }
+
/**
* Called during Taskbar initialization.
*/
@@ -219,8 +231,11 @@
mListenersRegistered = true;
- updateDockedState(mContext.registerReceiver(mDockedReceiver,
- new IntentFilter(Intent.ACTION_DOCK_EVENT)));
+ mBgExecutor.execute(() -> {
+ final Intent intent = mContext.registerReceiver(mDockedReceiver,
+ new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ mContext.getMainExecutor().execute(() -> updateDockedState(intent));
+ });
if (registerRotationWatcher) {
try {
@@ -246,11 +261,13 @@
mListenersRegistered = false;
- try {
- mContext.unregisterReceiver(mDockedReceiver);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Docked receiver already unregistered", e);
- }
+ mBgExecutor.execute(() -> {
+ try {
+ mContext.unregisterReceiver(mDockedReceiver);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Docked receiver already unregistered", e);
+ }
+ });
if (mRotationWatcherRegistered) {
try {
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index 87a9b0f..ae282c7 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -24,7 +24,7 @@
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.Locale;
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index b52ee01..873c3d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -39,7 +39,7 @@
import com.android.keyguard.logging.CarrierTextManagerLogger;
import com.android.settingslib.WirelessUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index 006974c..d677a15 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -25,7 +25,7 @@
import android.view.View
import android.view.WindowManager
import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.R
+import com.android.systemui.res.R
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java b/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java
index 225bebe..b2ffccc 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java
@@ -21,7 +21,7 @@
import android.view.MotionEvent;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class EmergencyCarrierArea extends AlphaOptimizedLinearLayout {
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
index 788a66d..6a170a5 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
@@ -20,10 +20,11 @@
import android.os.Build
import android.os.PowerManager
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.util.settings.GlobalSettings
import java.io.PrintWriter
import java.util.stream.Collectors
@@ -38,6 +39,7 @@
private val defaultTriggerFaceAuthOnWakeUpFrom: Set<Int> =
resources.getIntArray(R.array.config_face_auth_wake_up_triggers).toSet()
private val triggerFaceAuthOnWakeUpFrom: Set<Int>
+ private val wakeSleepReasonsToTriggerFaceAuth: Set<WakeSleepReason>
init {
triggerFaceAuthOnWakeUpFrom =
@@ -52,6 +54,14 @@
} else {
defaultTriggerFaceAuthOnWakeUpFrom
}
+ wakeSleepReasonsToTriggerFaceAuth =
+ triggerFaceAuthOnWakeUpFrom
+ .map {
+ val enumVal = WakeSleepReason.fromPowerManagerWakeReason(it)
+ assert(enumVal != WakeSleepReason.OTHER)
+ enumVal
+ }
+ .toSet()
dumpManager.registerDumpable(this)
}
@@ -59,6 +69,9 @@
return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
}
+ fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
+ wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("FaceWakeUpTriggers:")
for (pmWakeReason in triggerFaceAuthOnWakeUpFrom) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index c6d1471..dfeb1f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -25,7 +25,7 @@
import androidx.annotation.CallSuper;
import com.android.internal.widget.LockscreenCredential;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Base class for PIN and password unlock screens.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index e3f9de1..167bd59 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -34,7 +34,7 @@
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java
index a78c293..f7db48a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java
@@ -23,7 +23,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Replaces fancy colons with regular colons. Only works on TextViews.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 1703b30..ba8e427 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -22,7 +22,7 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.ClockController;
@@ -112,6 +112,8 @@
private int mWeatherClockSmartspaceTranslateY = 0;
private int mDrawAlpha = 255;
+ private int mStatusBarHeight = 0;
+
/**
* Maintain state so that a newly connected plugin can be initialized.
*/
@@ -154,6 +156,8 @@
R.dimen.weather_clock_smartspace_translateX);
mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize(
R.dimen.weather_clock_smartspace_translateY);
+ mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
updateStatusArea(/* animate= */false);
}
@@ -299,6 +303,8 @@
mStatusAreaAnim = null;
View in, out;
+ // statusAreaYTranslation uses for the translation for both mStatusArea and mSmallClockFrame
+ // statusAreaClockTranslateY only uses for mStatusArea
float statusAreaYTranslation, statusAreaClockScale = 1f;
float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f;
float clockInYTranslation, clockOutYTranslation;
@@ -313,10 +319,21 @@
&& mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) {
statusAreaClockScale = mWeatherClockSmartspaceScaling;
statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX;
- statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY - mSmartspaceTop;
if (mSplitShadeCentered) {
statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER;
}
+
+ // On large weather clock,
+ // top padding for time is status bar height from top of the screen.
+ // On small one,
+ // it's screenOffsetYPadding (translationY for KeyguardStatusView),
+ // Cause smartspace is positioned according to the smallClockFrame
+ // we need to translate the difference between bottom of large clock and small clock
+ // Also, we need to counter offset the empty date weather view, mSmartspaceTop
+ // mWeatherClockSmartspaceTranslateY is only for Felix
+ statusAreaClockTranslateY = mStatusBarHeight - 0.6F * mSmallClockFrame.getHeight()
+ - mSmartspaceTop - screenOffsetYPadding
+ - statusAreaYTranslation + mWeatherClockSmartspaceTranslateY;
}
clockInYTranslation = 0;
clockOutYTranslation = 0; // Small clock translation is handled with statusArea
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 11f9589..29414ea 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -37,7 +37,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -527,7 +527,7 @@
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
- com.android.systemui.R.id.left_aligned_notification_icon_container);
+ com.android.systemui.res.R.id.left_aligned_notification_icon_container);
mNotificationIconAreaController.setupAodIcons(nic);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1c5a575..9c015fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -39,7 +39,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index ccbb0c5..fc4e122 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -32,7 +32,7 @@
import android.view.WindowManager;
import android.widget.Button;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/***
* This button is used by the device with embedded SIM card to disable current carrier to unlock
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index bb799fc..d7019b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -27,7 +27,7 @@
override var userId: Int = 0,
override var listening: Boolean = false,
// keep sorted
- var allowedDisplayState: Boolean = false,
+ var allowedDisplayStateWhileAwake: Boolean = false,
var alternateBouncerShowing: Boolean = false,
var authInterruptActive: Boolean = false,
var biometricSettingEnabledForUser: Boolean = false,
@@ -58,7 +58,7 @@
userId.toString(),
listening.toString(),
// keep sorted
- allowedDisplayState.toString(),
+ allowedDisplayStateWhileAwake.toString(),
alternateBouncerShowing.toString(),
authInterruptActive.toString(),
biometricSettingEnabledForUser.toString(),
@@ -98,7 +98,7 @@
userId = model.userId
listening = model.listening
// keep sorted
- allowedDisplayState = model.allowedDisplayState
+ allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake
alternateBouncerShowing = model.alternateBouncerShowing
authInterruptActive = model.authInterruptActive
biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
@@ -143,7 +143,7 @@
"userId",
"listening",
// keep sorted
- "allowedDisplayState",
+ "allowedDisplayStateWhileAwake",
"alternateBouncerShowing",
"authInterruptActive",
"biometricSettingEnabledForUser",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 98f082f..38c8495 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -28,7 +28,7 @@
import androidx.annotation.Nullable;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* A Base class for all Keyguard password/pattern/pin related inputs.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 8738d33..29ce18c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +29,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.ui.BouncerMessageView;
import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder;
@@ -257,10 +255,8 @@
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
- mFeatureFlags);
+ mDevicePostureController, mFeatureFlags);
} else if (keyguardInputView instanceof KeyguardPINView) {
- ((KeyguardPINView) keyguardInputView).setIsLockScreenLandscapeEnabled(
- mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index fc66527..7e44866 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -27,7 +27,7 @@
import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index bb3e759..622b67f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -17,6 +17,8 @@
package com.android.keyguard;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
@@ -39,7 +41,7 @@
import com.android.app.animation.Interpolators;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
@@ -51,6 +53,8 @@
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
@Nullable private MotionLayout mContainerMotionLayout;
+ // TODO (b/293252410) - usage of mContainerConstraintLayout should be removed
+ // when the flag is enabled/removed
@Nullable private ConstraintLayout mContainerConstraintLayout;
private int mDisappearYTranslation;
private View[][] mViews;
@@ -58,7 +62,7 @@
private int mYTransOffset;
private View mBouncerMessageArea;
private boolean mAlreadyUsingSplitBouncer = false;
- private boolean mIsLockScreenLandscapeEnabled = false;
+ private boolean mIsSmallLockScreenLandscapeEnabled = false;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public static final long ANIMATION_DURATION = 650;
@@ -86,12 +90,12 @@
/** Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
* enabled, instead of constraint layout (old bouncer implementation) */
public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) {
- mIsLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled;
+ mIsSmallLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled;
findContainerLayout();
}
private void findContainerLayout() {
- if (mIsLockScreenLandscapeEnabled) {
+ if (mIsSmallLockScreenLandscapeEnabled) {
mContainerMotionLayout = findViewById(R.id.pin_container);
} else {
mContainerConstraintLayout = findViewById(R.id.pin_container);
@@ -105,21 +109,21 @@
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- if (mLastDevicePosture != posture) {
- mLastDevicePosture = posture;
+ if (mLastDevicePosture == posture) return;
+ mLastDevicePosture = posture;
- if (mIsLockScreenLandscapeEnabled) {
- boolean useSplitBouncerAfterFold =
- mLastDevicePosture == DEVICE_POSTURE_CLOSED
- && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ if (mIsSmallLockScreenLandscapeEnabled) {
+ boolean useSplitBouncerAfterFold =
+ mLastDevicePosture == DEVICE_POSTURE_CLOSED
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
- if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
- updateConstraints(useSplitBouncerAfterFold);
- }
+ if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+ updateConstraints(useSplitBouncerAfterFold);
}
-
- updateMargins();
}
+
+ updateMargins();
}
@Override
@@ -165,21 +169,45 @@
}
}
+ if (mIsSmallLockScreenLandscapeEnabled) {
+ updateHalfFoldedConstraints();
+ } else {
+ updateHalfFoldedGuideline();
+ }
+ }
+
+ private void updateHalfFoldedConstraints() {
+ // Update the constraints based on the device posture...
+ if (mAlreadyUsingSplitBouncer) return;
+
+ boolean shouldCollapsePin =
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
+ && mContext.getResources().getConfiguration().orientation
+ == ORIENTATION_PORTRAIT;
+
+ int expectedMotionLayoutState = shouldCollapsePin
+ ? R.id.half_folded_single_constraints
+ : R.id.single_constraints;
+
+ transitionToMotionLayoutState(expectedMotionLayoutState);
+ }
+
+ // TODO (b/293252410) - this method can be removed when the flag is enabled/removed
+ private void updateHalfFoldedGuideline() {
// Update the guideline based on the device posture...
float halfOpenPercentage =
mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
- if (mIsLockScreenLandscapeEnabled) {
- ConstraintSet cs = mContainerMotionLayout.getConstraintSet(R.id.single_constraints);
- cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
- mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
- cs.applyTo(mContainerMotionLayout);
- } else {
- ConstraintSet cs = new ConstraintSet();
- cs.clone(mContainerConstraintLayout);
- cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
- mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
- cs.applyTo(mContainerConstraintLayout);
+ ConstraintSet cs = new ConstraintSet();
+ cs.clone(mContainerConstraintLayout);
+ cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainerConstraintLayout);
+ }
+
+ private void transitionToMotionLayoutState(int state) {
+ if (mContainerMotionLayout.getCurrentState() != state) {
+ mContainerMotionLayout.transitionToState(state);
}
}
@@ -188,12 +216,24 @@
* Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled. */
@Override
protected void updateConstraints(boolean useSplitBouncer) {
+ if (!mIsSmallLockScreenLandscapeEnabled) return;
+
mAlreadyUsingSplitBouncer = useSplitBouncer;
+
if (useSplitBouncer) {
mContainerMotionLayout.jumpToState(R.id.split_constraints);
mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
} else {
- mContainerMotionLayout.jumpToState(R.id.single_constraints);
+ boolean useHalfFoldedConstraints =
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
+ && mContext.getResources().getConfiguration().orientation
+ == ORIENTATION_PORTRAIT;
+
+ if (useHalfFoldedConstraints) {
+ mContainerMotionLayout.jumpToState(R.id.half_folded_single_constraints);
+ } else {
+ mContainerMotionLayout.jumpToState(R.id.single_constraints);
+ }
mContainerMotionLayout.setMaxWidth(getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_width));
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 59ee0d8..51c0676 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.WindowInsets.Type.ime;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
@@ -27,10 +28,13 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Insets;
@@ -46,12 +50,16 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.constraintlayout.motion.widget.MotionLayout;
import com.android.app.animation.Interpolators;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.DejankUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+
+
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
@@ -68,10 +76,14 @@
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
-
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
private DisappearAnimationListener mDisappearAnimationListener;
+ @Nullable private MotionLayout mContainerMotionLayout;
+ private boolean mAlreadyUsingSplitBouncer = false;
+ private boolean mIsLockScreenLandscapeEnabled = false;
+ @DevicePostureController.DevicePostureInt
+ private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
private static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
private static final int[] ENABLE_STATE_SET = {android.R.attr.state_enabled};
@@ -89,6 +101,21 @@
context, android.R.interpolator.fast_out_linear_in);
}
+ /**
+ * Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
+ * enabled
+ */
+ public void setIsLockScreenLandscapeEnabled() {
+ mIsLockScreenLandscapeEnabled = true;
+ findContainerLayout();
+ }
+
+ private void findContainerLayout() {
+ if (mIsLockScreenLandscapeEnabled) {
+ mContainerMotionLayout = findViewById(R.id.password_container);
+ }
+ }
+
@Override
protected void resetState() {
}
@@ -124,6 +151,35 @@
}
}
+ void onDevicePostureChanged(@DevicePostureController.DevicePostureInt int posture) {
+ if (mLastDevicePosture == posture) return;
+ mLastDevicePosture = posture;
+
+ if (mIsLockScreenLandscapeEnabled) {
+ boolean useSplitBouncerAfterFold =
+ mLastDevicePosture == DEVICE_POSTURE_CLOSED
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
+
+ if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+ updateConstraints(useSplitBouncerAfterFold);
+ }
+ }
+
+ }
+
+ @Override
+ protected void updateConstraints(boolean useSplitBouncer) {
+ mAlreadyUsingSplitBouncer = useSplitBouncer;
+ if (useSplitBouncer) {
+ mContainerMotionLayout.jumpToState(R.id.split_constraints);
+ mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
+ } else {
+ mContainerMotionLayout.jumpToState(R.id.single_constraints);
+ mContainerMotionLayout.setMaxWidth(getResources()
+ .getDimensionPixelSize(R.dimen.keyguard_security_width));
+ }
+ }
@Override
protected void onFinishInflate() {
@@ -131,6 +187,11 @@
mPasswordEntry = findViewById(getPasswordTextViewId());
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
+
+ // EditText cursor can fail screenshot tests, so disable it when testing
+ if (ActivityManager.isRunningInTestHarness()) {
+ mPasswordEntry.setCursorVisible(false);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 5dbd014..959cf6f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+
import android.content.res.Resources;
import android.os.UserHandle;
import android.text.Editable;
@@ -37,10 +39,11 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
@@ -49,8 +52,10 @@
extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
-
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private final DevicePostureController mPostureController;
+ private final DevicePostureController.Callback mPostureCallback = posture ->
+ mView.onDevicePostureChanged(posture);
private final InputMethodManager mInputMethodManager;
private final DelayableExecutor mMainExecutor;
private final KeyguardViewController mKeyguardViewController;
@@ -106,14 +111,19 @@
@Main Resources resources,
FalsingCollector falsingCollector,
KeyguardViewController keyguardViewController,
+ DevicePostureController postureController,
FeatureFlags featureFlags) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
emergencyButtonController, featureFlags);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
+ mPostureController = postureController;
mMainExecutor = mainExecutor;
mKeyguardViewController = keyguardViewController;
+ if (featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
+ view.setIsLockScreenLandscapeEnabled();
+ }
mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
@@ -127,6 +137,9 @@
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ mView.onDevicePostureChanged(mPostureController.getDevicePosture());
+ mPostureController.addCallback(mPostureCallback);
+
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
@@ -164,6 +177,7 @@
protected void onViewDetached() {
super.onViewDetached();
mPasswordEntry.setOnEditorActionListener(null);
+ mPostureController.removeCallback(mPostureCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 2bdf46e..5c206e9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,9 +15,14 @@
*/
package com.android.keyguard;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -29,6 +34,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
@@ -37,7 +43,7 @@
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
public class KeyguardPatternView extends KeyguardInputView
@@ -75,7 +81,12 @@
BouncerKeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
- private ConstraintLayout mContainer;
+ @Nullable private MotionLayout mContainerMotionLayout;
+ // TODO (b/293252410) - usage of mContainerConstraintLayout should be removed
+ // when the flag is enabled/removed
+ @Nullable private ConstraintLayout mContainerConstraintLayout;
+ private boolean mAlreadyUsingSplitBouncer = false;
+ private boolean mIsSmallLockScreenLandscapeEnabled = false;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPatternView(Context context) {
@@ -98,29 +109,117 @@
mContext, android.R.interpolator.fast_out_linear_in));
}
+ /**
+ * Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
+ * enabled, instead of constraint layout (old bouncer implementation)
+ */
+ public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) {
+ mIsSmallLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled;
+ findContainerLayout();
+ }
+
+ private void findContainerLayout() {
+ if (mIsSmallLockScreenLandscapeEnabled) {
+ mContainerMotionLayout = findViewById(R.id.pattern_container);
+ } else {
+ mContainerConstraintLayout = findViewById(R.id.pattern_container);
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
updateMargins();
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- if (mLastDevicePosture != posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture == posture) return;
+ mLastDevicePosture = posture;
+
+ if (mIsSmallLockScreenLandscapeEnabled) {
+ boolean useSplitBouncerAfterFold =
+ mLastDevicePosture == DEVICE_POSTURE_CLOSED
+ && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+ && getResources().getBoolean(R.bool.update_bouncer_constraints);
+
+ if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+ updateConstraints(useSplitBouncerAfterFold);
+ }
}
+
+ updateMargins();
}
private void updateMargins() {
+ if (mIsSmallLockScreenLandscapeEnabled) {
+ updateHalfFoldedConstraints();
+ } else {
+ updateHalfFoldedGuideline();
+ }
+ }
+
+ private void updateHalfFoldedConstraints() {
+ // Update the constraints based on the device posture...
+ if (mAlreadyUsingSplitBouncer) return;
+
+ boolean shouldCollapsePattern =
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
+ && mContext.getResources().getConfiguration().orientation
+ == ORIENTATION_PORTRAIT;
+
+ int expectedMotionLayoutState = shouldCollapsePattern
+ ? R.id.half_folded_single_constraints
+ : R.id.single_constraints;
+
+ transitionToMotionLayoutState(expectedMotionLayoutState);
+ }
+
+ // TODO (b/293252410) - this method can be removed when the flag is enabled/removed
+ private void updateHalfFoldedGuideline() {
// Update the guideline based on the device posture...
float halfOpenPercentage =
mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
ConstraintSet cs = new ConstraintSet();
- cs.clone(mContainer);
+ cs.clone(mContainerConstraintLayout);
cs.setGuidelinePercent(R.id.pattern_top_guideline,
- mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
- ? halfOpenPercentage : 0.0f);
- cs.applyTo(mContainer);
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainerConstraintLayout);
+ }
+
+ private void transitionToMotionLayoutState(int state) {
+ if (mContainerMotionLayout.getCurrentState() != state) {
+ mContainerMotionLayout.transitionToState(state);
+ }
+ }
+
+ /**
+ * Updates the keyguard view's constraints (single or split constraints).
+ * Split constraints are only used for small landscape screens.
+ * Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled.
+ */
+ @Override
+ protected void updateConstraints(boolean useSplitBouncer) {
+ if (!mIsSmallLockScreenLandscapeEnabled) return;
+
+ mAlreadyUsingSplitBouncer = useSplitBouncer;
+
+ if (useSplitBouncer) {
+ mContainerMotionLayout.jumpToState(R.id.split_constraints);
+ mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
+ } else {
+ boolean useHalfFoldedConstraints =
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
+ && mContext.getResources().getConfiguration().orientation
+ == ORIENTATION_PORTRAIT;
+
+ if (useHalfFoldedConstraints) {
+ mContainerMotionLayout.jumpToState(R.id.half_folded_single_constraints);
+ } else {
+ mContainerMotionLayout.jumpToState(R.id.single_constraints);
+ }
+ mContainerMotionLayout.setMaxWidth(getResources()
+ .getDimensionPixelSize(R.dimen.biometric_auth_pattern_view_max_size));
+ }
}
@Override
@@ -130,7 +229,6 @@
mLockPatternView = findViewById(R.id.lockPatternView);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
- mContainer = findViewById(R.id.pattern_container);
}
@Override
@@ -209,7 +307,7 @@
getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR));
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
- ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
+ ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -220,7 +318,7 @@
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
(long) (200 * durationMultiplier),
- - mDisappearAnimationUtils.getStartTranslation() * 3,
+ -mDisappearAnimationUtils.getStartTranslation() * 3,
false /* appearing */,
mDisappearAnimationUtils.getInterpolator(),
null /* finishRunnable */);
@@ -229,9 +327,16 @@
}
private void enableClipping(boolean enable) {
- setClipChildren(enable);
- mContainer.setClipToPadding(enable);
- mContainer.setClipChildren(enable);
+ if (mContainerConstraintLayout != null) {
+ setClipChildren(enable);
+ mContainerConstraintLayout.setClipToPadding(enable);
+ mContainerConstraintLayout.setClipChildren(enable);
+ }
+ if (mContainerMotionLayout != null) {
+ setClipChildren(enable);
+ mContainerMotionLayout.setClipToPadding(enable);
+ mContainerMotionLayout.setClipChildren(enable);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index a30b447..714ba81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
@@ -35,7 +36,7 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -205,6 +206,8 @@
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
+ view.setIsLockScreenLandscapeEnabled(
+ featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 38e5dc5..9d6d033 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -39,7 +39,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.widget.LockscreenCredential;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 31cbdde..aacf866 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,7 +25,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 574a059..9a78868 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -16,12 +16,14 @@
package com.android.keyguard;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+
import android.view.View;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -61,6 +63,7 @@
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
mFeatureFlags = featureFlags;
+ view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mBackspaceKey = view.findViewById(R.id.delete_button);
mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 3f3efe9..f7a4d47 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -97,7 +97,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shade.TouchLogger;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index e9dd08c..625c1de 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -68,7 +68,7 @@
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor;
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate;
import com.android.systemui.biometrics.SideFpsController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 21960e2..83b1a2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -67,42 +67,6 @@
int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
/**
- * Prompt that is shown when there is an incorrect primary authentication input.
- */
- int PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT = 9;
-
- /**
- * Prompt that is shown when there is an incorrect face biometric input.
- */
- int PROMPT_REASON_INCORRECT_FACE_INPUT = 10;
-
- /**
- * Prompt that is shown when there is an incorrect fingerprint biometric input.
- */
- int PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT = 11;
-
- /**
- * Prompt that is shown when face authentication is in locked out state.
- */
- int PROMPT_REASON_FACE_LOCKED_OUT = 12;
-
- /**
- * Prompt that is shown when fingerprint authentication is in locked out state.
- */
- int PROMPT_REASON_FINGERPRINT_LOCKED_OUT = 13;
-
- /**
- * Default prompt that is shown on the bouncer.
- */
- int PROMPT_REASON_DEFAULT = 14;
-
- /**
- * Prompt that is shown when primary authentication is in locked out state after too many
- * attempts
- */
- int PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT = 15;
-
- /**
* Strong auth is required because the device has just booted because of an automatic
* mainline update.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 74f9006..44db01e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -30,7 +30,7 @@
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 4cc90c2..ec2999f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import android.util.Log;
@@ -29,7 +30,7 @@
import com.android.keyguard.KeyguardInputViewController.Factory;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.util.ViewController;
@@ -138,12 +139,12 @@
onViewInflatedListener.onViewInflated(childController);
// Single bouncer constrains are default
- if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)
- &&
- getResources().getBoolean(R.bool.update_bouncer_constraints)) {
+ if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
boolean useSplitBouncer =
- getResources().getConfiguration().orientation
+ getResources().getBoolean(R.bool.update_bouncer_constraints)
+ && getResources().getConfiguration().orientation
== ORIENTATION_LANDSCAPE;
+
updateConstraints(useSplitBouncer);
}
}
@@ -154,9 +155,9 @@
private int getLayoutIdFor(SecurityMode securityMode) {
// TODO (b/297863911, b/297864907) - implement motion layout for other bouncers
switch (securityMode) {
- case Pattern: return R.layout.keyguard_pattern_view;
+ case Pattern: return R.layout.keyguard_pattern_motion_layout;
case PIN: return R.layout.keyguard_pin_motion_layout;
- case Password: return R.layout.keyguard_password_view;
+ case Password: return R.layout.keyguard_password_motion_layout;
case SimPin: return R.layout.keyguard_sim_pin_view;
case SimPuk: return R.layout.keyguard_sim_puk_view;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
index e1c060f..db191c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -28,7 +28,7 @@
import android.view.animation.AnimationUtils
import com.android.app.animation.Interpolators
import com.android.internal.R.interpolator.fast_out_extra_slow_in
-import com.android.systemui.R
+import com.android.systemui.res.R
/** Animates constraint layout changes for the security view. */
class KeyguardSecurityViewTransition : Transition() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
index 7c8d91f..392abf2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -21,7 +21,7 @@
import android.widget.ImageView
import androidx.core.graphics.drawable.DrawableCompat
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.EMERGENCY_BUTTON
abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) :
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 9d17015..bacd0c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -20,7 +20,7 @@
import android.content.res.Configuration;
import android.util.AttributeSet;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Displays a PIN pad for unlocking.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 1fc88ab..8717a53 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -39,7 +39,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -144,7 +144,7 @@
mView.resetPasswordText(true /* animate */, true /* announce */);
getKeyguardSecurityCallback().userActivity();
mMessageAreaController.setMessage(
- com.android.systemui.R.string.kg_invalid_sim_pin_hint);
+ com.android.systemui.res.R.string.kg_invalid_sim_pin_hint);
return;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 5f45fe3..151ca8a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -22,7 +22,7 @@
import android.util.AttributeSet;
import android.util.Log;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 49d786f..248b7af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -36,7 +36,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -139,25 +139,25 @@
if (mState == ENTER_PUK) {
if (checkPuk()) {
mState = ENTER_PIN;
- msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
+ msg = com.android.systemui.res.R.string.kg_puk_enter_pin_hint;
} else {
- msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
+ msg = com.android.systemui.res.R.string.kg_invalid_sim_puk_hint;
}
} else if (mState == ENTER_PIN) {
if (checkPin()) {
mState = CONFIRM_PIN;
- msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
+ msg = com.android.systemui.res.R.string.kg_enter_confirm_pin_hint;
} else {
- msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
+ msg = com.android.systemui.res.R.string.kg_invalid_sim_pin_hint;
}
} else if (mState == CONFIRM_PIN) {
if (confirmPin()) {
mState = DONE;
- msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
+ msg = com.android.systemui.res.R.string.keyguard_sim_unlock_progress_dialog_message;
updateSim();
} else {
mState = ENTER_PIN; // try again?
- msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
+ msg = com.android.systemui.res.R.string.kg_invalid_confirm_pin_hint;
}
}
mView.resetPasswordText(true /* animate */, true /* announce */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index b4f124a..7b5325d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -48,7 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt
index e7da2b9..11b1a7d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt
@@ -4,7 +4,7 @@
import android.util.AttributeSet
import android.util.FloatProperty
import android.widget.LinearLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.AnimatableProperty
class KeyguardStatusAreaView(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 5774e42..d848602 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -29,7 +29,7 @@
import android.view.ViewPropertyAnimator;
import android.widget.GridLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.statusbar.CrossFadeHelper;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index c314586..8d0d299 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -49,7 +49,7 @@
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index ca64ae0..7170be61 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e4bbd3a..51308b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -35,6 +35,7 @@
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
+
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -74,6 +75,7 @@
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -151,7 +153,7 @@
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -160,6 +162,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -202,7 +205,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -342,15 +344,16 @@
return;
}
- if (mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
+ if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE
+ && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
== Display.STATE_OFF) {
- mAllowedDisplayStateForFaceAuth = false;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = false;
updateFaceListeningState(
BIOMETRIC_ACTION_STOP,
FACE_AUTH_DISPLAY_OFF
);
} else {
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
}
}
};
@@ -374,7 +377,7 @@
private boolean mOccludingAppRequestingFp;
private boolean mOccludingAppRequestingFace;
private boolean mSecureCameraLaunched;
- private boolean mAllowedDisplayStateForFaceAuth = true;
+ private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
@VisibleForTesting
protected boolean mTelephonyCapable;
private boolean mAllowFingerprintOnCurrentOccludingActivity;
@@ -422,6 +425,7 @@
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
+ private final WakefulnessLifecycle mWakefulness;
private final DisplayTracker mDisplayTracker;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@@ -2207,7 +2211,7 @@
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) {
FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
@@ -2363,7 +2367,8 @@
Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
- DisplayTracker displayTracker) {
+ DisplayTracker displayTracker,
+ WakefulnessLifecycle wakefulness) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2410,6 +2415,7 @@
.collect(Collectors.toSet());
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
+ mWakefulness = wakefulness;
mDisplayTracker = displayTracker;
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
@@ -2439,7 +2445,7 @@
handleDevicePolicyManagerStateChanged(msg.arg1);
break;
case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj);
+ handleUserSwitching(msg.arg1, (Runnable) msg.obj);
break;
case MSG_USER_SWITCH_COMPLETE:
handleUserSwitchComplete(msg.arg1);
@@ -2740,10 +2746,12 @@
}
private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
+
@Override
- public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) {
+ public void onUserChanging(int newUser, @NonNull Context userContext,
+ @NonNull Runnable resultCallback) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
- newUser, 0, latch));
+ newUser, 0, resultCallback));
}
@Override
@@ -3212,7 +3220,7 @@
&& faceAndFpNotAuthenticated
&& !mGoingToSleep
&& isPostureAllowedForFaceAuth
- && mAllowedDisplayStateForFaceAuth;
+ && mAllowedDisplayStateWhileAwakeForFaceAuth;
// Aggregate relevant fields for debug logging.
logListenerModelData(
@@ -3220,7 +3228,7 @@
System.currentTimeMillis(),
user,
shouldListen,
- mAllowedDisplayStateForFaceAuth,
+ mAllowedDisplayStateWhileAwakeForFaceAuth,
mAlternateBouncerShowing,
mAuthInterruptActive,
biometricEnabledForUser,
@@ -3574,7 +3582,7 @@
* Handle {@link #MSG_USER_SWITCHING}
*/
@VisibleForTesting
- void handleUserSwitching(int userId, CountDownLatch latch) {
+ void handleUserSwitching(int userId, Runnable resultCallback) {
mLogger.logUserSwitching(userId, "from UserTracker");
Assert.isMainThread();
clearBiometricRecognized();
@@ -3588,7 +3596,7 @@
cb.onUserSwitching(userId);
}
}
- latch.countDown();
+ resultCallback.run();
}
/**
@@ -4201,7 +4209,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
mAllowFingerprintOnCurrentOccludingActivity =
- standardTask.topActivity != null
+ standardTask != null && standardTask.topActivity != null
&& !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
&& mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
standardTask.topActivity.getPackageName())
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
index 5f3ba72..04e8a97 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
@@ -20,7 +20,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.LinearLayout
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Custom View for the multi-user switcher pull-down menu anchor
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
index b793fd2..f746459 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -25,7 +25,7 @@
import android.widget.ListPopupWindow;
import android.widget.ListView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.FalsingManager;
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index c522881..1d2d77f 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -37,7 +37,7 @@
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 214b122..165c4bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -56,7 +56,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 5c2f3b3..76f93e1 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -30,7 +30,7 @@
import androidx.annotation.Nullable;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Similar to the {@link NumPadKey}, but displays an image.
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 466d154..871d57d 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -36,7 +36,7 @@
import androidx.annotation.Nullable;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Viewgroup for the bouncer numpad button, specifically for digits.
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 9a2ffe0..85f8b48 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -37,7 +37,7 @@
import android.view.animation.Interpolator;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeAdapter.kt b/packages/SystemUI/src/com/android/keyguard/PinShapeAdapter.kt
index 4496dc31..8d1bbb2 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeAdapter.kt
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeAdapter.kt
@@ -17,7 +17,7 @@
package com.android.keyguard
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import kotlin.random.Random
class PinShapeAdapter {
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
index 7c129b4..5e9eed9 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java
@@ -29,7 +29,7 @@
import androidx.core.graphics.drawable.DrawableCompat;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* This class contains implementation for methods that will be used when user has set a
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
index 5f33ef9..8c987e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
@@ -40,7 +40,7 @@
import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* This class contains implementation for methods that will be used when user has set a
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 83fc278..9716d98 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -20,7 +20,7 @@
import android.content.res.Resources;
import android.view.LayoutInflater;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 893239b..afea224 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -25,7 +25,7 @@
import com.android.keyguard.KeyguardSecurityContainer;
import com.android.keyguard.KeyguardSecurityViewFlipper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index 8762769..37600da 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -17,7 +17,7 @@
package com.android.keyguard.dagger;
import com.android.keyguard.CarrierText;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarLocation;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
index b8841ed..7575f0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
@@ -19,7 +19,7 @@
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardSliceView;
import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import dagger.Module;
import dagger.Provides;
diff --git a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
index 4c16d41c..72fa2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
@@ -24,6 +24,8 @@
import android.view.View;
import android.widget.FrameLayout;
+import com.android.systemui.res.R;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index eee705d..2d2ebe9 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -22,8 +22,8 @@
import android.graphics.RectF
import android.hardware.camera2.CameraManager
import android.util.PathParser
+import com.android.systemui.res.R
import java.util.concurrent.Executor
-
import kotlin.math.roundToInt
/**
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
index 5e96379..2010a70 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
@@ -43,6 +43,8 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.android.systemui.res.R;
+
import java.util.HashSet;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
index 2b8d3e0..22d0bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.view.ContextThemeWrapper
import com.android.settingslib.Utils
+import com.android.systemui.res.R
/**
* A color blender for `Theme.SystemUI` and other themes.
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 7c377d2..086713f 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -36,6 +36,8 @@
import androidx.core.animation.AnimatorListenerAdapter;
import androidx.core.animation.ObjectAnimator;
+import com.android.systemui.res.R;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index e6d5719..ab431d0 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -37,6 +37,7 @@
import android.widget.ListView;
import android.widget.TextView;
+import com.android.systemui.res.R;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.internal.logging.MetricsLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 104b71f..4541384 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
+import com.android.systemui.res.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
index 7a6b1c3..161cb43 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
@@ -26,6 +26,7 @@
import android.os.UserHandle;
import android.provider.Settings;
+import com.android.systemui.res.R;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java b/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java
index d631cf3..0643d02 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import com.android.systemui.res.R;
import com.android.settingslib.Utils;
public class HardwareBgDrawable extends LayerDrawable {
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index 1bb0329..b9e412c 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -20,6 +20,7 @@
import android.util.Log;
import android.view.View;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.ViewProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index ff395da..4af2c74 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -84,6 +84,7 @@
import com.android.systemui.decor.ScreenDecorCommand;
import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
@@ -334,7 +335,7 @@
mThreadFactory = threadFactory;
mDotFactory = dotFactory;
mFaceScanningFactory = faceScanningFactory;
- mFaceScanningViewId = com.android.systemui.R.id.face_scanning_anim;
+ mFaceScanningViewId = com.android.systemui.res.R.id.face_scanning_anim;
mLogger = logger;
mAuthController = authController;
}
@@ -1195,7 +1196,7 @@
if (faceScanningOverlay != null) {
faceScanningOverlay.setFaceScanningAnimColor(
Utils.getColorAttrDefaultColor(faceScanningOverlay.getContext(),
- com.android.systemui.R.attr.wallpaperTextColorAccent));
+ com.android.systemui.res.R.attr.wallpaperTextColorAccent));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 6f99a24..67012cb 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -35,6 +35,8 @@
import android.widget.CheckBox;
import android.widget.TextView;
+import com.android.systemui.res.R;
+
public class SlicePermissionActivity extends Activity implements OnClickListener,
OnDismissListener {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3ca74ac..954129e 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -51,6 +51,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.animation.PhysicsAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
index 12108b0..b15aaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -25,6 +25,9 @@
import android.util.Log
import androidx.core.app.AppComponentFactory
import com.android.systemui.dagger.ContextComponentHelper
+import com.android.systemui.dagger.SysUIComponent
+import com.android.tools.r8.keepanno.annotations.KeepTarget
+import com.android.tools.r8.keepanno.annotations.UsesReflection
import java.lang.reflect.InvocationTargetException
import java.util.concurrent.ExecutionException
import javax.inject.Inject
@@ -88,6 +91,7 @@
return app
}
+ @UsesReflection(KeepTarget(extendsClassConstant = SysUIComponent::class, methodName = "inject"))
override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
val contentProvider = super.instantiateProviderCompat(cl, className)
if (contentProvider is ContextInitializer) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 38298cf..bf44517 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -37,6 +37,7 @@
import android.view.ThreadedRenderer;
import android.view.View;
+import com.android.systemui.res.R;
import com.android.internal.protolog.common.ProtoLog;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 2b468cf..872b005 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -22,6 +22,7 @@
import android.os.HandlerThread;
import android.util.Log;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 6cf9eff..76c2282 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -33,6 +33,7 @@
import com.android.systemui.dump.LogBufferEulogizer;
import com.android.systemui.dump.LogBufferFreezer;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
+import com.android.systemui.res.R;
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
import com.android.systemui.statusbar.policy.BatteryStateNotifier;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index f817c3c..84f1395 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -46,7 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.Collections;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index 859e183..ee7781d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -73,7 +73,7 @@
context.getDisplay(),
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
null);
- mContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
+ mContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
mDisplayId = mContext.getDisplayId();
mConfiguration = new Configuration(mContext.getResources().getConfiguration());
mSettingsControllerCallback = settingsControllerCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
index 6de3e93..443441f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
@@ -31,7 +31,7 @@
import android.view.View;
import android.view.WindowManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Contains a movable control UI to manipulate mirrored window's position, size and scale. The
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
index ed6fbec..bc469ee 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
@@ -28,7 +28,7 @@
import android.view.MotionEvent;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* A basic control to move the mirror window.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 69041d3..59b85d1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -102,7 +102,7 @@
protected WindowMagnificationController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
- windowContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new WindowMagnificationController(
windowContext,
mHandler,
@@ -141,7 +141,7 @@
protected MagnificationSettingsController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
- windowContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new MagnificationSettingsController(
windowContext,
new SfVsyncFrameCallbackProvider(),
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index d061c8e..9dd1454 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -30,7 +30,7 @@
import android.view.animation.AccelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 773241e..c095aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -80,7 +80,7 @@
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 7c11311..c03e403 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -59,7 +59,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index ecfe4ca..83ad3c2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -29,7 +29,7 @@
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipView.java
index 5ec024e..1ff9eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipView.java
@@ -41,7 +41,7 @@
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.recents.TriangleShape;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
index 14517ba..9c22a77 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
@@ -27,7 +27,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* An accessibility item delegate for the individual items of the list view in the
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
index 3e2b06b..bf121fb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
@@ -37,7 +37,7 @@
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index df2c05d..89ce065 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -35,7 +35,7 @@
import androidx.annotation.DimenRes;
import com.android.systemui.Flags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 6ba40d6..b6e8997 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -59,7 +59,7 @@
import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.bubbles.DismissView;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
index 0ef256d..2d2f2956 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
@@ -29,7 +29,7 @@
import android.widget.TextView
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener.ControlUnitType
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index c9801d7..9fd0602 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -54,6 +54,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -84,6 +85,7 @@
private final SystemClock mClock;
private H mBGHandler;
+ private final Executor mBgExecutor;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
private boolean mListening;
@@ -153,6 +155,7 @@
public AppOpsControllerImpl(
Context context,
@Background Looper bgLooper,
+ @Background Executor bgExecutor,
DumpManager dumpManager,
AudioManager audioManager,
IndividualSensorPrivacyController sensorPrivacyController,
@@ -162,6 +165,7 @@
mDispatcher = dispatcher;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mBGHandler = new H(bgLooper);
+ mBgExecutor = bgExecutor;
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
mCallbacksByCode.put(OPS[i], new ArraySet<>());
@@ -184,41 +188,43 @@
@VisibleForTesting
protected void setListening(boolean listening) {
mListening = listening;
- if (listening) {
- // System UI could be restarted while ops are active, so fetch the currently active ops
- // once System UI starts listening again.
- fetchCurrentActiveOps();
+ // Move IPCs to the background.
+ mBgExecutor.execute(() -> {
+ if (listening) {
+ // System UI could be restarted while ops are active, so fetch the currently active
+ // ops once System UI starts listening again -- see b/294104969.
+ fetchCurrentActiveOps();
- mAppOps.startWatchingActive(OPS, this);
- mAppOps.startWatchingNoted(OPS, this);
- mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
- mSensorPrivacyController.addCallback(this);
+ mAppOps.startWatchingActive(OPS, this);
+ mAppOps.startWatchingNoted(OPS, this);
+ mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+ mSensorPrivacyController.addCallback(this);
- mMicMuted = mAudioManager.isMicrophoneMute()
- || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
- mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
+ mMicMuted = mAudioManager.isMicrophoneMute()
+ || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
- mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
- mAudioManager.getActiveRecordingConfigurations()));
- mDispatcher.registerReceiverWithHandler(this,
- new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+ mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
+ mAudioManager.getActiveRecordingConfigurations()));
+ mDispatcher.registerReceiverWithHandler(this,
+ new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+ } else {
+ mAppOps.stopWatchingActive(this);
+ mAppOps.stopWatchingNoted(this);
+ mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+ mSensorPrivacyController.removeCallback(this);
- } else {
- mAppOps.stopWatchingActive(this);
- mAppOps.stopWatchingNoted(this);
- mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
- mSensorPrivacyController.removeCallback(this);
-
- mBGHandler.removeCallbacksAndMessages(null); // null removes all
- mDispatcher.unregisterReceiver(this);
- synchronized (mActiveItems) {
- mActiveItems.clear();
- mRecordingsByUid.clear();
+ mBGHandler.removeCallbacksAndMessages(null); // null removes all
+ mDispatcher.unregisterReceiver(this);
+ synchronized (mActiveItems) {
+ mActiveItems.clear();
+ mRecordingsByUid.clear();
+ }
+ synchronized (mNotedItems) {
+ mNotedItems.clear();
+ }
}
- synchronized (mNotedItems) {
- mNotedItems.clear();
- }
- }
+ });
}
private void fetchCurrentActiveOps() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index d491975..a5c5bec 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -33,7 +33,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Visually discloses that contextual data was provided to an assistant.
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index f26404ca..0e339dd 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -29,7 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -309,8 +309,14 @@
}
int invocationType = args.getInt(INVOCATION_TYPE_KEY);
- return mAssistOverrideInvocationTypes != null && Arrays.stream(
- mAssistOverrideInvocationTypes).anyMatch(override -> override == invocationType);
+ return shouldOverrideAssist(invocationType);
+ }
+
+ /** @return true if the invocation type should be handled by OverviewProxy instead of SysUI. */
+ public boolean shouldOverrideAssist(int invocationType) {
+ return mAssistOverrideInvocationTypes != null
+ && Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
+ override -> override == invocationType);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 1c98099..1ee06cc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -35,7 +35,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.assist.AssistLogger;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.assist.AssistantSessionEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
index 9b441ad..5765f3a 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DisplayUtils.java
@@ -21,7 +21,7 @@
import android.view.Display;
import android.view.Surface;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Utility class for determining screen and corner dimensions.
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
index ac39ed5..4d89231 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
@@ -32,7 +32,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBar;
import com.android.systemui.navigationbar.NavigationBarTransitions;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/PathSpecCornerPathRenderer.java b/packages/SystemUI/src/com/android/systemui/assist/ui/PathSpecCornerPathRenderer.java
index 523378e..fa9676b 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/PathSpecCornerPathRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/PathSpecCornerPathRenderer.java
@@ -23,7 +23,7 @@
import android.util.Log;
import android.util.PathParser;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Parses a path describing rounded corners from a string.
diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt
index b81d7fc..b8de117 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt
@@ -28,7 +28,7 @@
import android.graphics.drawable.DrawableWrapper
import android.util.PathParser
import com.android.settingslib.graph.ThemedBatteryDrawable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT_WITH_SHIELD
import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 6ca1c3d..f25f994 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -47,7 +47,7 @@
import com.android.app.animation.Interpolators;
import com.android.systemui.DualToneHandler;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
index b6f47e9..5b840b5 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
@@ -30,7 +30,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
index ea8f5d3..3f2da5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
@@ -19,7 +19,7 @@
import android.graphics.drawable.Drawable
import android.util.Log
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
private const val TAG = "AuthBiometricFaceIconController"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index fb22c6b..09eabf2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -19,7 +19,7 @@
import android.annotation.RawRes
import android.content.Context
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_ERROR
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 683541b..0ad3848 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -26,7 +26,7 @@
import androidx.annotation.VisibleForTesting
import com.airbnb.lottie.LottieAnimationView
import com.android.settingslib.widget.LottieColorUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATING
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index c7d7fe3..85122ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -63,7 +63,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b752c3b..a64e862 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -627,7 +627,7 @@
int xFpLocation = mCachedDisplayInfo.getNaturalWidth() / 2;
try {
xFpLocation = mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen
+ com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_x);
} catch (Resources.NotFoundException e) {
}
@@ -635,7 +635,7 @@
return new Point(
(int) (xFpLocation * mScaleFactor),
(int) (mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen
+ com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_y)
* mScaleFactor)
);
@@ -815,7 +815,7 @@
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
int[] faceAuthLocation = context.getResources().getIntArray(
- com.android.systemui.R.array.config_face_auth_props);
+ com.android.systemui.res.R.array.config_face_auth_props);
if (faceAuthLocation == null || faceAuthLocation.length < 2) {
mFaceSensorLocationDefault = null;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 97b0617..054bd08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -25,7 +25,7 @@
shadeExpansionCollectorJob =
scope.launch {
// wait for it to emit true once
- shadeInteractorLazy.get().anyExpanding.first { it }
+ shadeInteractorLazy.get().isAnyExpanding.first { it }
onShadeInteraction.run()
}
shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index 167067e..bc0c8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -27,7 +27,7 @@
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateDecelerateInterpolator;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 141983b..c09e68d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -31,7 +31,7 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java
index 3e6508c..2962be8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java
@@ -27,7 +27,7 @@
import android.provider.Settings;
import android.util.Log;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
index dc874d8..d6a4cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
@@ -21,6 +21,8 @@
import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG;
import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -29,8 +31,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.BiometricStateListener;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -39,10 +43,12 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.Optional;
+
import javax.inject.Inject;
/**
@@ -66,6 +72,9 @@
private final Handler mHandler;
private final NotificationManager mNotificationManager;
private final BiometricNotificationBroadcastReceiver mBroadcastReceiver;
+ private final FingerprintReEnrollNotification mFingerprintReEnrollNotification;
+ private final FingerprintManager mFingerprintManager;
+ private final FaceManager mFaceManager;
private NotificationChannel mNotificationChannel;
private boolean mFaceNotificationQueued;
private boolean mFingerprintNotificationQueued;
@@ -102,26 +111,53 @@
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_RE_ENROLL, REENROLL_REQUIRED,
UserHandle.USER_CURRENT);
- } else if (msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_RE_ENROLL
- && biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ }
+ }
+
+ @Override
+ public void onBiometricHelp(int msgId, String helpString,
+ BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && mFingerprintReEnrollNotification.isFingerprintReEnrollRequired(
+ msgId)) {
mFingerprintReenrollRequired = true;
}
}
};
+ private final BiometricStateListener mFaceStateListener = new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mNotificationManager.cancelAsUser(TAG, FACE_NOTIFICATION_ID, UserHandle.CURRENT);
+ }
+ };
+
+ private final BiometricStateListener mFingerprintStateListener = new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mNotificationManager.cancelAsUser(TAG, FINGERPRINT_NOTIFICATION_ID, UserHandle.CURRENT);
+ }
+ };
@Inject
- public BiometricNotificationService(Context context,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardStateController keyguardStateController,
- Handler handler, NotificationManager notificationManager,
- BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver) {
+ public BiometricNotificationService(@NonNull Context context,
+ @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+ @NonNull KeyguardStateController keyguardStateController,
+ @NonNull Handler handler, @NonNull NotificationManager notificationManager,
+ @NonNull BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver,
+ @NonNull Optional<FingerprintReEnrollNotification> fingerprintReEnrollNotification,
+ @Nullable FingerprintManager fingerprintManager,
+ @Nullable FaceManager faceManager) {
mContext = context;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mNotificationManager = notificationManager;
mBroadcastReceiver = biometricNotificationBroadcastReceiver;
+ mFingerprintReEnrollNotification = fingerprintReEnrollNotification.orElse(
+ new FingerprintReEnrollNotificationImpl());
+ mFingerprintManager = fingerprintManager;
+ mFaceManager = faceManager;
}
@Override
@@ -135,12 +171,19 @@
intentFilter.addAction(ACTION_SHOW_FACE_REENROLL_DIALOG);
mContext.registerReceiver(mBroadcastReceiver, intentFilter,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ if (mFingerprintManager != null) {
+ mFingerprintManager.registerBiometricStateListener(mFingerprintStateListener);
+ }
+ if (mFaceManager != null) {
+ mFaceManager.registerBiometricStateListener(mFaceStateListener);
+ }
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_RE_ENROLL, REENROLL_NOT_REQUIRED,
UserHandle.USER_CURRENT);
}
private void queueFaceReenrollNotification() {
+ Log.d(TAG, "Face re-enroll notification queued.");
mFaceNotificationQueued = true;
final String title = mContext.getString(R.string.face_re_enroll_notification_title);
final String content = mContext.getString(
@@ -153,6 +196,7 @@
}
private void queueFingerprintReenrollNotification() {
+ Log.d(TAG, "Fingerprint re-enroll notification queued.");
mFingerprintNotificationQueued = true;
final String title = mContext.getString(R.string.fingerprint_re_enroll_notification_title);
final String content = mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
index a24a47b..bbdcadb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
@@ -22,7 +22,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import com.android.keyguard.FaceAuthApiRequestReason
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 320e362..b015f70 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -20,7 +20,7 @@
import com.android.keyguard.logging.BiometricMessageDeferralLogger
import com.android.keyguard.logging.FaceMessageDeferralLogger
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotification.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotification.java
new file mode 100644
index 0000000..9050f26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotification.java
@@ -0,0 +1,29 @@
+/*
+ * 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.biometrics;
+
+import android.hardware.biometrics.BiometricFingerprintConstants;
+
+/**
+ * Checks if the fingerprint HAL has sent a re-enrollment request.
+ */
+public interface FingerprintReEnrollNotification {
+ //TODO: Remove this class and add a constant in the HAL API instead (b/281841852)
+ /** Returns true if msgId corresponds to FINGERPRINT_ACQUIRED_RE_ENROLL. */
+ boolean isFingerprintReEnrollRequired(
+ @BiometricFingerprintConstants.FingerprintAcquired int msgId);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotificationImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotificationImpl.java
new file mode 100644
index 0000000..1f86bc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintReEnrollNotificationImpl.java
@@ -0,0 +1,29 @@
+/*
+ * 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.biometrics;
+
+import android.hardware.biometrics.BiometricFingerprintConstants;
+
+/**
+ * Checks if the fingerprint HAL has sent a re-enrollment request.
+ */
+public class FingerprintReEnrollNotificationImpl implements FingerprintReEnrollNotification{
+ @Override
+ public boolean isFingerprintReEnrollRequired(int msgId) {
+ return msgId == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_RE_ENROLL;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 017ac60..c1f6259 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -54,7 +54,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.keyguard.KeyguardPINView
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 0264356..46d3c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -580,7 +580,9 @@
+ mOverlay.getRequestId());
return false;
}
- if (mLockscreenShadeTransitionController.getQSDragProgress() != 0f
+
+ if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
+ && !mAlternateBouncerInteractor.isVisibleState())
|| mPrimaryBouncerInteractor.isInTransit()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 941df687..34a0d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,7 +46,7 @@
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 16dc42a..abbfa01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -35,7 +35,7 @@
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Adapter that remeasures an auth dialog view to ensure that it matches the location of a physical
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
index 511b4e3..c16dfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
@@ -24,7 +24,7 @@
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.PathShape
import android.util.PathParser
-import com.android.systemui.R
+import com.android.systemui.res.R
private const val DEFAULT_STROKE_WIDTH = 3f
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
index 5dafa61..0838855 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
@@ -20,7 +20,7 @@
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* View corresponding with udfps_fpm_empty_view.xml
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt
index 8497879..99da660 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt
@@ -20,7 +20,7 @@
import android.os.Bundle
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 84a746c..8ce98a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -27,7 +27,7 @@
import com.android.app.animation.Interpolators
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
index b916810c..36a42f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
@@ -42,7 +42,7 @@
import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 54e6215..6ce6172 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -26,7 +26,7 @@
import android.util.Log
import android.view.MotionEvent
import android.widget.FrameLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.doze.DozeReceiver
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 7a9efcf..c4c52e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -41,6 +41,12 @@
/** Repository for the current state of the display */
interface DisplayStateRepository {
+ /**
+ * Whether or not the direction rotation is applied to get to an application's requested
+ * orientation is reversed.
+ */
+ val isReverseDefaultRotation: Boolean
+
/** Provides the current rear display state. */
val isInRearDisplayMode: StateFlow<Boolean>
@@ -59,6 +65,9 @@
@Main handler: Handler,
@Main mainExecutor: Executor
) : DisplayStateRepository {
+ override val isReverseDefaultRotation =
+ context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
+
override val isInRearDisplayMode: StateFlow<Boolean> =
conflatedCallbackFlow {
val sendRearDisplayStateUpdate = { state: Boolean ->
@@ -94,7 +103,11 @@
private fun getDisplayRotation(): DisplayRotation {
val cachedDisplayInfo = DisplayInfo()
context.display?.getDisplayInfo(cachedDisplayInfo)
- return cachedDisplayInfo.rotation.toDisplayRotation()
+ var rotation = cachedDisplayInfo.rotation
+ if (isReverseDefaultRotation) {
+ rotation = (rotation + 1) % 4
+ }
+ return rotation.toDisplayRotation()
}
override val currentRotation: StateFlow<DisplayRotation> =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
index 2a02667..191533c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -7,7 +7,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.internal.widget.VerifyCredentialResponse
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
index fb246cd..cef0be0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
@@ -29,7 +29,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthBiometricFingerprintIconController;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index a7ecf38..56e3f39 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -10,7 +10,7 @@
import android.view.accessibility.AccessibilityManager
import android.widget.LinearLayout
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 02847c2..c29efc0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -38,7 +38,7 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthBiometricFaceIconController
import com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceIconController
import com.android.systemui.biometrics.AuthBiometricFingerprintIconController
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index b9af031..7e16d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -30,7 +30,7 @@
import androidx.core.view.doOnLayout
import androidx.core.view.isGone
import androidx.lifecycle.lifecycleScope
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.ui.BiometricPromptLayout
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index 996b62e..0ad07ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -12,7 +12,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
import com.android.systemui.biometrics.ui.IPinPad
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
index b692ad3..eff6987 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
@@ -4,7 +4,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.CredentialPatternView
import com.android.systemui.biometrics.ui.CredentialView
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 931946a..ce52e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -8,7 +8,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialPatternView
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
index 906206c..05fdb2f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
@@ -20,7 +20,7 @@
import android.view.KeyEvent
import android.widget.ImeAwareEditText
import com.android.internal.widget.LockscreenCredential
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.IPinPad
import com.android.systemui.biometrics.ui.PinPadClickListener
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
index 188c82b..d28f1dc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics.ui.binder
-import android.view.DisplayInfo
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
@@ -33,14 +32,14 @@
fun bind(view: LottieAnimationView, viewModel: PromptFingerprintIconViewModel) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- val displayInfo = DisplayInfo()
- view.context.display?.getDisplayInfo(displayInfo)
- viewModel.setRotation(displayInfo.rotation)
viewModel.onConfigurationChanged(view.context.resources.configuration)
launch {
viewModel.iconAsset.collect { iconAsset ->
if (iconAsset != -1) {
view.setAnimation(iconAsset)
+ // TODO: must replace call below once non-sfps asset logic and
+ // shouldAnimateIconView logic is migrated to this ViewModel.
+ view.playAnimation()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 6212ef0..03c5c53 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -4,7 +4,7 @@
import android.graphics.drawable.Drawable
import android.text.InputType
import com.android.internal.widget.LockPatternView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.CredentialStatus
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
index b406ea4..dfd3a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
@@ -19,10 +19,10 @@
import android.annotation.RawRes
import android.content.res.Configuration
-import android.view.Surface
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -35,19 +35,21 @@
private val displayStateInteractor: DisplayStateInteractor,
promptSelectorInteractor: PromptSelectorInteractor,
) {
- /** Current device rotation. */
- private var rotation: Int = Surface.ROTATION_0
-
/** Current BiometricPromptLayout.iconView asset. */
val iconAsset: Flow<Int> =
combine(
+ displayStateInteractor.currentRotation,
displayStateInteractor.isFolded,
displayStateInteractor.isInRearDisplayMode,
promptSelectorInteractor.sensorType,
- ) { isFolded: Boolean, isInRearDisplayMode: Boolean, sensorType: FingerprintSensorType ->
+ ) {
+ rotation: DisplayRotation,
+ isFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ sensorType: FingerprintSensorType ->
when (sensorType) {
FingerprintSensorType.POWER_BUTTON ->
- getSideFpsAnimationAsset(isFolded, isInRearDisplayMode)
+ getSideFpsAnimationAsset(rotation, isFolded, isInRearDisplayMode)
// Replace below when non-SFPS iconAsset logic is migrated to this ViewModel
else -> -1
}
@@ -55,11 +57,12 @@
@RawRes
private fun getSideFpsAnimationAsset(
+ rotation: DisplayRotation,
isDeviceFolded: Boolean,
isInRearDisplayMode: Boolean,
): Int =
when (rotation) {
- Surface.ROTATION_90 ->
+ DisplayRotation.ROTATION_90 ->
if (isInRearDisplayMode) {
R.raw.biometricprompt_rear_portrait_reverse_base
} else if (isDeviceFolded) {
@@ -67,7 +70,7 @@
} else {
R.raw.biometricprompt_portrait_base_topleft
}
- Surface.ROTATION_270 ->
+ DisplayRotation.ROTATION_270 ->
if (isInRearDisplayMode) {
R.raw.biometricprompt_rear_portrait_base
} else if (isDeviceFolded) {
@@ -89,8 +92,4 @@
fun onConfigurationChanged(newConfig: Configuration) {
displayStateInteractor.onConfigurationChanged(newConfig)
}
-
- fun setRotation(newRotation: Int) {
- rotation = newRotation
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
index 83e61d6..00e9527 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
@@ -38,7 +38,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.MediaOutputConstants;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
deleted file mode 100644
index 64bf688..0000000
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.factory
-
-import android.annotation.IntDef
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
-import com.android.systemui.R.string.bouncer_face_not_recognized
-import com.android.systemui.R.string.keyguard_enter_password
-import com.android.systemui.R.string.keyguard_enter_pattern
-import com.android.systemui.R.string.keyguard_enter_pin
-import com.android.systemui.R.string.kg_bio_too_many_attempts_password
-import com.android.systemui.R.string.kg_bio_too_many_attempts_pattern
-import com.android.systemui.R.string.kg_bio_too_many_attempts_pin
-import com.android.systemui.R.string.kg_bio_try_again_or_password
-import com.android.systemui.R.string.kg_bio_try_again_or_pattern
-import com.android.systemui.R.string.kg_bio_try_again_or_pin
-import com.android.systemui.R.string.kg_face_locked_out
-import com.android.systemui.R.string.kg_fp_locked_out
-import com.android.systemui.R.string.kg_fp_not_recognized
-import com.android.systemui.R.string.kg_primary_auth_locked_out_password
-import com.android.systemui.R.string.kg_primary_auth_locked_out_pattern
-import com.android.systemui.R.string.kg_primary_auth_locked_out_pin
-import com.android.systemui.R.string.kg_prompt_after_dpm_lock
-import com.android.systemui.R.string.kg_prompt_after_update_password
-import com.android.systemui.R.string.kg_prompt_after_update_pattern
-import com.android.systemui.R.string.kg_prompt_after_update_pin
-import com.android.systemui.R.string.kg_prompt_after_user_lockdown_password
-import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pattern
-import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin
-import com.android.systemui.R.string.kg_prompt_auth_timeout
-import com.android.systemui.R.string.kg_prompt_password_auth_timeout
-import com.android.systemui.R.string.kg_prompt_pattern_auth_timeout
-import com.android.systemui.R.string.kg_prompt_pin_auth_timeout
-import com.android.systemui.R.string.kg_prompt_reason_restart_password
-import com.android.systemui.R.string.kg_prompt_reason_restart_pattern
-import com.android.systemui.R.string.kg_prompt_reason_restart_pin
-import com.android.systemui.R.string.kg_prompt_unattended_update
-import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown
-import com.android.systemui.R.string.kg_trust_agent_disabled
-import com.android.systemui.R.string.kg_unlock_with_password_or_fp
-import com.android.systemui.R.string.kg_unlock_with_pattern_or_fp
-import com.android.systemui.R.string.kg_unlock_with_pin_or_fp
-import com.android.systemui.R.string.kg_wrong_input_try_fp_suggestion
-import com.android.systemui.R.string.kg_wrong_password_try_again
-import com.android.systemui.R.string.kg_wrong_pattern_try_again
-import com.android.systemui.R.string.kg_wrong_pin_try_again
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import javax.inject.Inject
-
-@SysUISingleton
-class BouncerMessageFactory
-@Inject
-constructor(
- private val biometricSettingsRepository: BiometricSettingsRepository,
- private val securityModel: KeyguardSecurityModel,
-) {
-
- fun createFromPromptReason(
- @BouncerPromptReason reason: Int,
- userId: Int,
- secondaryMsgOverride: String? = null
- ): BouncerMessageModel? {
- val pair =
- getBouncerMessage(
- reason,
- securityModel.getSecurityMode(userId),
- biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value
- )
- return pair?.let {
- BouncerMessageModel(
- message = Message(messageResId = pair.first, animate = false),
- secondaryMessage =
- secondaryMsgOverride?.let {
- Message(message = secondaryMsgOverride, animate = false)
- }
- ?: Message(messageResId = pair.second, animate = false)
- )
- }
- }
-
- /**
- * Helper method that provides the relevant bouncer message that should be shown for different
- * scenarios indicated by [reason]. [securityMode] & [fpAuthIsAllowed] parameters are used to
- * provide a more specific message.
- */
- private fun getBouncerMessage(
- @BouncerPromptReason reason: Int,
- securityMode: SecurityMode,
- fpAuthIsAllowed: Boolean = false
- ): Pair<Int, Int>? {
- return when (reason) {
- // Primary auth locked out
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
- // Primary auth required reasons
- PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
- PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
- PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
- PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
- PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
- PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -> authRequiredForMainlineUpdate(securityMode)
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
- PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
- // Non strong auth not available reasons
- PROMPT_REASON_FACE_LOCKED_OUT ->
- if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode)
- else faceLockedOut(securityMode)
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
- if (fpAuthIsAllowed) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
- else nonStrongAuthTimeout(securityMode)
- PROMPT_REASON_TRUSTAGENT_EXPIRED ->
- if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode)
- else trustAgentDisabled(securityMode)
- // Auth incorrect input reasons.
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
- if (fpAuthIsAllowed) incorrectSecurityInputWithFingerprint(securityMode)
- else incorrectSecurityInput(securityMode)
- PROMPT_REASON_INCORRECT_FACE_INPUT ->
- if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode)
- else incorrectFaceInput(securityMode)
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
- // Default message
- PROMPT_REASON_DEFAULT ->
- if (fpAuthIsAllowed) defaultMessageWithFingerprint(securityMode)
- else defaultMessage(securityMode)
- else -> null
- }
- }
-
- fun emptyMessage(): BouncerMessageModel =
- BouncerMessageModel(Message(message = ""), Message(message = ""))
-}
-
-@Retention(AnnotationRetention.SOURCE)
-@IntDef(
- PROMPT_REASON_TIMEOUT,
- PROMPT_REASON_DEVICE_ADMIN,
- PROMPT_REASON_USER_REQUEST,
- PROMPT_REASON_AFTER_LOCKOUT,
- PROMPT_REASON_PREPARE_FOR_UPDATE,
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT,
- PROMPT_REASON_TRUSTAGENT_EXPIRED,
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- PROMPT_REASON_INCORRECT_FACE_INPUT,
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT,
- PROMPT_REASON_FACE_LOCKED_OUT,
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
- PROMPT_REASON_DEFAULT,
- PROMPT_REASON_NONE,
- PROMPT_REASON_RESTART,
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
- PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE,
-)
-annotation class BouncerPromptReason
-
-private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
- SecurityMode.Password -> Pair(keyguard_enter_password, 0)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
- SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
- SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
- SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
- SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
- SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
- SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
- SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
- SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized)
- else -> Pair(0, 0)
- }
-}
-
-private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
- SecurityMode.Password ->
- Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun faceLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_fp_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled)
- else -> Pair(0, 0)
- }
-}
-
-private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled)
- else -> Pair(0, 0)
- }
-}
-
-private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
- SecurityMode.Password ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password)
- SecurityMode.PIN ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin)
- else -> Pair(0, 0)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
index 97c1bdb..094dc0a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
@@ -16,315 +16,26 @@
package com.android.systemui.bouncer.data.repository
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.biometrics.BiometricSourceType.FACE
-import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
/** Provide different sources of messages that needs to be shown on the bouncer. */
interface BouncerMessageRepository {
- /**
- * Messages that are shown in response to the incorrect security attempts on the bouncer and
- * primary authentication method being locked out, along with countdown messages before primary
- * auth is active again.
- */
- val primaryAuthMessage: Flow<BouncerMessageModel?>
+ val bouncerMessage: Flow<BouncerMessageModel>
- /**
- * Help messages that are shown to the user on how to successfully perform authentication using
- * face.
- */
- val faceAcquisitionMessage: Flow<BouncerMessageModel?>
-
- /**
- * Help messages that are shown to the user on how to successfully perform authentication using
- * fingerprint.
- */
- val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?>
-
- /** Custom message that is displayed when the bouncer is being shown to launch an app. */
- val customMessage: Flow<BouncerMessageModel?>
-
- /**
- * Messages that are shown in response to biometric authentication attempts through face or
- * fingerprint.
- */
- val biometricAuthMessage: Flow<BouncerMessageModel?>
-
- /** Messages that are shown when certain auth flags are set. */
- val authFlagsMessage: Flow<BouncerMessageModel?>
-
- /** Messages that are show after biometrics are locked out temporarily or permanently */
- val biometricLockedOutMessage: Flow<BouncerMessageModel?>
-
- /** Set the value for [primaryAuthMessage] */
- fun setPrimaryAuthMessage(value: BouncerMessageModel?)
-
- /** Set the value for [faceAcquisitionMessage] */
- fun setFaceAcquisitionMessage(value: BouncerMessageModel?)
- /** Set the value for [fingerprintAcquisitionMessage] */
- fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?)
-
- /** Set the value for [customMessage] */
- fun setCustomMessage(value: BouncerMessageModel?)
-
- /**
- * Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage],
- * [fingerprintAcquisitionMessage] & [customMessage]
- */
- fun clearMessage()
+ fun setMessage(message: BouncerMessageModel)
}
-private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
-private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
-
@SysUISingleton
-class BouncerMessageRepositoryImpl
-@Inject
-constructor(
- trustRepository: TrustRepository,
- biometricSettingsRepository: BiometricSettingsRepository,
- updateMonitor: KeyguardUpdateMonitor,
- private val bouncerMessageFactory: BouncerMessageFactory,
- private val userRepository: UserRepository,
- private val systemPropertiesHelper: SystemPropertiesHelper,
- fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
-) : BouncerMessageRepository {
+class BouncerMessageRepositoryImpl @Inject constructor() : BouncerMessageRepository {
- private val isAnyBiometricsEnabledAndEnrolled =
- or(
- biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
- biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
- )
+ private val _bouncerMessage = MutableStateFlow(BouncerMessageModel())
+ override val bouncerMessage: Flow<BouncerMessageModel> = _bouncerMessage
- private val wasRebootedForMainlineUpdate
- get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
-
- private val authFlagsBasedPromptReason: Flow<Int> =
- combine(
- biometricSettingsRepository.authenticationFlags,
- trustRepository.isCurrentUserTrustManaged,
- isAnyBiometricsEnabledAndEnrolled,
- ::Triple
- )
- .map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) ->
- val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled)
- return@map if (
- trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
- ) {
- if (wasRebootedForMainlineUpdate) PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
- else PROMPT_REASON_RESTART
- } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
- PROMPT_REASON_TIMEOUT
- } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
- PROMPT_REASON_DEVICE_ADMIN
- } else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) {
- PROMPT_REASON_TRUSTAGENT_EXPIRED
- } else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
- PROMPT_REASON_TRUSTAGENT_EXPIRED
- } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
- PROMPT_REASON_USER_REQUEST
- } else if (
- trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
- ) {
- PROMPT_REASON_PREPARE_FOR_UPDATE
- } else if (
- trustOrBiometricsAvailable &&
- flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
- ) {
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
- } else {
- PROMPT_REASON_NONE
- }
- }
-
- private val biometricAuthReason: Flow<Int> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricAuthFailed(
- biometricSourceType: BiometricSourceType?
- ) {
- val promptReason =
- if (biometricSourceType == FINGERPRINT)
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
- else if (
- biometricSourceType == FACE && !updateMonitor.isFaceLockedOut
- ) {
- PROMPT_REASON_INCORRECT_FACE_INPUT
- } else PROMPT_REASON_NONE
- trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed")
- }
-
- override fun onBiometricsCleared() {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "onBiometricsCleared"
- )
- }
-
- override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType?,
- acquireInfo: Int
- ) {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "clearBiometricPrompt for new auth session."
- )
- }
-
- override fun onBiometricAuthenticated(
- userId: Int,
- biometricSourceType: BiometricSourceType?,
- isStrongBiometric: Boolean
- ) {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "onBiometricAuthenticated"
- )
- }
- }
- updateMonitor.registerCallback(callback)
- awaitClose { updateMonitor.removeCallback(callback) }
- }
- .distinctUntilChanged()
-
- private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage
-
- private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage
-
- private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> =
- _fingerprintAcquisitionMessage
-
- private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val customMessage: Flow<BouncerMessageModel?> = _customMessage
-
- override val biometricAuthMessage: Flow<BouncerMessageModel?> =
- biometricAuthReason
- .map {
- if (it == PROMPT_REASON_NONE) null
- else
- bouncerMessageFactory.createFromPromptReason(
- it,
- userRepository.getSelectedUserInfo().id
- )
- }
- .onStart { emit(null) }
- .distinctUntilChanged()
-
- override val authFlagsMessage: Flow<BouncerMessageModel?> =
- authFlagsBasedPromptReason
- .map {
- if (it == PROMPT_REASON_NONE) null
- else
- bouncerMessageFactory.createFromPromptReason(
- it,
- userRepository.getSelectedUserInfo().id
- )
- }
- .onStart { emit(null) }
- .distinctUntilChanged()
-
- // TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system
- // has been launched.
- private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
- if (biometricSourceType == FACE) {
- trySendWithFailureLogging(
- updateMonitor.isFaceLockedOut,
- TAG,
- "face lock out state changed."
- )
- }
- }
- }
- updateMonitor.registerCallback(callback)
- trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value")
- awaitClose { updateMonitor.removeCallback(callback) }
- }
-
- override val biometricLockedOutMessage: Flow<BouncerMessageModel?> =
- combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face ->
- return@combine if (fp) {
- bouncerMessageFactory.createFromPromptReason(
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
- userRepository.getSelectedUserInfo().id
- )
- } else if (face) {
- bouncerMessageFactory.createFromPromptReason(
- PROMPT_REASON_FACE_LOCKED_OUT,
- userRepository.getSelectedUserInfo().id
- )
- } else null
- }
-
- override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
- _primaryAuthMessage.value = value
- }
-
- override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
- _faceAcquisitionMessage.value = value
- }
-
- override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
- _fingerprintAcquisitionMessage.value = value
- }
-
- override fun setCustomMessage(value: BouncerMessageModel?) {
- _customMessage.value = value
- }
-
- override fun clearMessage() {
- _fingerprintAcquisitionMessage.value = null
- _faceAcquisitionMessage.value = null
- _primaryAuthMessage.value = null
- _customMessage.value = null
- }
-
- companion object {
- const val TAG = "BouncerDetailedMessageRepository"
+ override fun setMessage(message: BouncerMessageModel) {
+ _bouncerMessage.value = message
}
}
-
-private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
- flow.combine(anotherFlow) { a, b -> a || b }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index abddb0a..9b2f2ba 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.domain.interactor
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
@@ -225,6 +225,20 @@
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
+ /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */
+ fun hide(
+ loggingReason: String,
+ ) {
+ if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
+ return
+ }
+
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Lockscreen),
+ loggingReason = loggingReason,
+ )
+ }
+
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
is AuthenticationMethodModel.Pin ->
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
index 497747f..aecfe1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
@@ -16,16 +16,13 @@
package com.android.systemui.bouncer.domain.interactor
-import android.os.Build
import android.util.Log
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
private val TAG = BouncerMessageAuditLogger::class.simpleName!!
@@ -37,24 +34,8 @@
constructor(
@Application private val scope: CoroutineScope,
private val repository: BouncerMessageRepository,
- private val interactor: BouncerMessageInteractor,
) : CoreStartable {
override fun start() {
- if (Build.isDebuggable()) {
- collectAndLog(repository.biometricAuthMessage, "biometricMessage: ")
- collectAndLog(repository.primaryAuthMessage, "primaryAuthMessage: ")
- collectAndLog(repository.customMessage, "customMessage: ")
- collectAndLog(repository.faceAcquisitionMessage, "faceAcquisitionMessage: ")
- collectAndLog(
- repository.fingerprintAcquisitionMessage,
- "fingerprintAcquisitionMessage: "
- )
- collectAndLog(repository.authFlagsMessage, "authFlagsMessage: ")
- collectAndLog(interactor.bouncerMessage, "interactor.bouncerMessage: ")
- }
- }
-
- private fun collectAndLog(flow: Flow<BouncerMessageModel?>, context: String) {
- scope.launch { flow.collect { Log.d(TAG, context + it) } }
+ scope.launch { repository.bouncerMessage.collect { Log.d(TAG, "bouncerMessage: $it") } }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index fe01d08..f612f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -16,55 +16,234 @@
package com.android.systemui.bouncer.domain.interactor
+import android.hardware.biometrics.BiometricSourceType
import android.os.CountDownTimer
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
+import com.android.systemui.flags.SystemPropertiesHelper
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.res.R.string.bouncer_face_not_recognized
+import com.android.systemui.res.R.string.keyguard_enter_password
+import com.android.systemui.res.R.string.keyguard_enter_pattern
+import com.android.systemui.res.R.string.keyguard_enter_pin
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_password
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pattern
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pin
+import com.android.systemui.res.R.string.kg_bio_try_again_or_password
+import com.android.systemui.res.R.string.kg_bio_try_again_or_pattern
+import com.android.systemui.res.R.string.kg_bio_try_again_or_pin
+import com.android.systemui.res.R.string.kg_face_locked_out
+import com.android.systemui.res.R.string.kg_fp_not_recognized
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin
+import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock
+import com.android.systemui.res.R.string.kg_prompt_after_update_password
+import com.android.systemui.res.R.string.kg_prompt_after_update_pattern
+import com.android.systemui.res.R.string.kg_prompt_after_update_pin
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_password
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pattern
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin
+import com.android.systemui.res.R.string.kg_prompt_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_password_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_pattern_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_password
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_pattern
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin
+import com.android.systemui.res.R.string.kg_prompt_unattended_update
+import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
+import com.android.systemui.res.R.string.kg_trust_agent_disabled
+import com.android.systemui.res.R.string.kg_unlock_with_password_or_fp
+import com.android.systemui.res.R.string.kg_unlock_with_pattern_or_fp
+import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp
+import com.android.systemui.res.R.string.kg_wrong_input_try_fp_suggestion
+import com.android.systemui.res.R.string.kg_wrong_password_try_again
+import com.android.systemui.res.R.string.kg_wrong_pattern_try_again
+import com.android.systemui.res.R.string.kg_wrong_pin_try_again
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.Quint
import javax.inject.Inject
import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
+private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
+private const val TAG = "BouncerMessageInteractor"
@SysUISingleton
class BouncerMessageInteractor
@Inject
constructor(
private val repository: BouncerMessageRepository,
- private val factory: BouncerMessageFactory,
private val userRepository: UserRepository,
private val countDownTimerUtil: CountDownTimerUtil,
private val featureFlags: FeatureFlags,
+ private val updateMonitor: KeyguardUpdateMonitor,
+ trustRepository: TrustRepository,
+ biometricSettingsRepository: BiometricSettingsRepository,
+ private val systemPropertiesHelper: SystemPropertiesHelper,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+ @Application private val applicationScope: CoroutineScope,
+ private val facePropertyRepository: FacePropertyRepository,
+ deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ faceAuthRepository: DeviceEntryFaceAuthRepository,
+ private val securityModel: KeyguardSecurityModel,
) {
+
+ private val isFingerprintAuthCurrentlyAllowed =
+ deviceEntryFingerprintAuthRepository.isLockedOut
+ .isFalse()
+ .and(biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed)
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ private val currentSecurityMode
+ get() = securityModel.getSecurityMode(currentUserId)
+ private val currentUserId
+ get() = userRepository.getSelectedUserInfo().id
+
+ private val kumCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
+ repository.setMessage(
+ when (biometricSourceType) {
+ BiometricSourceType.FINGERPRINT ->
+ incorrectFingerprintInput(currentSecurityMode)
+ BiometricSourceType.FACE ->
+ incorrectFaceInput(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ else ->
+ defaultMessage(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ }
+ )
+ }
+
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType?,
+ acquireInfo: Int
+ ) {
+ super.onBiometricAcquired(biometricSourceType, acquireInfo)
+ }
+
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType?,
+ isStrongBiometric: Boolean
+ ) {
+ repository.setMessage(defaultMessage)
+ }
+ }
+
+ private val isAnyBiometricsEnabledAndEnrolled =
+ biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.or(
+ biometricSettingsRepository.isFingerprintEnrolledAndEnabled
+ )
+
+ private val wasRebootedForMainlineUpdate
+ get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
+
+ private val isFaceAuthClass3
+ get() = facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG
+
+ private val initialBouncerMessage: Flow<BouncerMessageModel> =
+ combine(
+ biometricSettingsRepository.authenticationFlags,
+ trustRepository.isCurrentUserTrustManaged,
+ isAnyBiometricsEnabledAndEnrolled,
+ deviceEntryFingerprintAuthRepository.isLockedOut,
+ faceAuthRepository.isLockedOut,
+ ::Quint
+ )
+ .map { (flags, _, biometricsEnrolledAndEnabled, fpLockedOut, faceLockedOut) ->
+ val isTrustUsuallyManaged = trustRepository.isCurrentUserTrustUsuallyManaged.value
+ val trustOrBiometricsAvailable =
+ (isTrustUsuallyManaged || biometricsEnrolledAndEnabled)
+ return@map if (
+ trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
+ ) {
+ if (wasRebootedForMainlineUpdate) {
+ authRequiredForMainlineUpdate(currentSecurityMode)
+ } else {
+ authRequiredAfterReboot(currentSecurityMode)
+ }
+ } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
+ authRequiredAfterPrimaryAuthTimeout(currentSecurityMode)
+ } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
+ authRequiredAfterAdminLockdown(currentSecurityMode)
+ } else if (
+ trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
+ ) {
+ authRequiredForUnattendedUpdate(currentSecurityMode)
+ } else if (fpLockedOut) {
+ class3AuthLockedOut(currentSecurityMode)
+ } else if (faceLockedOut) {
+ if (isFaceAuthClass3) {
+ class3AuthLockedOut(currentSecurityMode)
+ } else {
+ faceLockedOut(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ }
+ } else if (
+ trustOrBiometricsAvailable &&
+ flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
+ ) {
+ nonStrongAuthTimeout(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterUserRequest) {
+ trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
+ trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
+ authRequiredAfterUserLockdown(currentSecurityMode)
+ } else {
+ defaultMessage
+ }
+ }
+
fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
val callback =
object : CountDownTimerCallback {
override fun onFinish() {
- repository.clearMessage()
+ repository.setMessage(defaultMessage)
}
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished / 1000.0).roundToInt()
- val message =
- factory.createFromPromptReason(
- reason = PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
- userId = userRepository.getSelectedUserInfo().id
- )
- message?.message?.animate = false
- message?.message?.formatterArgs =
+ val message = primaryAuthLockedOut(currentSecurityMode)
+ message.message?.animate = false
+ message.message?.formatterArgs =
mutableMapOf<String, Any>(Pair("count", secondsRemaining))
- repository.setPrimaryAuthMessage(message)
+ repository.setMessage(message)
}
}
countDownTimerUtil.startNewTimer(secondsBeforeLockoutReset * 1000, 1000, callback)
@@ -73,104 +252,58 @@
fun onPrimaryAuthIncorrectAttempt() {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
- repository.setPrimaryAuthMessage(
- factory.createFromPromptReason(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- userRepository.getSelectedUserInfo().id
- )
+ repository.setMessage(
+ incorrectSecurityInput(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setFingerprintAcquisitionMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.setFingerprintAcquisitionMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setFaceAcquisitionMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.setFaceAcquisitionMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setCustomMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
- repository.setCustomMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
+ private val defaultMessage: BouncerMessageModel
+ get() = defaultMessage(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+
fun onPrimaryBouncerUserInput() {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.clearMessage()
+ repository.setMessage(defaultMessage)
}
- fun onBouncerBeingHidden() {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ val bouncerMessage = repository.bouncerMessage
- repository.clearMessage()
+ init {
+ updateMonitor.registerCallback(kumCallback)
+
+ combine(primaryBouncerInteractor.isShowing, initialBouncerMessage) { showing, bouncerMessage
+ ->
+ if (showing) {
+ bouncerMessage
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+ .onEach { repository.setMessage(it) }
+ .launchIn(applicationScope)
}
-
- private fun firstNonNullMessage(
- oneMessageModel: Flow<BouncerMessageModel?>,
- anotherMessageModel: Flow<BouncerMessageModel?>
- ): Flow<BouncerMessageModel?> {
- return oneMessageModel.combine(anotherMessageModel) { a, b -> a ?: b }
- }
-
- // Null if feature flag is enabled which gets ignored always or empty bouncer message model that
- // always maps to an empty string.
- private fun nullOrEmptyMessage() =
- flowOf(
- if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null else factory.emptyMessage()
- )
-
- val bouncerMessage =
- listOf(
- nullOrEmptyMessage(),
- repository.primaryAuthMessage,
- repository.biometricAuthMessage,
- repository.fingerprintAcquisitionMessage,
- repository.faceAcquisitionMessage,
- repository.customMessage,
- repository.authFlagsMessage,
- repository.biometricLockedOutMessage,
- userRepository.selectedUserInfo.map {
- factory.createFromPromptReason(PROMPT_REASON_DEFAULT, it.id)
- },
- )
- .reduce(::firstNonNullMessage)
- .distinctUntilChanged()
}
interface CountDownTimerCallback {
@@ -199,3 +332,272 @@
.start()
}
}
+
+private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>) =
+ this.combine(anotherFlow) { a, b -> a || b }
+
+private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>) =
+ this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse() = this.map { !it }
+
+private fun defaultMessage(
+ securityMode: SecurityMode,
+ secondaryMessage: String?,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return BouncerMessageModel(
+ message =
+ Message(
+ messageResId = defaultMessage(securityMode, fpAuthIsAllowed).message?.messageResId,
+ animate = false
+ ),
+ secondaryMessage = Message(message = secondaryMessage, animate = false)
+ )
+}
+
+private fun defaultMessage(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ defaultMessageWithFingerprint(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
+ SecurityMode.Password -> Pair(keyguard_enter_password, 0)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectSecurityInput(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ incorrectSecurityInputWithFingerprint(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
+ SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
+ SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
+ SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
+ SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
+ SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
+ SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFaceInput(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
+ SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
+ SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFaceInputWithFingerprintAllowed(
+ securityMode: SecurityMode
+): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun biometricLockout(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
+ SecurityMode.Password ->
+ Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun nonStrongAuthTimeout(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun faceLockedOut(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun class3AuthLockedOut(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun trustAgentDisabled(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun trustAgentDisabledWithFingerprintAllowed(
+ securityMode: SecurityMode
+): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
+ SecurityMode.Password ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password)
+ SecurityMode.PIN ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun Pair<Int, Int>.toMessage(): BouncerMessageModel {
+ return BouncerMessageModel(
+ message = Message(messageResId = this.first, animate = false),
+ secondaryMessage = Message(messageResId = this.second, animate = false)
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 579f0b7..6e26fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -28,7 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.DejankUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -38,8 +38,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.plugins.ActivityStarter
@@ -74,7 +72,6 @@
private val context: Context,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
- private val featureFlags: FeatureFlags,
@Application private val applicationScope: CoroutineScope,
) {
private val passiveAuthBouncerDelay =
@@ -135,11 +132,9 @@
init {
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
- if (featureFlags.isEnabled(Flags.DELAY_BOUNCER)) {
- applicationScope.launch {
- trustRepository.isCurrentUserActiveUnlockRunning.collect {
- currentUserActiveUnlockRunning = it
- }
+ applicationScope.launch {
+ trustRepository.isCurrentUserActiveUnlockRunning.collect {
+ currentUserActiveUnlockRunning = it
}
}
}
@@ -415,9 +410,7 @@
currentUserActiveUnlockRunning &&
keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
- return featureFlags.isEnabled(Flags.DELAY_BOUNCER) &&
- !needsFullscreenBouncer() &&
- (canRunFaceAuth || canRunActiveUnlock)
+ return !needsFullscreenBouncer() && (canRunFaceAuth || canRunActiveUnlock)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
index 0e9e962..7b169f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
@@ -22,7 +22,10 @@
* Represents the message displayed on the bouncer. It has two parts, primary and a secondary
* message
*/
-data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null)
+data class BouncerMessageModel(
+ val message: Message? = null,
+ val secondaryMessage: Message? = null,
+)
/**
* Representation of a single message on the bouncer. It can be either a string or a string resource
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
index 6486802..79a11ee 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
@@ -22,7 +22,7 @@
import com.android.keyguard.BouncerKeyguardMessageArea
import com.android.keyguard.KeyguardMessageArea
import com.android.keyguard.KeyguardMessageAreaController
-import com.android.systemui.R
+import com.android.systemui.res.R
class BouncerMessageView : LinearLayout {
constructor(context: Context?) : super(context)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index e29d6bd..36e5db4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -144,7 +144,6 @@
)
}
} else {
- bouncerMessageInteractor.onBouncerBeingHidden()
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ false
)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index d95b70c..4546bea 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.bouncer.ui.viewmodel
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -28,6 +29,7 @@
* being able to attempt to unlock the device.
*/
val isInputEnabled: StateFlow<Boolean>,
+ private val interactor: BouncerInteractor,
) {
private val _animateFailure = MutableStateFlow(false)
@@ -37,6 +39,9 @@
*/
val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
+ /** Whether the input method editor (for example, the software keyboard) is visible. */
+ private var isImeVisible: Boolean = false
+
/**
* Notifies that the failure animation has been shown. This should be called to consume a `true`
* value in [animateFailure].
@@ -45,6 +50,21 @@
_animateFailure.value = false
}
+ /**
+ * Notifies that the input method editor (for example, the software keyboard) has been shown or
+ * hidden.
+ */
+ fun onImeVisibilityChanged(isVisible: Boolean) {
+ if (isImeVisible && !isVisible) {
+ // The IME has gone from visible to invisible, dismiss the bouncer.
+ interactor.hide(
+ loggingReason = "IME hidden",
+ )
+ }
+
+ isImeVisible = isVisible
+ }
+
/** Ask the UI to show the failure animation. */
protected fun showFailureAnimation() {
_animateFailure.value = true
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index f6794d4..15d1dae 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index d214797..9e10f29 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -31,6 +31,7 @@
) :
AuthMethodBouncerViewModel(
isInputEnabled = isInputEnabled,
+ interactor = interactor,
) {
private val _password = MutableStateFlow("")
@@ -60,6 +61,10 @@
/** Notifies that the user has pressed the key for attempting to authenticate the password. */
fun onAuthenticateKeyPressed() {
val password = _password.value.toCharArray().toList()
+ if (password.isEmpty()) {
+ return
+ }
+
_password.value = ""
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 1985c37..497276b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -42,6 +42,7 @@
) :
AuthMethodBouncerViewModel(
isInputEnabled = isInputEnabled,
+ interactor = interactor,
) {
/** The number of columns in the dot grid. */
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index dc5c528..8e6421e 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -37,6 +37,7 @@
) :
AuthMethodBouncerViewModel(
isInputEnabled = isInputEnabled,
+ interactor = interactor,
) {
val pinShapes = PinShapeAdapter(applicationContext)
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
index cc43e7e..1e17059 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
@@ -20,7 +20,7 @@
import android.content.Intent
import android.provider.MediaStore
import android.text.TextUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
class CameraIntents {
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index ddb09749..718ef51 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -27,7 +27,7 @@
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 7bf8f4d..047d15d 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -32,7 +32,7 @@
import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.surfaceeffects.ripple.RippleShader;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index 0a1aed6..2729b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -19,7 +19,7 @@
import android.content.res.Resources;
import android.view.ViewConfiguration;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
index 0542e13..e83a825 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
@@ -20,7 +20,7 @@
import android.net.Uri
import android.util.Log
import android.util.Size
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import java.io.IOException
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
index 789833c..12597a7 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
@@ -24,7 +24,7 @@
import android.util.Log
import android.util.Size
import android.view.textclassifier.TextLinks
-import com.android.systemui.R
+import com.android.systemui.res.R
import java.io.IOException
data class ClipboardModel(
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index ffd836b..70736ae 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -61,7 +61,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
index 758a656..2406d61 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -30,7 +30,7 @@
import android.view.textclassifier.TextLinks;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index a76d2ea..2af49cf 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -54,7 +54,7 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.screenshot.DraggableConstraintLayout;
import com.android.systemui.screenshot.FloatingWindowUtil;
import com.android.systemui.screenshot.OverlayActionChip;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardToast.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardToast.java
index e9daa46..0a4d76e 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardToast.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardToast.java
@@ -19,7 +19,7 @@
import android.content.Context;
import android.widget.Toast;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index 1ffbe32..bb201b6 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -31,7 +31,7 @@
import android.widget.EditText;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Lightweight activity for editing text clipboard contents
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index d58fab4..a18b4c8 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -24,7 +24,7 @@
import android.net.Uri;
import android.text.TextUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
class IntentCreator {
private static final String EXTRA_EDIT_SOURCE = "edit_source";
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index 09b2e44..0fd34bd 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -25,7 +25,7 @@
import android.view.Display;
import android.view.LayoutInflater;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
import com.android.systemui.settings.DisplayTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
index f362831..7fe0032 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
@@ -29,7 +29,7 @@
import android.widget.SeekBar;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
new file mode 100644
index 0000000..ea3ddac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** Encapsulates the state of communal mode. */
+interface CommunalRepository {
+ /** Whether communal features are enabled. */
+ val isCommunalEnabled: Boolean
+}
+
+@SysUISingleton
+class CommunalRepositoryImpl
+@Inject
+constructor(
+ featureFlags: FeatureFlagsClassic,
+) : CommunalRepository {
+ override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_HUB)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
new file mode 100644
index 0000000..9d95b9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalRepositoryModule {
+ @Binds fun communalRepository(impl: CommunalRepositoryImpl): CommunalRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 6dc305e..9fb8da3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.domain.interactor
+import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import com.android.systemui.dagger.SysUISingleton
@@ -27,8 +28,12 @@
class CommunalInteractor
@Inject
constructor(
+ communalRepository: CommunalRepository,
widgetRepository: CommunalWidgetRepository,
) {
+ /** Whether communal features are enabled. */
+ val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled
+
/** A flow of info about the widget to be displayed, or null if widget is unavailable. */
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
index 1b6d3a8..65bf4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
@@ -18,7 +18,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
index 560f4fa..039078e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
@@ -20,7 +20,7 @@
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
/** Wraps around a widget rendered in communal mode. */
class CommunalWidgetWrapper(context: Context, attrs: AttributeSet? = null) :
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
index 8640c97..c3e8a96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -22,7 +22,7 @@
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java
index e1dd1a6..20b2494 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java
@@ -40,7 +40,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.ComplicationLayoutParams.Direction;
import com.android.systemui.complication.ComplicationLayoutParams.Position;
import com.android.systemui.complication.dagger.ComplicationModule;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/ComplicationHostViewModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/ComplicationHostViewModule.java
index a7d017dd..712213a 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/ComplicationHostViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/ComplicationHostViewModule.java
@@ -22,7 +22,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import dagger.Module;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
index 87c3b2f..099e3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
@@ -21,7 +21,7 @@
import android.view.View
import android.widget.TextClock
import com.android.internal.util.Preconditions
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.complication.DreamClockTimeComplication
import com.android.systemui.complication.DreamClockTimeComplication.DreamClockTimeViewHolder
import dagger.Module
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
index 2b5aa7c..08d0595 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -25,7 +25,7 @@
import android.widget.ImageView;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.DreamHomeControlsComplication;
import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamMediaEntryComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamMediaEntryComplicationComponent.java
index f15a586..c0a292c 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamMediaEntryComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamMediaEntryComplicationComponent.java
@@ -21,7 +21,7 @@
import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.DreamMediaEntryComplication;
import dagger.Module;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 776c972..92fdb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -19,7 +19,7 @@
import android.content.res.Resources;
import android.view.ViewGroup;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.ComplicationLayoutParams;
import com.android.systemui.dagger.SystemUIBinder;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
index e627b68..e9b5930 100644
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
@@ -28,7 +28,7 @@
import android.view.View
import android.widget.FrameLayout
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index adb0bf3..97b4c94 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -32,7 +32,7 @@
import android.util.IconDrawableFactory
import androidx.annotation.WorkerThread
import com.android.settingslib.applications.DefaultAppInfo
-import com.android.systemui.R
+import com.android.systemui.res.R
import java.util.Objects
open class ControlsServiceInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
index 6e17bc9..21458e8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
@@ -28,7 +28,7 @@
import android.view.animation.DecelerateInterpolator
import android.widget.TextView
import com.android.systemui.Prefs
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.recents.TriangleShape
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt
index 0249060..05ff7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt
@@ -16,7 +16,7 @@
package com.android.systemui.controls.controller
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 3fe0f03..72483bb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -26,7 +26,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.util.icuMessageFormat
import java.text.Collator
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index ec76f43..f034851 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -36,7 +36,7 @@
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlInterface
import com.android.systemui.controls.ui.CanUseIconPredicate
import com.android.systemui.controls.ui.RenderInfo
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
index b447d66..a534097 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
@@ -31,7 +31,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.controls.ui.ControlsUiController
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 4c9dbe0..41edb4a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -34,7 +34,7 @@
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 8bae667..2ea4303 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -39,7 +39,7 @@
import androidx.annotation.VisibleForTesting
import androidx.viewpager2.widget.ViewPager2
import com.android.systemui.Prefs
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.TooltipManager
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index d5b8693..10a0117 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -35,7 +35,7 @@
import androidx.annotation.VisibleForTesting
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index 86bde5c..d7a68b3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -32,7 +32,7 @@
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.ComponentActivity
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsController
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
index 6f87aa9..5df26b3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
@@ -20,7 +20,7 @@
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import java.util.function.Consumer
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
index 5977d37..7e56077 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
@@ -21,7 +21,7 @@
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
class StructureAdapter(
private val models: List<StructureContainer>,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
index 4aef209..4e935df 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.content.SharedPreferences
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt
index 501bcf0..4e391b7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt
@@ -24,7 +24,7 @@
import android.content.SharedPreferences
import android.provider.Settings
import androidx.annotation.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG
import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
index a13f717..7e039cd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
@@ -32,7 +32,7 @@
import android.widget.CheckBox
import android.widget.EditText
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Creates all dialogs for challengeValues that can occur from a call to
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index abe3423..fdb9971 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -49,7 +49,7 @@
import androidx.annotation.ColorInt
import androidx.annotation.VisibleForTesting
import com.android.internal.graphics.ColorUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlsController
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 8341964..955a5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -31,7 +31,7 @@
import android.view.WindowInsets.Type
import android.view.WindowManager
import androidx.activity.ComponentActivity
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.management.ControlsAnimations
import com.android.systemui.controls.settings.ControlsSettingsDialogManager
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
index d6cfb79..2ad6014 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
@@ -20,7 +20,7 @@
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import java.util.function.Consumer
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
index f7c8e96..252f02e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
@@ -30,7 +30,7 @@
import android.widget.ListPopupWindow
import android.widget.ListView
import android.widget.PopupWindow
-import com.android.systemui.R
+import com.android.systemui.res.R
import kotlin.math.max
class ControlsPopupMenu(context: Context) : ListPopupWindow(context) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 3cdf9ab..be9a299 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -51,7 +51,7 @@
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.CustomIconCache
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 98f17f4..9b93522 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -34,7 +34,7 @@
import android.view.WindowManager
import android.widget.ImageView
import com.android.internal.policy.ScreenDecorationsUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index db009dc..1b0d032 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -28,7 +28,7 @@
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Trace
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.util.boundsOnScreen
import com.android.wm.shell.taskview.TaskView
import java.util.concurrent.Executor
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index dbbda9a..ac589eb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -26,7 +26,7 @@
import android.util.ArrayMap
import android.util.SparseArray
-import com.android.systemui.R
+import com.android.systemui.res.R
data class RenderInfo(
val icon: Drawable,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
index 3c2bfa0..0706a96 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
@@ -26,7 +26,7 @@
import android.view.View
import android.view.WindowManager
-import com.android.systemui.R
+import com.android.systemui.res.R
class StatusBehavior : Behavior {
lateinit var cvh: ControlViewHolder
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 39d6970..2c9eb3e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -22,7 +22,7 @@
import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.TemperatureControlTemplate
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
index d011dd4..4190788 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
@@ -26,7 +26,7 @@
import android.service.controls.templates.ThumbnailTemplate
import android.util.TypedValue
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index dc7247c..4f47012 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -23,7 +23,7 @@
import android.service.controls.templates.ToggleTemplate
import android.util.Log
import android.view.View
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
class ToggleBehavior : Behavior {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 0d570d2..e34a94b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -37,7 +37,7 @@
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
index 9dd0f53..580a615 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -23,7 +23,7 @@
import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.StatelessTemplate
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 7b58b1f..9e5fd55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.media.dagger.MediaModule;
+import com.android.systemui.navigationbar.NavigationBarControllerModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -101,6 +102,7 @@
GestureModule.class,
MediaModule.class,
MultiUserUtilsModule.class,
+ NavigationBarControllerModule.class,
PowerModule.class,
QSModule.class,
ReferenceScreenshotModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index f12b919..d89332d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.power.PowerUI
import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
+import com.android.systemui.recents.ScreenPinningRequest
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.ImmersiveModeConfirmation
@@ -366,4 +367,9 @@
@IntoMap
@ClassKey(KeyguardDismissBinder::class)
abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenPinningRequest::class)
+ abstract fun bindScreenPinningRequest(impl: ScreenPinningRequest): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 3a942bd..a325ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,6 +35,7 @@
import com.android.systemui.authentication.AuthenticationModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
+import com.android.systemui.biometrics.FingerprintReEnrollNotification;
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.bouncer.ui.BouncerViewModule;
@@ -89,6 +90,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.connectivity.ConnectivityModule;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -153,71 +155,72 @@
* may not appreciate that.
*/
@Module(includes = {
- AccessibilityModule.class,
- AccessibilityRepositoryModule.class,
- AConfigModule.class,
- AppOpsModule.class,
- AssistModule.class,
- AuthenticationModule.class,
- BiometricsModule.class,
- BouncerViewModule.class,
- ClipboardOverlayModule.class,
- ClockRegistryModule.class,
- CommonRepositoryModule.class,
- DisplayModule.class,
- ConnectivityModule.class,
- CoroutinesModule.class,
- DreamModule.class,
- ControlsModule.class,
- DemoModeModule.class,
- DisableFlagsModule.class,
- FalsingModule.class,
- FlagsModule.class,
- SystemPropertiesFlagsModule.class,
- FooterActionsModule.class,
- KeyboardModule.class,
- LetterboxModule.class,
- KeyguardBlueprintModule.class,
- LogModule.class,
- MediaProjectionModule.class,
- MediaProjectionTaskSwitcherModule.class,
- MotionToolModule.class,
- NotificationIconAreaControllerModule.class,
- PeopleHubModule.class,
- PeopleModule.class,
- PluginModule.class,
- PolicyModule.class,
- PrivacyModule.class,
- QRCodeScannerModule.class,
- QSFragmentStartableModule.class,
- RetailModeModule.class,
- ScreenshotModule.class,
- SensorModule.class,
- SecurityRepositoryModule.class,
- ScreenRecordModule.class,
- SettingsUtilModule.class,
- SmartRepliesInflationModule.class,
- SmartspaceModule.class,
- StatusBarPipelineModule.class,
- StatusBarPolicyModule.class,
- StatusBarWindowModule.class,
- SysUIConcurrencyModule.class,
- SysUIUnfoldModule.class,
- TelephonyRepositoryModule.class,
- TemporaryDisplayModule.class,
- TunerModule.class,
- UserModule.class,
- UtilModule.class,
- NoteTaskModule.class,
- WalletModule.class
+ AccessibilityModule.class,
+ AccessibilityRepositoryModule.class,
+ AConfigModule.class,
+ AppOpsModule.class,
+ AssistModule.class,
+ AuthenticationModule.class,
+ BiometricsModule.class,
+ BouncerViewModule.class,
+ ClipboardOverlayModule.class,
+ ClockRegistryModule.class,
+ CommonRepositoryModule.class,
+ ConnectivityModule.class,
+ ControlsModule.class,
+ CoroutinesModule.class,
+ DemoModeModule.class,
+ DisableFlagsModule.class,
+ DisplayModule.class,
+ DreamModule.class,
+ FalsingModule.class,
+ FlagsModule.class,
+ FooterActionsModule.class,
+ KeyboardModule.class,
+ KeyguardBlueprintModule.class,
+ LetterboxModule.class,
+ LogModule.class,
+ MediaProjectionModule.class,
+ MediaProjectionTaskSwitcherModule.class,
+ MotionToolModule.class,
+ NotificationIconAreaControllerModule.class,
+ PeopleHubModule.class,
+ PeopleModule.class,
+ PluginModule.class,
+ PolicyModule.class,
+ PrivacyModule.class,
+ QRCodeScannerModule.class,
+ QSFragmentStartableModule.class,
+ RetailModeModule.class,
+ ScreenshotModule.class,
+ SensorModule.class,
+ SecurityRepositoryModule.class,
+ ScreenRecordModule.class,
+ SettingsUtilModule.class,
+ SmartRepliesInflationModule.class,
+ SmartspaceModule.class,
+ StatusBarModule.class,
+ StatusBarPipelineModule.class,
+ StatusBarPolicyModule.class,
+ StatusBarWindowModule.class,
+ SystemPropertiesFlagsModule.class,
+ SysUIConcurrencyModule.class,
+ SysUIUnfoldModule.class,
+ TelephonyRepositoryModule.class,
+ TemporaryDisplayModule.class,
+ TunerModule.class,
+ UserModule.class,
+ UtilModule.class,
+ NoteTaskModule.class,
+ WalletModule.class
},
subcomponents = {
ComplicationComponent.class,
- NavigationBarComponent.class,
- NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
KeyguardBouncerComponent.class,
+ NavigationBarComponent.class,
+ NotificationRowComponent.class,
NotificationShelfComponent.class,
WindowRootViewComponent.class,
})
@@ -297,6 +300,9 @@
@BindsOptionalOf
abstract SystemStatusAnimationScheduler optionalSystemStatusAnimationScheduler();
+ @BindsOptionalOf
+ abstract FingerprintReEnrollNotification optionalFingerprintReEnrollNotification();
+
@SysUISingleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
index ded0fb7..dbe6b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
@@ -21,7 +21,7 @@
import android.view.Surface
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.ScreenDecorations.DisplayCutoutView
class CutoutDecorProviderImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index ac0d3c8..31a5d37 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -115,7 +115,7 @@
private val logger: ScreenDecorationsLogger,
private val featureFlags: FeatureFlags,
) : BoundDecorProvider() {
- override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
+ override val viewId: Int = com.android.systemui.res.R.id.face_scanning_anim
override fun onReloadResAndMeasure(
view: View,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
index 8cfd391..14ecc66 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
@@ -23,7 +23,7 @@
import android.view.Surface
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
index 0ffb4fb..2f2c952f 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
@@ -17,7 +17,7 @@
package com.android.systemui.decor
import android.view.DisplayCutout
-import com.android.systemui.R
+import com.android.systemui.res.R
class RoundedCornerDecorProviderFactory(
private val roundedCornerResDelegate: RoundedCornerResDelegate
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
index 8156797..0ee11ae 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
@@ -25,7 +25,7 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
-import com.android.systemui.R
+import com.android.systemui.res.R
class RoundedCornerDecorProviderImpl(
override val viewId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index c64766a..0314b0fe 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -24,7 +24,7 @@
import android.util.Size
import android.view.RoundedCorners
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import java.io.PrintWriter
interface RoundedCornerResDelegate {
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 1751358..4f16685 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.display.data.repository
import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
import android.hardware.display.DisplayManager.DisplayListener
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_ADDED
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
@@ -95,28 +96,36 @@
@Background backgroundCoroutineDispatcher: CoroutineDispatcher
) : DisplayRepository {
// Displays are enabled only after receiving them in [onDisplayAdded]
- private val allDisplayEvents: Flow<DisplayEvent> = conflatedCallbackFlow {
- val callback =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- trySend(DisplayEvent.Added(displayId))
- }
+ private val allDisplayEvents: Flow<DisplayEvent> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ trySend(DisplayEvent.Added(displayId))
+ }
- override fun onDisplayRemoved(displayId: Int) {
- trySend(DisplayEvent.Removed(displayId))
- }
+ override fun onDisplayRemoved(displayId: Int) {
+ trySend(DisplayEvent.Removed(displayId))
+ }
- override fun onDisplayChanged(displayId: Int) {
- trySend(DisplayEvent.Changed(displayId))
- }
+ override fun onDisplayChanged(displayId: Int) {
+ trySend(DisplayEvent.Changed(displayId))
+ }
+ }
+ // Triggers an initial event when subscribed. This is needed to avoid getDisplays to
+ // be called when this class is constructed, but only when someone subscribes to
+ // this flow.
+ trySend(DisplayEvent.Changed(Display.DEFAULT_DISPLAY))
+ displayManager.registerDisplayListener(
+ callback,
+ backgroundHandler,
+ EVENT_FLAG_DISPLAY_ADDED or
+ EVENT_FLAG_DISPLAY_CHANGED or
+ EVENT_FLAG_DISPLAY_REMOVED,
+ )
+ awaitClose { displayManager.unregisterDisplayListener(callback) }
}
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- EVENT_FLAG_DISPLAY_ADDED or EVENT_FLAG_DISPLAY_CHANGED or EVENT_FLAG_DISPLAY_REMOVED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
+ .flowOn(backgroundCoroutineDispatcher)
override val displayChangeEvent: Flow<Int> =
allDisplayEvents.filter { it is DisplayEvent.Changed }.map { it.displayId }
@@ -128,7 +137,9 @@
.stateIn(
applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = getDisplays()
+ // To avoid getting displays on this object construction, they are get after the
+ // first event. allDisplayEvents emits a changed event when we subscribe to it.
+ initialValue = emptySet()
)
private fun getDisplays(): Set<Display> =
@@ -146,12 +157,23 @@
private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+ private fun getInitialConnectedDisplays(): Set<Int> =
+ displayManager
+ .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ .map { it.displayId }
+ .toSet()
+ .also {
+ if (DEBUG) {
+ Log.d(TAG, "getInitialConnectedDisplays: $it")
+ }
+ }
+
/* keeps connected displays until they are disconnected. */
private val connectedDisplayIds: StateFlow<Set<Int>> =
conflatedCallbackFlow {
+ val connectedIds = getInitialConnectedDisplays().toMutableSet()
val callback =
object : DisplayConnectionListener {
- private val connectedIds = mutableSetOf<Int>()
override fun onDisplayConnected(id: Int) {
if (DEBUG) {
Log.d(TAG, "display with id=$id connected.")
@@ -170,6 +192,7 @@
trySend(connectedIds.toSet())
}
}
+ trySend(connectedIds.toSet())
displayManager.registerDisplayListener(
callback,
backgroundHandler,
@@ -183,6 +206,10 @@
.stateIn(
applicationScope,
started = SharingStarted.WhileSubscribed(),
+ // The initial value is set to empty, but connected displays are gathered as soon as
+ // the flow starts being collected. This is to ensure the call to get displays (an
+ // IPC) happens in the background instead of when this object
+ // is instantiated.
initialValue = emptySet()
)
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index f730935..7510cf6c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -22,7 +22,7 @@
import android.view.View
import android.view.WindowManager
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Dialog used to decide what to do with a connected display.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 55df779..8e542b5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -28,7 +28,7 @@
import android.util.KeyValueListParser;
import android.util.Log;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index d408472..db0c3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -20,7 +20,7 @@
import android.hardware.Sensor;
import android.os.Handler;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.doze.DozeAuthRemover;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 01fb522..4cfed33 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -27,7 +27,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.dream.lowlight.util.TruncatedInterpolator
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.complication.ComplicationLayoutParams
import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 78ac453..d0f2559 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -33,7 +33,7 @@
import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.ComplicationHostViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
index 02a8b39a..409b196 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -31,7 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 1865c38..8e77079 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -26,7 +26,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index a6401b6..8d8702e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -32,7 +32,7 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
index 95c225d..6fd6f4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
@@ -1,7 +1,7 @@
package com.android.systemui.dreams.complication.dagger
import android.content.res.Resources
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 4bafe32..5ebb2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -23,7 +23,7 @@
import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.settingslib.dream.DreamBackend;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 51eefd6..34dd1008 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -25,7 +25,7 @@
import androidx.lifecycle.LifecycleOwner;
import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.dreams.DreamOverlayStatusBarView;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
index 5f03743..8cf11a9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
@@ -21,7 +21,7 @@
import android.util.TypedValue;
import android.view.VelocityTracker;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler;
import com.android.systemui.dreams.touch.DreamTouchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
index 9e0ae41..94fe4bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
@@ -18,7 +18,7 @@
import android.content.res.Resources;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.touch.DreamTouchHandler;
import com.android.systemui.dreams.touch.ShadeTouchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/dump/SystemUIConfigDumpable.kt b/packages/SystemUI/src/com/android/systemui/dump/SystemUIConfigDumpable.kt
index b70edcc..7a441d6 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/SystemUIConfigDumpable.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/SystemUIConfigDumpable.kt
@@ -19,7 +19,7 @@
import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import java.io.PrintWriter
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e11fcb7..467e84d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -17,7 +17,7 @@
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FlagsFactory.releasedFlag
import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
import com.android.systemui.flags.FlagsFactory.sysPropBooleanFlag
@@ -107,7 +107,7 @@
// TODO(b/268005230): Tracking Bug
@JvmField
- val SENSITIVE_REVEAL_ANIM = unreleasedFlag("sensitive_reveal_anim", teamfood = true)
+ val SENSITIVE_REVEAL_ANIM = releasedFlag("sensitive_reveal_anim")
// TODO(b/280783617): Tracking Bug
@Keep
@@ -182,15 +182,12 @@
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
- @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag("face_auth_refactor", teamfood = true)
+ @JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
/** Flag to control the revamp of keyguard biometrics progress animation */
// TODO(b/244313043): Tracking bug
@JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
- // TODO(b/262780002): Tracking Bug
- @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag("revamped_wallpaper_ui")
-
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
@@ -253,10 +250,6 @@
// TODO(b/277961132): Tracking bug.
@JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
- /** Whether to delay showing bouncer UI when face auth or active unlock are enrolled. */
- // TODO(b/279794160): Tracking bug.
- @JvmField val DELAY_BOUNCER = releasedFlag("delay_bouncer")
-
/** Keyguard Migration */
/**
@@ -294,6 +287,11 @@
// TODO(b/291767565): Tracking bug.
@JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag("migrate_keyguard_status_view")
+ /** Migrate the status bar view on keyguard from notification panel to keyguard root view. */
+ // TODO(b/299115332): Tracking Bug.
+ @JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
+ unreleasedFlag("migrate_keyguard_status_bar_view")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -318,6 +316,11 @@
R.bool.flag_stop_pulsing_face_scanning_animation,
"stop_pulsing_face_scanning_animation")
+ /** Flag to use a separate view for the alternate bouncer. */
+ // TODO(b/300440924): Tracking bug
+ @JvmField
+ val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -383,13 +386,15 @@
// 600- status bar
// TODO(b/291315866): Tracking Bug
- @JvmField val SIGNAL_CALLBACK_DEPRECATION = unreleasedFlag("signal_callback_deprecation")
+ @JvmField val SIGNAL_CALLBACK_DEPRECATION =
+ unreleasedFlag("signal_callback_deprecation", teamfood = true)
// TODO(b/265892345): Tracking Bug
val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
// TODO(b/292533677): Tracking Bug
- val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon")
+ val WIFI_TRACKER_LIB_FOR_WIFI_ICON =
+ unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true)
// TODO(b/293863612): Tracking Bug
@JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
@@ -403,17 +408,7 @@
// TODO(b/290676905): Tracking Bug
val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
- unreleasedFlag("new_shade_carrier_group_mobile_icons", teamfood = true)
-
- // 700 - dialer/calls
- // TODO(b/254512734): Tracking Bug
- val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag("ongoing_call_status_bar_chip")
-
- // TODO(b/254512681): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE = releasedFlag("ongoing_call_in_immersive")
-
- // TODO(b/254512753): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag("ongoing_call_in_immersive_chip_tap")
+ releasedFlag("new_shade_carrier_group_mobile_icons")
// 800 - general visual/theme
@JvmField val MONET = resourceBooleanFlag(R.bool.flag_monet, "monet")
@@ -760,14 +755,13 @@
unreleasedFlag("enable_new_privacy_dialog", teamfood = true)
// TODO(b/289573946): Tracking Bug
- @JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text")
+ @JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
@JvmField
- val USE_REPOS_FOR_BOUNCER_SHOWING =
- unreleasedFlag("use_repos_for_bouncer_showing", teamfood = true)
+ val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
// 3100 - Haptic interactions
@@ -783,11 +777,11 @@
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
- val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space", teamfood = true)
+ val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space")
/** Enable the Compose implementation of the Quick Settings footer actions. */
@JvmField
- val COMPOSE_QS_FOOTER_ACTIONS = unreleasedFlag("compose_qs_footer_actions", teamfood = true)
+ val COMPOSE_QS_FOOTER_ACTIONS = releasedFlag("compose_qs_footer_actions")
/** Enable the share wifi button in Quick Settings internet dialog. */
@JvmField
@@ -799,9 +793,15 @@
// TODO(b/287205379): Tracking bug
@JvmField
- val QS_CONTAINER_GRAPH_OPTIMIZER = unreleasedFlag( "qs_container_graph_optimizer")
+ val QS_CONTAINER_GRAPH_OPTIMIZER = unreleasedFlag( "qs_container_graph_optimizer",
+ teamfood = true)
/** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
@JvmField
- val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog")
+ val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog", teamfood = true)
+
+ // TODO(b/300995746): Tracking Bug
+ /** Enable communal hub features. */
+ @JvmField
+ val COMMUNAL_HUB = resourceBooleanFlag(R.bool.config_communalServiceEnabled, "communal_hub")
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
index 5907028..a3065d3 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
@@ -26,7 +26,7 @@
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Grid-based implementation of the button layout created by the global actions dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 3eb1740..ac40230 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -546,8 +546,8 @@
*/
@VisibleForTesting
protected int getMaxShownPowerItems() {
- return mResources.getInteger(com.android.systemui.R.integer.power_menu_lite_max_columns)
- * mResources.getInteger(com.android.systemui.R.integer.power_menu_lite_max_rows);
+ return mResources.getInteger(com.android.systemui.res.R.integer.power_menu_lite_max_columns)
+ * mResources.getInteger(com.android.systemui.res.R.integer.power_menu_lite_max_rows);
}
/**
@@ -698,7 +698,7 @@
ActionsDialogLite dialog = new ActionsDialogLite(
mContext,
- com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
+ com.android.systemui.res.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter,
mOverflowAdapter,
mSysuiColorExtractor,
@@ -776,7 +776,7 @@
@VisibleForTesting
protected final class PowerOptionsAction extends SinglePressAction {
private PowerOptionsAction() {
- super(com.android.systemui.R.drawable.ic_settings_power,
+ super(com.android.systemui.res.R.drawable.ic_settings_power,
R.string.global_action_power_options);
}
@@ -884,17 +884,17 @@
protected int getEmergencyTextColor(Context context) {
return context.getResources().getColor(
- com.android.systemui.R.color.global_actions_lite_text);
+ com.android.systemui.res.R.color.global_actions_lite_text);
}
protected int getEmergencyIconColor(Context context) {
return context.getResources().getColor(
- com.android.systemui.R.color.global_actions_lite_emergency_icon);
+ com.android.systemui.res.R.color.global_actions_lite_emergency_icon);
}
protected int getEmergencyBackgroundColor(Context context) {
return context.getResources().getColor(
- com.android.systemui.R.color.global_actions_lite_emergency_background);
+ com.android.systemui.res.R.color.global_actions_lite_emergency_background);
}
private class EmergencyAffordanceAction extends EmergencyAction {
@@ -912,7 +912,7 @@
@VisibleForTesting
class EmergencyDialerAction extends EmergencyAction {
private EmergencyDialerAction() {
- super(com.android.systemui.R.drawable.ic_emergency_star,
+ super(com.android.systemui.res.R.drawable.ic_emergency_star,
R.string.global_action_emergency);
}
@@ -1482,7 +1482,7 @@
Log.w(TAG, "No power options action found at position: " + position);
return null;
}
- int viewLayoutResource = com.android.systemui.R.layout.global_actions_power_item;
+ int viewLayoutResource = com.android.systemui.res.R.layout.global_actions_power_item;
View view = convertView != null ? convertView
: LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
view.setOnClickListener(v -> onClickItem(position));
@@ -1564,7 +1564,7 @@
Log.w(TAG, "No overflow action found at position: " + position);
return null;
}
- int viewLayoutResource = com.android.systemui.R.layout.controls_more_item;
+ int viewLayoutResource = com.android.systemui.res.R.layout.controls_more_item;
View view = convertView != null ? convertView
: LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
TextView textView = (TextView) view;
@@ -1778,7 +1778,7 @@
}
protected int getGridItemLayoutResource() {
- return com.android.systemui.R.layout.global_actions_grid_item_lite;
+ return com.android.systemui.res.R.layout.global_actions_grid_item_lite;
}
private enum ToggleState {
@@ -1870,7 +1870,7 @@
LayoutInflater inflater) {
willCreate();
- View v = inflater.inflate(com.android.systemui.R.layout.global_actions_grid_item_v2,
+ View v = inflater.inflate(com.android.systemui.res.R.layout.global_actions_grid_item_v2,
parent, false /* attach */);
ViewGroup.LayoutParams p = v.getLayoutParams();
p.width = WRAP_CONTENT;
@@ -2327,7 +2327,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setTitle(getContext().getString(
- com.android.systemui.R.string.accessibility_quick_settings_power_menu));
+ com.android.systemui.res.R.string.accessibility_quick_settings_power_menu));
initializeLayout();
mWindowDimAmount = getWindow().getAttributes().dimAmount;
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
@@ -2379,14 +2379,14 @@
GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
new ContextThemeWrapper(
mContext,
- com.android.systemui.R.style.Control_ListPopupWindow
+ com.android.systemui.res.R.style.Control_ListPopupWindow
), false /* isDropDownMode */);
popup.setOnItemClickListener(
(parent, view, position, id) -> mOverflowAdapter.onClickItem(position));
popup.setOnItemLongClickListener(
(parent, view, position, id) -> mOverflowAdapter.onLongClickItem(position));
View overflowButton =
- findViewById(com.android.systemui.R.id.global_actions_overflow_button);
+ findViewById(com.android.systemui.res.R.id.global_actions_overflow_button);
popup.setAnchorView(overflowButton);
popup.setAdapter(mOverflowAdapter);
return popup;
@@ -2403,14 +2403,14 @@
}
protected int getLayoutResource() {
- return com.android.systemui.R.layout.global_actions_grid_lite;
+ return com.android.systemui.res.R.layout.global_actions_grid_lite;
}
protected void initializeLayout() {
setContentView(getLayoutResource());
fixNavBarClipping();
- mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
+ mGlobalActionsLayout = findViewById(com.android.systemui.res.R.id.global_actions_view);
mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public boolean dispatchPopulateAccessibilityEvent(
@@ -2422,14 +2422,14 @@
});
mGlobalActionsLayout.setRotationListener(this::onRotate);
mGlobalActionsLayout.setAdapter(mAdapter);
- mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
+ mContainer = findViewById(com.android.systemui.res.R.id.global_actions_container);
mContainer.setOnTouchListener((v, event) -> {
mGestureDetector.onTouchEvent(event);
return v.onTouchEvent(event);
});
View overflowButton = findViewById(
- com.android.systemui.R.id.global_actions_overflow_button);
+ com.android.systemui.res.R.id.global_actions_overflow_button);
if (overflowButton != null) {
if (mOverflowAdapter.getCount() > 0) {
overflowButton.setOnClickListener((view) -> showPowerOverflowMenu());
@@ -2442,7 +2442,7 @@
LinearLayout.LayoutParams params =
(LinearLayout.LayoutParams) mGlobalActionsLayout.getLayoutParams();
params.setMarginEnd(mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.global_actions_side_margin));
+ com.android.systemui.res.R.dimen.global_actions_side_margin));
mGlobalActionsLayout.setLayoutParams(params);
}
}
@@ -2473,7 +2473,7 @@
private void showSmartLockDisabledMessage() {
// Since power menu is the top window, make a Toast-like view that will show up
View message = LayoutInflater.from(mContext)
- .inflate(com.android.systemui.R.layout.global_actions_toast, mContainer, false);
+ .inflate(com.android.systemui.res.R.layout.global_actions_toast, mContainer, false);
// Set up animation
AccessibilityManager mAccessibilityManager =
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
index 83046ef..bc1e13f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
@@ -27,7 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.HardwareBgDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Flat, single-row implementation of the button layout created by the global actions dialog.
@@ -55,7 +55,7 @@
}
private View getOverflowButton() {
- return findViewById(com.android.systemui.R.id.global_actions_overflow_button);
+ return findViewById(com.android.systemui.res.R.id.global_actions_overflow_button);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index e1462d1..2272b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -148,7 +148,7 @@
protected float getAnimationDistance() {
int rows = getListView().getRowCount();
float gridItemSize = getContext().getResources().getDimension(
- com.android.systemui.R.dimen.global_actions_grid_item_height);
+ com.android.systemui.res.R.dimen.global_actions_grid_item_height);
return rows * gridItemSize / 2;
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
index 183ee45..6afefb0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -25,7 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.HardwareBgDrawable;
import com.android.systemui.MultiListLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.util.leak.RotationUtils;
import java.util.Locale;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
index 42230ae..9b318a2 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
@@ -25,7 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.HardwareBgDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* ConstraintLayout implementation of the button layout created by the global actions dialog.
@@ -53,7 +53,7 @@
public void onUpdateList() {
super.onUpdateList();
int nElementsWrap = getResources().getInteger(
- com.android.systemui.R.integer.power_menu_lite_max_columns);
+ com.android.systemui.res.R.integer.power_menu_lite_max_columns);
int nChildren = getListView().getChildCount() - 1; // don't count flow element
// Avoid having just one action on the last row if there are more than 2 columns because
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
index 6d083f6..b9a6a55 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
@@ -27,7 +27,7 @@
import android.widget.ListPopupWindow;
import android.widget.ListView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Customized widget for use in the GlobalActionsDialog. Ensures common positioning and user
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
index caa88a3..b8bf142 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java
@@ -36,7 +36,7 @@
*/
public static Dialog create(@NonNull Context context, ListAdapter adapter) {
ViewGroup listView = (ViewGroup) LayoutInflater.from(context).inflate(
- com.android.systemui.R.layout.global_actions_power_dialog, null);
+ com.android.systemui.res.R.layout.global_actions_power_dialog, null);
for (int i = 0; i < adapter.getCount(); i++) {
View action = adapter.getView(i, null, listView);
@@ -53,7 +53,7 @@
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.setTitle(""); // prevent Talkback from speaking first item name twice
window.setBackgroundDrawable(res.getDrawable(
- com.android.systemui.R.drawable.control_background, context.getTheme()));
+ com.android.systemui.res.R.drawable.control_background, context.getTheme()));
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
return dialog;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
index 68dc1b3..51978ec 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
@@ -59,7 +59,7 @@
ScrimDrawable background = new ScrimDrawable();
final Dialog d = new Dialog(mContext,
- com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
+ com.android.systemui.res.R.style.Theme_SystemUI_Dialog_GlobalActions);
d.setOnShowListener(dialog -> {
if (mBlurUtils.supportsBlursOnWindows()) {
@@ -69,7 +69,7 @@
(int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255);
} else {
float backgroundAlpha = mContext.getResources().getFloat(
- com.android.systemui.R.dimen.shutdown_scrim_behind_alpha);
+ com.android.systemui.res.R.dimen.shutdown_scrim_behind_alpha);
background.setAlpha((int) (backgroundAlpha * 255));
}
});
@@ -96,7 +96,7 @@
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setBackgroundDrawable(background);
- window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi);
+ window.setWindowAnimations(com.android.systemui.res.R.style.Animation_ShutdownUi);
d.setContentView(getShutdownDialogContent(isReboot));
d.setCancelable(false);
@@ -104,10 +104,10 @@
int color;
if (mBlurUtils.supportsBlursOnWindows()) {
color = Utils.getColorAttrDefaultColor(mContext,
- com.android.systemui.R.attr.wallpaperTextColor);
+ com.android.systemui.res.R.attr.wallpaperTextColor);
} else {
color = mContext.getResources().getColor(
- com.android.systemui.R.color.global_actions_shutdown_ui_text);
+ com.android.systemui.res.R.color.global_actions_shutdown_ui_text);
}
ProgressBar bar = d.findViewById(R.id.progress);
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
new file mode 100644
index 0000000..d9b2c39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.haptics.slider
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Main
+import kotlin.math.abs
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+
+/**
+ * Slider tracker attached to a seekable slider.
+ *
+ * The tracker runs a state machine to execute actions on touch-based events typical of a seekable
+ * slider such as [android.widget.SeekBar]. Coroutines responsible for running the state machine,
+ * collecting slider events and maintaining waiting states are run on the main thread via the
+ * [com.android.systemui.dagger.qualifiers.Main] coroutine dispatcher.
+ *
+ * @param[sliderStateListener] Listener of the slider state.
+ * @param[sliderEventProducer] Producer of slider events arising from the slider.
+ * @param[mainDispatcher] [CoroutineDispatcher] used to launch coroutines for the collection of
+ * slider events and the launch of timer jobs.
+ * @property[config] Configuration parameters of the slider tracker.
+ */
+class SeekableSliderTracker(
+ sliderStateListener: SliderStateListener,
+ sliderEventProducer: SliderEventProducer,
+ @Main mainDispatcher: CoroutineDispatcher,
+ private val config: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
+) : SliderTracker(CoroutineScope(mainDispatcher), sliderStateListener, sliderEventProducer) {
+
+ // History of the latest progress collected from slider events
+ private var latestProgress = 0f
+ // Timer job for the wait state
+ private var timerJob: Job? = null
+ // Indicator that there is waiting job active
+ var isWaiting = false
+ private set
+ get() = timerJob != null && timerJob?.isActive == true
+
+ override suspend fun iterateState(event: SliderEvent) {
+ when (currentState) {
+ SliderState.IDLE -> handleIdle(event.type)
+ SliderState.WAIT -> handleWait(event.type, event.currentProgress)
+ SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH -> handleAcquired(event.type)
+ SliderState.DRAG_HANDLE_DRAGGING -> handleDragging(event.type, event.currentProgress)
+ SliderState.DRAG_HANDLE_REACHED_BOOKEND ->
+ handleReachedBookend(event.type, event.currentProgress)
+ SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH -> setState(SliderState.IDLE)
+ SliderState.JUMP_TRACK_LOCATION_SELECTED -> handleJumpToTrack(event.type)
+ SliderState.JUMP_BOOKEND_SELECTED -> handleJumpToBookend(event.type)
+ }
+ latestProgress = event.currentProgress
+ }
+
+ private fun handleIdle(newEventType: SliderEventType) {
+ if (newEventType == SliderEventType.STARTED_TRACKING_TOUCH) {
+ timerJob = launchTimer()
+ // The WAIT state will wait for the timer to complete or a slider progress to occur.
+ // This will disambiguate between an imprecise touch that acquires the slider handle,
+ // and a select and jump operation in the slider track.
+ setState(SliderState.WAIT)
+ }
+ }
+
+ private fun launchTimer() =
+ scope.launch {
+ delay(config.waitTimeMillis)
+ if (isActive && currentState == SliderState.WAIT) {
+ setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+ // This transitory state must also trigger the corresponding action
+ executeOnState(currentState)
+ }
+ }
+
+ private fun handleWait(newEventType: SliderEventType, currentProgress: Float) {
+ // The timer may have completed and may have already modified the state
+ if (currentState != SliderState.WAIT) return
+
+ // The timer is still running but the state may be modified by the progress change
+ val deltaProgressIsJump = deltaProgressIsAboveThreshold(currentProgress)
+ if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_USER) {
+ if (bookendReached(currentProgress)) {
+ setState(SliderState.JUMP_BOOKEND_SELECTED)
+ } else if (deltaProgressIsJump) {
+ setState(SliderState.JUMP_TRACK_LOCATION_SELECTED)
+ } else {
+ setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+ }
+ } else if (newEventType == SliderEventType.STOPPED_TRACKING_TOUCH) {
+ setState(SliderState.IDLE)
+ }
+
+ // If the state changed, the timer does not need to complete. No further synchronization
+ // will be required onwards until WAIT is reached again.
+ if (currentState != SliderState.WAIT) {
+ timerJob?.cancel()
+ timerJob = null
+ }
+ }
+
+ private fun handleAcquired(newEventType: SliderEventType) {
+ if (newEventType == SliderEventType.STOPPED_TRACKING_TOUCH) {
+ setState(SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH)
+ } else if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_USER) {
+ setState(SliderState.DRAG_HANDLE_DRAGGING)
+ }
+ }
+
+ private fun handleDragging(newEventType: SliderEventType, currentProgress: Float) {
+ if (newEventType == SliderEventType.STOPPED_TRACKING_TOUCH) {
+ setState(SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH)
+ } else if (
+ newEventType == SliderEventType.PROGRESS_CHANGE_BY_USER &&
+ bookendReached(currentProgress)
+ ) {
+ setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+ }
+ }
+
+ private fun handleReachedBookend(newEventType: SliderEventType, currentProgress: Float) {
+ if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_USER) {
+ if (!bookendReached(currentProgress)) {
+ setState(SliderState.DRAG_HANDLE_DRAGGING)
+ }
+ } else if (newEventType == SliderEventType.STOPPED_TRACKING_TOUCH) {
+ setState(SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH)
+ }
+ }
+
+ private fun handleJumpToTrack(newEventType: SliderEventType) {
+ when (newEventType) {
+ SliderEventType.PROGRESS_CHANGE_BY_USER -> setState(SliderState.DRAG_HANDLE_DRAGGING)
+ SliderEventType.STOPPED_TRACKING_TOUCH ->
+ setState(SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH)
+ else -> {}
+ }
+ }
+
+ private fun handleJumpToBookend(newEventType: SliderEventType) {
+ when (newEventType) {
+ SliderEventType.PROGRESS_CHANGE_BY_USER -> setState(SliderState.DRAG_HANDLE_DRAGGING)
+ SliderEventType.STOPPED_TRACKING_TOUCH ->
+ setState(SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH)
+ else -> {}
+ }
+ }
+
+ override fun executeOnState(currentState: SliderState) {
+ when (currentState) {
+ SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH -> sliderListener.onHandleAcquiredByTouch()
+ SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH -> {
+ sliderListener.onHandleReleasedFromTouch()
+ // This transitory state must also reset the state machine
+ resetState()
+ }
+ SliderState.DRAG_HANDLE_DRAGGING -> sliderListener.onProgress(latestProgress)
+ SliderState.DRAG_HANDLE_REACHED_BOOKEND -> executeOnBookend()
+ SliderState.JUMP_TRACK_LOCATION_SELECTED ->
+ sliderListener.onProgressJump(latestProgress)
+ SliderState.JUMP_BOOKEND_SELECTED -> executeOnBookend()
+ else -> {}
+ }
+ }
+
+ private fun executeOnBookend() {
+ if (latestProgress >= config.upperBookendThreshold) sliderListener.onUpperBookend()
+ else sliderListener.onLowerBookend()
+ }
+
+ override fun resetState() {
+ timerJob?.cancel()
+ timerJob = null
+ super.resetState()
+ }
+
+ private fun deltaProgressIsAboveThreshold(
+ currentProgress: Float,
+ epsilon: Float = 0.00001f,
+ ): Boolean {
+ val delta = abs(currentProgress - latestProgress)
+ return abs(delta - config.jumpThreshold) < epsilon
+ }
+
+ private fun bookendReached(currentProgress: Float): Boolean {
+ return currentProgress >= config.upperBookendThreshold ||
+ currentProgress <= config.lowerBookendThreshold
+ }
+
+ @VisibleForTesting
+ fun setState(state: SliderState) {
+ currentState = state
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTrackerConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTrackerConfig.kt
new file mode 100644
index 0000000..cb0f43b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTrackerConfig.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.haptics.slider
+
+import androidx.annotation.FloatRange
+
+/**
+ * Configuration parameters of a seekable slider tracker.
+ *
+ * @property[waitTimeMillis] Wait period to determine if a touch event acquires the slider handle.
+ * @property[jumpThreshold] Threshold on the slider progress to detect if a touch event is qualified
+ * as an imprecise acquisition of the slider handle.
+ * @property[lowerBookendThreshold] Threshold to determine the progress on the slider that qualifies
+ * as reaching the lower bookend.
+ * @property[upperBookendThreshold] Threshold to determine the progress on the slider that qualifies
+ * as reaching the upper bookend.
+ */
+data class SeekableSliderTrackerConfig(
+ val waitTimeMillis: Long = 100,
+ @FloatRange(from = 0.0, to = 1.0) val jumpThreshold: Float = 0.02f,
+ @FloatRange(from = 0.0, to = 1.0) val lowerBookendThreshold: Float = 0.05f,
+ @FloatRange(from = 0.0, to = 1.0) val upperBookendThreshold: Float = 0.95f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
new file mode 100644
index 0000000..20d99d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.haptics.slider
+
+import androidx.annotation.FloatRange
+
+/** Configuration parameters of a [SliderHapticFeedbackProvider] */
+data class SliderHapticFeedbackConfig(
+ /** Interpolator factor for velocity-based vibration scale interpolations. Must be positive */
+ val velocityInterpolatorFactor: Float = 1f,
+ /** Interpolator factor for progress-based vibration scale interpolations. Must be positive */
+ val progressInterpolatorFactor: Float = 1f,
+ /** Minimum vibration scale for vibrations based on slider progress */
+ @FloatRange(from = 0.0, to = 1.0) val progressBasedDragMinScale: Float = 0f,
+ /** Maximum vibration scale for vibrations based on slider progress */
+ @FloatRange(from = 0.0, to = 1.0) val progressBasedDragMaxScale: Float = 0.2f,
+ /** Additional vibration scaling due to velocity */
+ @FloatRange(from = 0.0, to = 1.0) val additionalVelocityMaxBump: Float = 0.15f,
+ /** Additional time delta to wait between drag texture vibrations */
+ @FloatRange(from = 0.0) val deltaMillisForDragInterval: Float = 0f,
+ /** Number of low ticks in a drag texture composition. This is not expected to change */
+ val numberOfLowTicks: Int = 5,
+ /** Maximum velocity allowed for vibration scaling. This is not expected to change. */
+ val maxVelocityToScale: Float = 2000f, /* In pixels/sec */
+ /** Vibration scale at the upper bookend of the slider */
+ @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f,
+ /** Vibration scale at the lower bookend of the slider */
+ @FloatRange(from = 0.0, to = 1.0) val lowerBookendScale: Float = 0.05f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
new file mode 100644
index 0000000..e6de156
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.view.VelocityTracker
+import android.view.animation.AccelerateInterpolator
+import androidx.annotation.FloatRange
+import com.android.systemui.statusbar.VibratorHelper
+import kotlin.math.abs
+import kotlin.math.min
+
+/**
+ * Listener of slider events that triggers haptic feedback.
+ *
+ * @property[vibratorHelper] Singleton instance of the [VibratorHelper] to deliver haptics.
+ * @property[velocityTracker] Instance of a [VelocityTracker] that tracks slider dragging velocity.
+ * @property[config] Configuration parameters for vibration encapsulated as a
+ * [SliderHapticFeedbackConfig].
+ * @property[clock] Clock to obtain elapsed real time values.
+ */
+class SliderHapticFeedbackProvider(
+ private val vibratorHelper: VibratorHelper,
+ private val velocityTracker: VelocityTracker,
+ private val config: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(),
+ private val clock: com.android.systemui.util.time.SystemClock,
+) : SliderStateListener {
+
+ private val velocityAccelerateInterpolator =
+ AccelerateInterpolator(config.velocityInterpolatorFactor)
+ private val positionAccelerateInterpolator =
+ AccelerateInterpolator(config.progressInterpolatorFactor)
+ private var dragTextureLastTime = clock.elapsedRealtime()
+ private val lowTickDurationMs =
+ vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0]
+ private var hasVibratedAtLowerBookend = false
+ private var hasVibratedAtUpperBookend = false
+
+ /** Time threshold to wait before making new API call. */
+ private val thresholdUntilNextDragCallMillis =
+ lowTickDurationMs * config.numberOfLowTicks + config.deltaMillisForDragInterval
+
+ /**
+ * Vibrate when the handle reaches either bookend with a certain velocity.
+ *
+ * @param[absoluteVelocity] Velocity of the handle when it reached the bookend.
+ */
+ private fun vibrateOnEdgeCollision(absoluteVelocity: Float) {
+ val velocityInterpolated =
+ velocityAccelerateInterpolator.getInterpolation(
+ min(absoluteVelocity / config.maxVelocityToScale, 1f)
+ )
+ val bookendScaleRange = config.upperBookendScale - config.lowerBookendScale
+ val bookendsHitScale = bookendScaleRange * velocityInterpolated + config.lowerBookendScale
+
+ val vibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, bookendsHitScale)
+ .compose()
+ vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING)
+ }
+
+ /**
+ * Create a drag texture vibration based on velocity and slider progress.
+ *
+ * @param[absoluteVelocity] Absolute velocity of the handle.
+ * @param[normalizedSliderProgress] Progress of the slider handled normalized to the range from
+ * 0F to 1F (inclusive).
+ */
+ private fun vibrateDragTexture(
+ absoluteVelocity: Float,
+ @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float
+ ) {
+ // Check if its time to vibrate
+ val currentTime = clock.elapsedRealtime()
+ val elapsedSinceLastDrag = currentTime - dragTextureLastTime
+ if (elapsedSinceLastDrag < thresholdUntilNextDragCallMillis) return
+
+ val velocityInterpolated =
+ velocityAccelerateInterpolator.getInterpolation(
+ min(absoluteVelocity / config.maxVelocityToScale, 1f)
+ )
+
+ // Scaling of vibration due to the position of the slider
+ val positionScaleRange = config.progressBasedDragMaxScale - config.progressBasedDragMinScale
+ val sliderProgressInterpolated =
+ positionAccelerateInterpolator.getInterpolation(normalizedSliderProgress)
+ val positionBasedScale =
+ positionScaleRange * sliderProgressInterpolated + config.progressBasedDragMinScale
+
+ // Scaling bump due to velocity
+ val velocityBasedScale = velocityInterpolated * config.additionalVelocityMaxBump
+
+ // Total scale
+ val scale = positionBasedScale + velocityBasedScale
+
+ // Trigger the vibration composition
+ val composition = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scale)
+ }
+ vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING)
+ dragTextureLastTime = currentTime
+ }
+
+ override fun onHandleAcquiredByTouch() {}
+
+ override fun onHandleReleasedFromTouch() {}
+
+ override fun onLowerBookend() {
+ if (!hasVibratedAtLowerBookend) {
+ velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
+ vibrateOnEdgeCollision(abs(velocityTracker.xVelocity))
+ hasVibratedAtLowerBookend = true
+ }
+ }
+
+ override fun onUpperBookend() {
+ if (!hasVibratedAtUpperBookend) {
+ velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
+ vibrateOnEdgeCollision(abs(velocityTracker.xVelocity))
+ hasVibratedAtUpperBookend = true
+ }
+ }
+
+ override fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
+ velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
+ vibrateDragTexture(abs(velocityTracker.xVelocity), progress)
+ hasVibratedAtUpperBookend = false
+ hasVibratedAtLowerBookend = false
+ }
+
+ override fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
+
+ override fun onSelectAndArrow(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
+
+ private companion object {
+ private val VIBRATION_ATTRIBUTES_PIPELINING =
+ VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+ .build()
+ private const val UNITS_SECOND = 1000
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
new file mode 100644
index 0000000..fe092e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.haptics.slider
+
+/** State of a slider */
+enum class SliderState {
+ /* The slider is idle */
+ IDLE,
+ /* Waiting state to disambiguate between handle acquisition and select and jump operations */
+ WAIT,
+ /* The slider handle was acquired by touch. */
+ DRAG_HANDLE_ACQUIRED_BY_TOUCH,
+ /* The slider handle was released. */
+ DRAG_HANDLE_RELEASED_FROM_TOUCH,
+ /* The slider handle is being dragged by touch. */
+ DRAG_HANDLE_DRAGGING,
+ /* The slider handle reached a bookend. */
+ DRAG_HANDLE_REACHED_BOOKEND,
+ /* A location in the slider track has been selected. */
+ JUMP_TRACK_LOCATION_SELECTED,
+ /* The slider handled moved to a bookend after it was selected. */
+ JUMP_BOOKEND_SELECTED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt
new file mode 100644
index 0000000..9c99c90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.haptics.slider
+
+import androidx.annotation.FloatRange
+
+/** Listener of events from a slider (such as [android.widget.SeekBar]) */
+interface SliderStateListener {
+
+ /** Notification that the handle is acquired by touch */
+ fun onHandleAcquiredByTouch()
+
+ /** Notification that the handle was released from touch */
+ fun onHandleReleasedFromTouch()
+
+ /** Notification that the handle reached the lower bookend */
+ fun onLowerBookend()
+
+ /** Notification that the handle reached the upper bookend */
+ fun onUpperBookend()
+
+ /**
+ * Notification that the slider reached a certain progress on the slider track.
+ *
+ * This method is called in all intermediate steps of a continuous progress change as the slider
+ * moves through the slider track.
+ *
+ * @param[progress] The progress of the slider in the range from 0F to 1F (inclusive).
+ */
+ fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float)
+
+ /**
+ * Notification that the slider handle jumped to a selected progress on the slider track.
+ *
+ * This method is specific to the case when the handle performed a single jump to a position on
+ * the slider track and reached the corresponding progress. In this case, [onProgress] is not
+ * called and the new progress reached is represented by the [progress] parameter.
+ *
+ * @param[progress] The selected progress on the slider track that the handle jumps to. The
+ * progress is in the range from 0F to 1F (inclusive).
+ */
+ fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float)
+
+ /**
+ * Notification that the slider handle was moved by a button press.
+ *
+ * @param[progress] The progress of the slider in the range from 0F to 1F (inclusive).
+ */
+ fun onSelectAndArrow(@FloatRange(from = 0.0, to = 1.0) progress: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt
new file mode 100644
index 0000000..002b5aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.haptics.slider
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+
+/**
+ * Tracker component for a slider.
+ *
+ * The tracker maintains a state machine operated by slider events coming from a
+ * [SliderEventProducer]. An action is executed in each state via a [SliderListener].
+ *
+ * @property[scope] [CoroutineScope] to launch the collection of [SliderEvent] and state machine
+ * logic.
+ * @property[sliderListener] [SliderListener] to execute actions on a given [SliderState].
+ * @property[eventProducer] Producer of [SliderEvent] to iterate over a state machine.
+ */
+sealed class SliderTracker(
+ protected val scope: CoroutineScope,
+ protected val sliderListener: SliderStateListener,
+ protected val eventProducer: SliderEventProducer,
+) {
+
+ /* Reference to the current state of the internal state machine */
+ var currentState: SliderState = SliderState.IDLE
+ protected set
+
+ /**
+ * Job that launches and maintains the coroutine that collects events and operates the state
+ * machine.
+ */
+ protected var job: Job? = null
+
+ /** Indicator that the tracker is active and tracking */
+ var isTracking = false
+ get() = job != null && job?.isActive == true
+ private set
+
+ /** Starts the [Job] that collects slider events and runs the state machine */
+ fun startTracking() {
+ job =
+ scope.launch {
+ eventProducer.produceEvents().collect { event ->
+ iterateState(event)
+ executeOnState(currentState)
+ }
+ }
+ }
+
+ /** Stops the collection of slider events and the state machine */
+ fun stopTracking() {
+ job?.cancel("Stopped tracking slider state")
+ job = null
+ resetState()
+ }
+
+ /**
+ * Iterate through the state machine due to a new slider event. As a result, the current state
+ * is modified.
+ *
+ * @param[event] The slider event that is received.
+ */
+ protected abstract suspend fun iterateState(event: SliderEvent)
+
+ /**
+ * Execute an action based on the state of the state machine. This method should use the
+ * [SliderListener] to act on the current state.
+ *
+ * @param[currentState] A [SliderState] in the state machine
+ */
+ protected abstract fun executeOnState(currentState: SliderState)
+
+ /** Reset the state machine by setting the current state to [SliderState.IDLE] */
+ protected open fun resetState() {
+ currentState = SliderState.IDLE
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index ad7973e..1cdbe6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -50,7 +50,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index 6f25f7c..e16bb0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -38,7 +38,7 @@
import androidx.annotation.IdRes
import androidx.core.view.setPadding
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
class KeyboardBacklightDialog(
context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 732102e..1f69cc0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -48,7 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 257006e..fb02c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -18,49 +18,30 @@
package com.android.systemui.keyguard
import android.content.Context
-import android.content.res.Configuration
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
@@ -74,30 +55,17 @@
@Inject
constructor(
private val keyguardRootView: KeyguardRootView,
- private val sharedNotificationContainer: SharedNotificationContainer,
private val keyguardRootViewModel: KeyguardRootViewModel,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
- private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
- private val keyguardQuickAffordancesCombinedViewModel:
- KeyguardQuickAffordancesCombinedViewModel,
- private val falsingManager: FalsingManager,
- private val vibratorHelper: VibratorHelper,
private val keyguardStateController: KeyguardStateController,
- private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
- private val activityStarter: ActivityStarter,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
- private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
- private val communalWidgetViewModel: CommunalWidgetViewModel,
- private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
- private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
@@ -105,10 +73,7 @@
private var rootViewHandle: DisposableHandle? = null
private var indicationAreaHandle: DisposableHandle? = null
- private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
- private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
- private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
- private var settingsPopupMenuHandle: DisposableHandle? = null
+
var keyguardStatusViewController: KeyguardStatusViewController? = null
get() {
if (field == null) {
@@ -127,52 +92,12 @@
override fun start() {
bindKeyguardRootView()
- if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
- keyguardRootView.removeAllViews()
- initializeViews()
- } else {
- val notificationPanel =
- notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
- unbindKeyguardBottomArea(notificationPanel)
- bindIndicationArea()
- bindLockIconView(notificationPanel)
- bindKeyguardStatusView(notificationPanel)
- setupNotificationStackScrollLayout(notificationPanel)
- bindLeftShortcut()
- bindRightShortcut()
- bindAmbientIndicationArea()
- bindSettingsPopupMenu()
- bindCommunalWidgetArea()
- }
+ initializeViews()
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
}
- fun setupNotificationStackScrollLayout(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- // This moves the existing NSSL view to a different parent, as the controller is a
- // singleton and recreating it has other bad side effects
- val nssl =
- legacyParent.requireViewById<View>(R.id.notification_stack_scroller).also {
- (it.getParent() as ViewGroup).removeView(it)
- }
- sharedNotificationContainer.addNotificationStackScrollLayout(nssl)
- SharedNotificationContainerBinder.bind(
- sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- notificationStackScrollerLayoutController,
- )
- }
- }
-
- override fun onConfigurationChanged(newConfig: Configuration?) {
- super.onConfigurationChanged(newConfig)
- leftShortcutHandle?.onConfigurationChanged()
- rightShortcutHandle?.onConfigurationChanged()
- ambientIndicationAreaHandle?.onConfigurationChanged()
- }
-
fun bindIndicationArea() {
indicationAreaHandle?.dispose()
@@ -213,135 +138,6 @@
)
}
- private fun bindLockIconView(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- legacyParent.requireViewById<View>(R.id.lock_icon_view).let {
- legacyParent.removeView(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
- keyguardRootView.removeView(it)
- }
- legacyParent.requireViewById<LockIconView>(R.id.lock_icon_view).let {
- lockIconViewController.setLockIconView(it)
- }
- }
- }
-
- private fun bindAmbientIndicationArea() {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- ambientIndicationAreaHandle?.destroy()
- ambientIndicationAreaHandle =
- KeyguardAmbientIndicationAreaViewBinder.bind(
- notificationShadeWindowView,
- keyguardAmbientIndicationViewModel,
- keyguardRootViewModel,
- )
- } else {
- keyguardRootView.findViewById<View?>(R.id.ambient_indication_container)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindSettingsPopupMenu() {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- settingsPopupMenuHandle?.dispose()
- settingsPopupMenuHandle =
- KeyguardSettingsViewBinder.bind(
- keyguardRootView,
- keyguardSettingsMenuViewModel,
- vibratorHelper,
- activityStarter,
- )
- } else {
- keyguardRootView.findViewById<View?>(R.id.keyguard_settings_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun unbindKeyguardBottomArea(legacyParent: ViewGroup) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- legacyParent.requireViewById<View>(R.id.keyguard_bottom_area).let {
- legacyParent.removeView(it)
- }
- }
- }
-
- private fun bindLeftShortcut() {
- leftShortcutHandle?.destroy()
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- leftShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.start_button),
- keyguardQuickAffordancesCombinedViewModel.startButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.start_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindRightShortcut() {
- rightShortcutHandle?.destroy()
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- rightShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.end_button),
- keyguardQuickAffordancesCombinedViewModel.endButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
- }
- } else {
- keyguardRootView.findViewById<View?>(R.id.end_button)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- fun bindKeyguardStatusView(legacyParent: ViewGroup) {
- // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
- // Disable one of them
- if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
- legacyParent.findViewById<View>(R.id.keyguard_status_view)?.let {
- legacyParent.removeView(it)
- }
-
- val keyguardStatusView = keyguardRootView.addStatusView()
- val statusViewComponent = keyguardStatusViewComponentFactory.build(keyguardStatusView)
- val controller = statusViewComponent.getKeyguardStatusViewController()
- controller.init()
- keyguardStatusViewController = controller
- } else {
- keyguardRootView.findViewById<View?>(R.id.keyguard_status_view)?.let {
- keyguardRootView.removeView(it)
- }
- }
- }
-
- private fun bindCommunalWidgetArea() {
- if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return
- }
-
- CommunalWidgetViewBinder.bind(
- keyguardRootView,
- communalWidgetViewModel,
- communalWidgetViewAdapter,
- keyguardBlueprintInteractor,
- )
- }
-
/**
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9915720..b506a36 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -128,7 +128,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -1968,6 +1968,10 @@
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
+ if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown");
+ return;
+ }
// hiding keyguard that is showing, remember to reshow later
if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+ "disabling status bar expansion");
@@ -2193,7 +2197,7 @@
* Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
- // if another app is disabling us, don't show unless we're in lockdown mode
+ // if another app is disabling us, don't show
if (!mExternallyEnabled
&& !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
@@ -3239,7 +3243,11 @@
+ "mPendingLock=" + mPendingLock + "."
+ "One of these being false means we re-locked the device during unlock. "
+ "Do not proceed to finish keyguard exit and unlock.");
+ doKeyguardLocked(null);
finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
+ // Ensure WM is notified that we made a decision to show
+ setShowingLocked(true /* showing */, true /* force */);
+
return;
}
@@ -3790,6 +3798,13 @@
}
/**
+ * Notify whether keyguard has created a remote animation runner for next app launch.
+ */
+ public void launchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
+ mKeyguardTransitions.setLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen);
+ }
+
+ /**
* Implementation of RemoteAnimationRunner that creates a new
* {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the
* remote animation methods to that runner.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index ffd8a02..d5316cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -30,7 +30,7 @@
import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.time.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 75677f2..8ebcece 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -45,8 +45,13 @@
/**
* Whether the lockscreen is showing, which we pass to [IActivityTaskManager.setLockScreenShown]
* in order to show the lockscreen and hide the surface behind the keyguard (or the inverse).
+ *
+ * This value is null if we have not yet called setLockScreenShown with any value. This will
+ * happen during the boot sequence, but we can't default to true here since otherwise we'll
+ * short-circuit on the first call to setLockScreenShown since we'll think we're already
+ * showing.
*/
- private var isLockscreenShowing = true
+ private var isLockscreenShowing: Boolean? = null
/**
* Whether AOD is showing, which we pass to [IActivityTaskManager.setLockScreenShown] in order
@@ -102,7 +107,7 @@
// The surface behind is always visible if the lockscreen is not showing, so we're already
// visible.
- if (visible && !isLockscreenShowing) {
+ if (visible && isLockscreenShowing != true) {
Log.d(TAG, "#setVisibility -> already visible since the lockscreen isn't showing")
return
}
@@ -159,9 +164,23 @@
}
}
+ /**
+ * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
+ *
+ * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence,
+ * when we haven't yet called ATMS#setLockScreenShown. Typically,
+ * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before
+ * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but
+ * if so, true should be the right choice.
+ */
private fun setWmLockscreenState(
- lockscreenShowing: Boolean = this.isLockscreenShowing,
- aodVisible: Boolean = this.isAodVisible
+ lockscreenShowing: Boolean = this.isLockscreenShowing ?: true.also {
+ Log.d(TAG, "Using isLockscreenShowing=true default in setWmLockscreenState, " +
+ "because setAodVisible was called before the first setLockscreenShown " +
+ "call during boot. This is not typical, but is theoretically possible. " +
+ "If you're investigating the lockscreen showing unexpectedly, start here.")
+ },
+ aodVisible: Boolean = this.isAodVisible
) {
Log.d(
TAG,
@@ -201,6 +220,6 @@
}
companion object {
- private val TAG = this::class.java.simpleName
+ private val TAG = WindowManagerLockscreenVisibilityManager::class.java.simpleName
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 82be009..09875a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -37,7 +37,7 @@
import android.window.OnBackInvokedDispatcher;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 13dfe24..03c166c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.data.repository.CommunalRepositoryModule;
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -93,6 +94,7 @@
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
+ CommunalRepositoryModule.class,
CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index af0abdf..77e8179 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@
import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.camera.CameraGestureHelper
import com.android.systemui.common.shared.model.ContentDescription
@@ -34,7 +34,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -58,16 +58,21 @@
get() = R.drawable.ic_camera
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() =
- flowOf(
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon =
- Icon.Resource(
- R.drawable.ic_camera,
- ContentDescription.Resource(R.string.accessibility_camera_button)
- )
- )
+ get() = flow {
+ emit(
+ if (isLaunchable()) {
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ R.drawable.ic_camera,
+ ContentDescription.Resource(R.string.accessibility_camera_button)
+ )
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+ }
)
+ }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (isLaunchable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index 16385ec..9b5f461 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -27,7 +27,7 @@
import android.service.notification.ZenModeConfig
import com.android.settingslib.notification.EnableZenModeDialog
import com.android.settingslib.notification.ZenModeDialogMetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index ed8823a..a7999c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -18,7 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index f3fc809..cc36961 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@
import android.content.Context
import android.content.Intent
import androidx.annotation.DrawableRes
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -78,6 +78,7 @@
component.getControlsListingController().getOrNull()?.getCurrentServices()
val hasFavorites =
component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true
+ val hasPanels = currentServices?.any { it.panelActivity != null } == true
val componentPackageName = component.getPackageName()
when {
currentServices.isNullOrEmpty() && !componentPackageName.isNullOrEmpty() -> {
@@ -100,8 +101,8 @@
),
)
}
- !hasFavorites -> {
- // Home app installed but no favorites selected.
+ !hasFavorites && !hasPanels -> {
+ // Home app installed but no favorites selected or panel activities available.
val activityClass = component.getControlsUiController().get().resolveActivity()
return disabledPickerState(
explanation =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 320d158..1cf6183 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@
import android.content.Context
import android.content.Intent
import android.net.Uri
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index 4dad179..deedbdb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -20,7 +20,7 @@
import android.content.Context
import android.content.IntentFilter
import android.content.SharedPreferences
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.backup.BackupHelper
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 2503568..1fe94d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index 45277b8..a503541 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -18,7 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 5d7a3d4..7337292 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -24,7 +24,7 @@
import android.service.quickaccesswallet.QuickAccessWalletClient
import android.service.quickaccesswallet.WalletCard
import android.util.Log
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -34,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.wallet.controller.QuickAccessWalletController
+import com.android.systemui.wallet.util.getPaymentCards
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -60,7 +61,7 @@
val callback =
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
- val hasCards = response?.walletCards?.isNotEmpty() == true
+ val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
trySendWithFailureLogging(
state(
isFeatureEnabled = isWalletAvailable(),
@@ -135,7 +136,7 @@
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
continuation.resumeWith(
- Result.success(response?.walletCards ?: emptyList())
+ Result.success(getPaymentCards(response.walletCards) ?: emptyList())
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index 1ccc689..bbdd903 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -22,7 +22,7 @@
import android.content.Context
import android.content.Intent
import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.camera.CameraIntents
import com.android.systemui.camera.CameraIntentsWrapper
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 682e841..06cf723 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -26,7 +26,7 @@
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 6db21b2..fd048ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -18,14 +18,13 @@
import android.app.StatusBarManager
import android.content.Context
-import android.hardware.face.FaceAuthenticateOptions
import android.hardware.face.FaceManager
import android.os.CancellationSignal
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -47,6 +46,7 @@
import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
@@ -54,13 +54,13 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
+import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.Arrays
import java.util.stream.Collectors
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -112,33 +112,27 @@
fun setLockedOut(isLockedOut: Boolean)
/**
- * Cancel current face authentication and prevent it from running until [resumeFaceAuth] is
- * invoked.
- */
- fun pauseFaceAuth()
-
- /**
- * Allow face auth paused using [pauseFaceAuth] to run again. The next invocation to
- * [authenticate] will run as long as other gating conditions don't stop it from running.
- */
- fun resumeFaceAuth()
-
- /**
- * Trigger face authentication.
+ * Request face authentication or detection to be run.
*
* [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
* ignored if face authentication is already running. Results should be propagated through
* [authenticationStatus]
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+ *
+ * Method returns immediately and the face auth request is processed as soon as possible.
*/
- suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+ fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
/** Stop currently running face authentication or detection. */
fun cancel()
}
-@OptIn(ExperimentalCoroutinesApi::class)
+private data class AuthenticationRequest(
+ val uiEvent: FaceAuthUiEvent,
+ val fallbackToDetection: Boolean
+)
+
@SysUISingleton
class DeviceEntryFaceAuthRepositoryImpl
@Inject
@@ -171,6 +165,8 @@
private var faceAcquiredInfoIgnoreList: Set<Int>
private var retryCount = 0
+ private var pendingAuthenticateRequest = MutableStateFlow<AuthenticationRequest?>(null)
+
private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null
@@ -193,15 +189,6 @@
override val isAuthRunning: StateFlow<Boolean>
get() = _isAuthRunning
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
private val keyguardSessionId: InstanceId?
get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
@@ -213,6 +200,8 @@
override val isAuthenticated: Flow<Boolean>
get() = _isAuthenticated
+ private var cancellationInProgress = MutableStateFlow(false)
+
override val isBypassEnabled: Flow<Boolean> =
keyguardBypassController?.let {
conflatedCallbackFlow {
@@ -302,6 +291,7 @@
observeFaceDetectGatingChecks()
observeFaceAuthResettingConditions()
listenForSchedulingWatchdog()
+ processPendingAuthRequests()
} else {
canRunFaceAuth = MutableStateFlow(false).asStateFlow()
canRunDetection = MutableStateFlow(false).asStateFlow()
@@ -338,6 +328,7 @@
)
.onEach { anyOfThemIsTrue ->
if (anyOfThemIsTrue) {
+ clearPendingAuthRequest("Resetting auth status")
_isAuthenticated.value = false
retryCount = 0
halErrorRetryJob?.cancel()
@@ -346,6 +337,15 @@
.launchIn(applicationScope)
}
+ private fun clearPendingAuthRequest(@CompileTimeConstant loggingContext: String) {
+ faceAuthLogger.clearingPendingAuthRequest(
+ loggingContext,
+ pendingAuthenticateRequest.value?.uiEvent,
+ pendingAuthenticateRequest.value?.fallbackToDetection
+ )
+ pendingAuthenticateRequest.value = null
+ }
+
private fun observeFaceDetectGatingChecks() {
canRunDetection
.onEach {
@@ -378,7 +378,6 @@
biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
"isFaceAuthEnrolledAndEnabled"
),
- Pair(faceAuthPaused.isFalse(), "faceAuthIsNotPaused"),
Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
Pair(
keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
@@ -402,7 +401,13 @@
biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
"userHasNotLockedDownDevice"
),
- Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing")
+ Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing"),
+ Pair(
+ userRepository.selectedUser
+ .map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS }
+ .isFalse(),
+ "userSwitchingInProgress"
+ )
)
}
@@ -440,9 +445,6 @@
}
_authenticationStatus.value = errorStatus
_isAuthenticated.value = false
- if (errorStatus.isCancellationError()) {
- handleFaceCancellationError()
- }
if (errorStatus.isHardwareError()) {
faceAuthLogger.hardwareError(errorStatus)
handleFaceHardwareError()
@@ -471,16 +473,6 @@
}
}
- private fun handleFaceCancellationError() {
- applicationScope.launch {
- faceAuthRequestedWhileCancellation?.let {
- faceAuthLogger.launchingQueuedFaceAuthRequest(it)
- authenticate(it)
- }
- faceAuthRequestedWhileCancellation = null
- }
- }
-
private fun handleFaceHardwareError() {
if (retryCount < HAL_ERROR_RETRY_MAX) {
retryCount++
@@ -490,7 +482,7 @@
delay(HAL_ERROR_RETRY_TIMEOUT)
if (retryCount < HAL_ERROR_RETRY_MAX) {
faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
- authenticate(
+ requestAuthenticate(
FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
fallbackToDetection = false
)
@@ -501,7 +493,7 @@
private fun onFaceAuthRequestCompleted() {
cancelNotReceivedHandlerJob?.cancel()
- cancellationInProgress = false
+ cancellationInProgress.value = false
_isAuthRunning.value = false
authCancellationSignal = null
}
@@ -512,24 +504,60 @@
_detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
}
- private var cancellationInProgress = false
- private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ if (pendingAuthenticateRequest.value != null) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ pendingAuthenticateRequest.value?.uiEvent,
+ "Previously queued trigger skipped due to new request"
+ )
+ }
+ faceAuthLogger.queueingRequest(uiEvent, fallbackToDetection)
+ pendingAuthenticateRequest.value = AuthenticationRequest(uiEvent, fallbackToDetection)
+ }
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ private fun processPendingAuthRequests() {
+ combine(
+ pendingAuthenticateRequest,
+ canRunFaceAuth,
+ canRunDetection,
+ cancellationInProgress,
+ ) { pending, canRunAuth, canRunDetect, cancelInProgress ->
+ if (
+ pending != null &&
+ !(canRunAuth || (canRunDetect && pending.fallbackToDetection)) ||
+ cancelInProgress
+ ) {
+ faceAuthLogger.notProcessingRequestYet(
+ pending?.uiEvent,
+ canRunAuth,
+ canRunDetect,
+ cancelInProgress
+ )
+ return@combine null
+ } else {
+ return@combine pending
+ }
+ }
+ .onEach {
+ it?.let {
+ faceAuthLogger.processingRequest(it.uiEvent, it.fallbackToDetection)
+ clearPendingAuthRequest("Authenticate was invoked")
+ authenticate(it.uiEvent, it.fallbackToDetection)
+ }
+ }
+ .flowOn(mainDispatcher)
+ .launchIn(applicationScope)
+ }
+
+ private suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
if (_isAuthRunning.value) {
faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running")
return
}
- if (cancellationInProgress) {
- faceAuthLogger.queuingRequestWhileCancelling(
- faceAuthRequestedWhileCancellation,
- uiEvent
- )
- faceAuthRequestedWhileCancellation = uiEvent
+ if (cancellationInProgress.value) {
+ faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "cancellation in progress")
return
- } else {
- faceAuthRequestedWhileCancellation = null
}
if (canRunFaceAuth.value) {
@@ -550,15 +578,27 @@
authCancellationSignal,
faceAuthCallback,
null,
- FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+ SysUiFaceAuthenticateOptions(
+ currentUserId,
+ uiEvent,
+ wakeReason = uiEvent.extraInfo
+ )
+ .toFaceAuthenticateOptions()
)
}
- } else if (fallbackToDetection && canRunDetection.value) {
- faceAuthLogger.ignoredFaceAuthTrigger(
- uiEvent,
- "face auth gating check is false, falling back to detection."
- )
- detect()
+ } else if (canRunDetection.value) {
+ if (fallbackToDetection) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ "face auth gating check is false, falling back to detection."
+ )
+ detect(uiEvent)
+ } else {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent = uiEvent,
+ "face auth gating check is false and fallback to detection is not requested"
+ )
+ }
} else {
faceAuthLogger.ignoredFaceAuthTrigger(
uiEvent,
@@ -567,7 +607,7 @@
}
}
- suspend fun detect() {
+ suspend fun detect(uiEvent: FaceAuthUiEvent) {
if (!isDetectionSupported) {
faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
return
@@ -584,7 +624,8 @@
faceManager?.detectFace(
checkNotNull(detectCancellationSignal),
detectionCallback,
- FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+ SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo)
+ .toFaceAuthenticateOptions()
)
}
}
@@ -608,13 +649,13 @@
faceAuthLogger.cancelSignalNotReceived(
_isAuthRunning.value,
_isLockedOut.value,
- cancellationInProgress,
- faceAuthRequestedWhileCancellation
+ cancellationInProgress.value,
+ pendingAuthenticateRequest.value?.uiEvent
)
_authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
onFaceAuthRequestCompleted()
}
- cancellationInProgress = true
+ cancellationInProgress.value = true
_isAuthRunning.value = false
}
@@ -647,9 +688,7 @@
" supportsFaceDetection: " +
"${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
)
- pw.println(
- " faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
- )
+ pw.println(" _pendingAuthenticateRequest: ${pendingAuthenticateRequest.value}")
pw.println(" authCancellationSignal: $authCancellationSignal")
pw.println(" detectCancellationSignal: $detectCancellationSignal")
pw.println(" faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index 34f6b4d..698328e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -22,7 +22,7 @@
import android.os.UserHandle
import android.util.LayoutDirection
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 1c0b73f..af01626 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -22,7 +22,7 @@
import android.graphics.Point
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index 46135fa..c8cb9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -56,9 +56,6 @@
get() = emptyFlow()
override fun setLockedOut(isLockedOut: Boolean) = Unit
- override fun pauseFaceAuth() = Unit
-
- override fun resumeFaceAuth() = Unit
/**
* Trigger face authentication.
@@ -69,7 +66,7 @@
*
* Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
*/
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {}
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) = Unit
/** Stop currently running face authentication or detection. */
override fun cancel() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
index 00036ce..6522439 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -29,6 +29,7 @@
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -36,6 +37,8 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
@@ -44,19 +47,25 @@
/** Encapsulates any state relevant to trust agents and trust grants. */
interface TrustRepository {
+ /** Flow representing whether the current user has enabled any trust agents. */
+ val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
+
/** Flow representing whether the current user is trusted. */
val isCurrentUserTrusted: Flow<Boolean>
/** Flow representing whether active unlock is running for the current user. */
val isCurrentUserActiveUnlockRunning: Flow<Boolean>
- /** Reports that whether trust is managed has changed for the current user. */
+ /**
+ * Reports whether a trust agent is currently enabled and managing the trust of the current user
+ */
val isCurrentUserTrustManaged: StateFlow<Boolean>
/** A trust agent is requesting to dismiss the keyguard from a trust change. */
val trustAgentRequestingToDismissKeyguard: Flow<TrustModel>
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class TrustRepositoryImpl
@Inject
@@ -174,6 +183,11 @@
}
.map { it!! }
+ override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { flowOf(trustManager.isTrustUsuallyManaged(it.id)) }
+ .stateIn(applicationScope, started = SharingStarted.Eagerly, false)
+
private fun isUserTrustManaged(userId: Int) =
trustManagedForUser[userId]?.isTrustManaged ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index ac936b1..b2e436c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -19,7 +19,7 @@
import android.content.Context
import androidx.annotation.DimenRes
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 9c6a1b1..8f39431 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -265,7 +265,11 @@
!isKeyguardUnlocked &&
statusBarState == KEYGUARD
) {
- transitionId = startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ transitionId =
+ startTransitionTo(
+ toState = KeyguardState.PRIMARY_BOUNCER,
+ animator = null, // transition will be manually controlled
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 5727857..338f994 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -23,7 +23,7 @@
import android.graphics.Point
import android.util.MathUtils
import com.android.app.animation.Interpolators
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index ca430da..299c8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -24,7 +24,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -172,7 +172,6 @@
private fun isFeatureEnabled(): Boolean {
return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI) &&
appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9c796f8..de791aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -23,7 +23,7 @@
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
@@ -380,10 +380,6 @@
suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
return listOf(
KeyguardPickerFlag(
- name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
- value = featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI),
- ),
- KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value =
!isFeatureDisabledByDevicePolicy() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index ccc2080..20e55e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -19,9 +19,10 @@
import android.content.Context
import android.hardware.biometrics.BiometricFaceConstants
import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceWakeUpTriggersConfig
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -31,8 +32,10 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -40,6 +43,7 @@
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -52,8 +56,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
/**
@@ -77,6 +79,8 @@
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val userRepository: UserRepository,
private val facePropertyRepository: FacePropertyRepository,
+ private val keyguardRepository: KeyguardRepository,
+ private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
) : CoreStartable, KeyguardFaceAuthInteractor {
private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -119,8 +123,21 @@
keyguardTransitionInteractor.dozingToLockscreenTransition
)
.filter { it.transitionState == TransitionState.STARTED }
+ .sample(keyguardRepository.wakefulness)
+ .filter { wakefulnessModel ->
+ val validWakeupReason =
+ faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(
+ wakefulnessModel.lastWakeReason
+ )
+ if (!validWakeupReason) {
+ faceAuthenticationLogger.ignoredWakeupReason(wakefulnessModel.lastWakeReason)
+ }
+ validWakeupReason
+ }
.onEach {
faceAuthenticationLogger.lockscreenBecameVisible(it)
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED.extraInfo =
+ it.lastWakeReason.powerManagerWakeReason
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
fallbackToDetect = true
@@ -144,14 +161,11 @@
.onEach { (previous, curr) ->
val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
- if (!wasSwitching && isSwitching) {
- repository.pauseFaceAuth()
- } else if (wasSwitching && !isSwitching) {
+ if (wasSwitching && !isSwitching) {
val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
repository.setLockedOut(
lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
)
- repository.resumeFaceAuth()
yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
@@ -232,12 +246,8 @@
)
} else {
faceAuthenticationStatusOverride.value = null
- applicationScope.launch {
- withContext(mainDispatcher) {
- faceAuthenticationLogger.authRequested(uiEvent)
- repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
- }
- }
+ faceAuthenticationLogger.authRequested(uiEvent)
+ repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
}
} else {
faceAuthenticationLogger.ignoredFaceAuthTrigger(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
index c8a04fd..3602be8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -21,18 +21,37 @@
/** The reason we're waking up or going to sleep, such as pressing the power button. */
enum class WakeSleepReason(
val isTouch: Boolean,
+ @PowerManager.WakeReason val powerManagerWakeReason: Int,
) {
/** The physical power button was pressed to wake up or sleep the device. */
- POWER_BUTTON(isTouch = false),
+ POWER_BUTTON(isTouch = false, PowerManager.WAKE_REASON_POWER_BUTTON),
/** The user has tapped or double tapped to wake the screen. */
- TAP(isTouch = true),
+ TAP(isTouch = true, PowerManager.WAKE_REASON_TAP),
/** The user performed some sort of gesture to wake the screen. */
- GESTURE(isTouch = true),
+ GESTURE(isTouch = true, PowerManager.WAKE_REASON_GESTURE),
+
+ /** Waking up because a wake key other than power was pressed. */
+ KEY(isTouch = false, PowerManager.WAKE_REASON_WAKE_KEY),
+
+ /** Waking up because a wake motion was performed */
+ MOTION(isTouch = false, PowerManager.WAKE_REASON_WAKE_MOTION),
+
+ /** Waking due to the lid being opened. */
+ LID(isTouch = false, PowerManager.WAKE_REASON_LID),
+
+ /** Waking the device due to unfolding of a foldable device. */
+ UNFOLD(isTouch = false, PowerManager.WAKE_REASON_UNFOLD_DEVICE),
+
+ /** Waking up due to a user performed lift gesture. */
+ LIFT(isTouch = false, PowerManager.WAKE_REASON_LIFT),
+
+ /** Waking up due to a user interacting with a biometric. */
+ BIOMETRIC(isTouch = false, PowerManager.WAKE_REASON_BIOMETRIC),
/** Something else happened to wake up or sleep the device. */
- OTHER(isTouch = false);
+ OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN);
companion object {
fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
@@ -40,6 +59,12 @@
PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON
PowerManager.WAKE_REASON_TAP -> TAP
PowerManager.WAKE_REASON_GESTURE -> GESTURE
+ PowerManager.WAKE_REASON_WAKE_KEY -> KEY
+ PowerManager.WAKE_REASON_WAKE_MOTION -> MOTION
+ PowerManager.WAKE_REASON_LID -> LID
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE -> UNFOLD
+ PowerManager.WAKE_REASON_LIFT -> LIFT
+ PowerManager.WAKE_REASON_BIOMETRIC -> BIOMETRIC
else -> OTHER
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
index 5c072fb..5900a24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardAmbientIndicationAreaViewBinder.kt
@@ -22,7 +22,7 @@
import android.view.ViewPropertyAnimator
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index a9c71ad..dd7eee9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -32,7 +32,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableLinearLayout
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index dc51944..f20a666 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -22,7 +22,7 @@
import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 7685345..125e2da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -24,7 +24,7 @@
import android.view.ViewPropertyAnimator
import androidx.core.animation.CycleInterpolator
import androidx.core.animation.ObjectAnimator
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 83b5463..eeb4ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 4b76821..f564d00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 82610e6..6beef8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -22,7 +22,7 @@
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.binder.IconViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
index 3bb01f2..aabb3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
@@ -18,7 +18,7 @@
package com.android.systemui.biometrics.ui.binder
import android.view.View
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.ui.binder.UdfpsAodFingerprintViewBinder
import com.android.systemui.keyguard.ui.binder.UdfpsBackgroundViewBinder
import com.android.systemui.keyguard.ui.binder.UdfpsFingerprintViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
index 667abae..a0e0da4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
@@ -23,7 +23,7 @@
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.UdfpsKeyguardView
import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 85a2fd5..2ad74fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -38,7 +38,7 @@
import androidx.core.view.isInvisible
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
index 890d565..78099d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -25,7 +25,7 @@
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView
class KeyguardIndicationArea(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a948741..f2b28d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,123 +19,14 @@
import android.content.Context
import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.content.res.ResourcesCompat
-import com.android.keyguard.KeyguardStatusView
-import com.android.keyguard.LockIconView
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableImageView
/** Provides a container for all keyguard ui content. */
class KeyguardRootView(
context: Context,
- private val attrs: AttributeSet?,
+ attrs: AttributeSet?,
) :
ConstraintLayout(
context,
attrs,
- ) {
-
- private var statusView: KeyguardStatusView? = null
-
- init {
- addIndicationTextArea()
- addLockIconView()
- addAmbientIndicationArea()
- addLeftShortcut()
- addRightShortcut()
- addSettingsPopupMenu()
- addStatusView()
- }
-
- private fun addIndicationTextArea() {
- val view = KeyguardIndicationArea(context, attrs)
- addView(view)
- }
-
- private fun addLockIconView() {
- val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
- addView(view)
- }
-
- private fun addAmbientIndicationArea() {
- LayoutInflater.from(context).inflate(R.layout.ambient_indication, this)
- }
-
- private fun addLeftShortcut() {
- val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
- val view =
- LaunchableImageView(context, attrs).apply {
- id = R.id.start_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- addView(view)
- }
-
- private fun addRightShortcut() {
- val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
- val view =
- LaunchableImageView(context, attrs).apply {
- id = R.id.end_button
- scaleType = ImageView.ScaleType.FIT_CENTER
- background =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_bg,
- context.theme
- )
- foreground =
- ResourcesCompat.getDrawable(
- context.resources,
- R.drawable.keyguard_bottom_affordance_selected_border,
- context.theme
- )
- visibility = View.INVISIBLE
- setPadding(padding, padding, padding, padding)
- }
- addView(view)
- }
-
- private fun addSettingsPopupMenu() {
- val view =
- LayoutInflater.from(context)
- .inflate(R.layout.keyguard_settings_popup_menu, this, false)
- .apply {
- id = R.id.keyguard_settings_button
- visibility = GONE
- }
- addView(view)
- }
-
- fun addStatusView(): KeyguardStatusView {
- // StatusView may need to be rebuilt on config changes. Remove and reinflate
- statusView?.let { removeView(it) }
- val view =
- (LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, this, false)
- as KeyguardStatusView)
- .apply {
- setClipChildren(false)
- statusView = this
- }
-
- addView(view)
- return view
- }
-}
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 15bb909..1eeb017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,10 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
@@ -29,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -49,10 +47,10 @@
defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
- private val featureFlags: FeatureFlags,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -64,21 +62,12 @@
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
)
- override fun replaceViews(
- previousBlueprint: KeyguardBlueprint?,
- constraintLayout: ConstraintLayout,
- bindData: Boolean
- ) {
- if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
- super.replaceViews(previousBlueprint, constraintLayout, bindData)
- }
- }
-
companion object {
const val DEFAULT = "default"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 6534dcf..a9885fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -41,6 +42,7 @@
defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
splitShadeGuidelines: SplitShadeGuidelines,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
aodNotificationIconsSection: AodNotificationIconsSection,
@@ -55,6 +57,7 @@
defaultSettingsPopupMenuSection,
alignShortcutsToUdfpsSection,
defaultStatusViewSection,
+ defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 5aba229..28e6a95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -25,7 +25,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index ac11ba5..669db34 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -26,7 +26,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
index d046a19..a4137ac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
@@ -4,7 +4,7 @@
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index ce86e97..9371d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -29,7 +29,7 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index a45223c..623eac0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -21,7 +21,7 @@
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 100099d..9409036 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -29,7 +29,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 7fff43b..afa49bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index b25f9af..6fd13e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -28,7 +28,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import androidx.core.view.isVisible
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 13ef985..a679120 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -24,7 +24,7 @@
import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
new file mode 100644
index 0000000..f713d5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
+import com.android.systemui.res.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeViewStateProvider
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+/** A section for the status bar displayed at the top of the lockscreen. */
+class DefaultStatusBarSection
+@Inject
+constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val keyguardStatusBarViewComponentFactory: KeyguardStatusBarViewComponent.Factory,
+) : KeyguardSection() {
+
+ private val statusBarViewId = R.id.keyguard_header
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ return
+ }
+
+ notificationPanelView.findViewById<View>(statusBarViewId)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ }
+
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.keyguard_status_bar, constraintLayout, false)
+ as KeyguardStatusBarView
+
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ return
+ }
+
+ val statusBarView =
+ constraintLayout.findViewById<KeyguardStatusBarView>(statusBarViewId) ?: return
+
+ val provider =
+ object : ShadeViewStateProvider {
+ override val lockscreenShadeDragProgress: Float = 0f
+ override val panelViewExpandedHeight: Float = 0f
+ override fun shouldHeadsUpBeVisible(): Boolean {
+ return false
+ }
+ }
+ val statusBarViewComponent =
+ keyguardStatusBarViewComponentFactory.build(statusBarView, provider)
+ val controller = statusBarViewComponent.keyguardStatusBarViewController
+ controller.init()
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ constrainHeight(statusBarViewId, Utils.getStatusBarHeaderHeightKeyguard(context))
+ connect(statusBarViewId, TOP, PARENT_ID, TOP)
+ connect(statusBarViewId, START, PARENT_ID, START)
+ connect(statusBarViewId, END, PARENT_ID, END)
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(statusBarViewId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index c7b0cb9..1a9f021 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -31,7 +31,7 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardViewConfigurator
@@ -91,7 +91,7 @@
it.requireViewById<ViewGroup>(R.id.status_view_media_container)
)
keyguardViewConfigurator.get().keyguardStatusViewController = controller
- notificationPanelViewController.get().updateStatusBarViewController()
+ notificationPanelViewController.get().updateStatusViewController()
}
}
}
@@ -100,6 +100,8 @@
constraintSet.apply {
constrainWidth(statusViewId, MATCH_CONSTRAINT)
constrainHeight(statusViewId, WRAP_CONTENT)
+ // TODO(b/296122465): Constrain to the top of [DefaultStatusBarSection] and remove the
+ // extra margin below.
connect(statusViewId, TOP, PARENT_ID, TOP)
connect(statusViewId, START, PARENT_ID, START)
connect(statusViewId, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
index 5e3ea05..c869771 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
@@ -20,7 +20,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.shared.model.KeyguardSection
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index 63d165d..33718c41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.content.res.Resources
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.SettingsClockSize
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
index c36da9d..66ceded 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 05c9323..91b3357 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -17,27 +17,50 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
authenticationInteractor: AuthenticationInteractor,
+ communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
- val upDestinationSceneKey: Flow<SceneKey> =
- authenticationInteractor.isUnlocked.map { isUnlocked ->
- if (isUnlocked) {
- SceneKey.Gone
- } else {
- SceneKey.Bouncer
- }
+ val upDestinationSceneKey: StateFlow<SceneKey> =
+ authenticationInteractor.isUnlocked
+ .map { isUnlocked -> upDestinationSceneKey(isUnlocked) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = upDestinationSceneKey(authenticationInteractor.isUnlocked.value),
+ )
+
+ private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey {
+ return if (isUnlocked) {
+ SceneKey.Gone
+ } else {
+ SceneKey.Bouncer
+ }
+ }
+
+ /** The key of the scene we should switch to when swiping left. */
+ val leftDestinationSceneKey: SceneKey? =
+ if (communalInteractor.isCommunalEnabled) {
+ SceneKey.Communal
+ } else {
+ null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
index 667c2f1..c10a463 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index dd58607..54abc5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,7 +19,7 @@
import android.content.Context
import androidx.annotation.ColorInt
import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 66067b1..66af36a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -5,7 +5,8 @@
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
import com.google.errorprone.annotations.CompileTimeConstant
@@ -29,36 +30,30 @@
constructor(
@FaceAuthLog private val logBuffer: LogBuffer,
) {
- fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) {
+
+ fun ignoredWakeupReason(lastWakeReason: WakeSleepReason) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = "$lastWakeReason" },
+ {
+ "Ignoring off/aod/dozing -> Lockscreen transition " +
+ "because the last wake up reason is not allow-listed: $str1"
+ }
+ )
+ }
+ fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent?, ignoredReason: String) {
logBuffer.log(
TAG,
DEBUG,
{
- str1 = uiEvent.reason
+ str1 = "${uiEvent?.reason}"
str2 = ignoredReason
},
{ "Ignoring trigger because $str2, Trigger reason: $str1" }
)
}
- fun queuingRequestWhileCancelling(
- alreadyQueuedRequest: FaceAuthUiEvent?,
- newRequest: FaceAuthUiEvent
- ) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- str1 = alreadyQueuedRequest?.reason
- str2 = newRequest.reason
- },
- {
- "Face auth requested while previous request is being cancelled, " +
- "already queued request: $str1 queueing the new request: $str2"
- }
- )
- }
-
fun authenticating(uiEvent: FaceAuthUiEvent) {
logBuffer.log(TAG, DEBUG, { str1 = uiEvent.reason }, { "Running authenticate for $str1" })
}
@@ -161,15 +156,6 @@
)
}
- fun launchingQueuedFaceAuthRequest(faceAuthRequestedWhileCancellation: FaceAuthUiEvent?) {
- logBuffer.log(
- TAG,
- DEBUG,
- { str1 = "${faceAuthRequestedWhileCancellation?.reason}" },
- { "Received cancellation error and starting queued face auth request: $str1" }
- )
- }
-
fun faceAuthSuccess(result: FaceManager.AuthenticationResult) {
logBuffer.log(
TAG,
@@ -182,31 +168,10 @@
)
}
- fun observedConditionChanged(newValue: Boolean, context: String) {
- logBuffer.log(
- TAG,
- DEBUG,
- {
- bool1 = newValue
- str1 = context
- },
- { "Observed condition changed: $str1, new value: $bool1" }
- )
- }
-
fun canFaceAuthRunChanged(canRun: Boolean) {
logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
}
- fun canRunDetectionChanged(canRunDetection: Boolean) {
- logBuffer.log(
- TAG,
- DEBUG,
- { bool1 = canRunDetection },
- { "canRunDetection value changed to $bool1" }
- )
- }
-
fun cancellingFaceAuth() {
logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
}
@@ -223,12 +188,12 @@
logBuffer.log(TAG, DEBUG, "Triggering face auth because alternate bouncer is visible")
}
- fun lockscreenBecameVisible(transitionStep: TransitionStep?) {
+ fun lockscreenBecameVisible(wake: WakefulnessModel?) {
logBuffer.log(
TAG,
DEBUG,
- { str1 = "$transitionStep" },
- { "Triggering face auth because lockscreen became visible due to transition: $str1" }
+ { str1 = "${wake?.lastWakeReason}" },
+ { "Triggering face auth because lockscreen became visible due to wake reason: $str1" }
)
}
@@ -236,7 +201,7 @@
logBuffer.log(
TAG,
DEBUG,
- { str1 = "$uiEvent" },
+ { str1 = uiEvent.reason },
{ "Requesting face auth for trigger: $str1" }
)
}
@@ -269,4 +234,77 @@
fun faceLockedOut(@CompileTimeConstant reason: String) {
logBuffer.log(TAG, DEBUG, "Face auth has been locked out: $reason")
}
+
+ fun queueingRequest(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "$uiEvent"
+ bool1 = fallbackToDetection
+ },
+ { "Queueing $str1 request for face auth, fallbackToDetection: $bool1" }
+ )
+ }
+
+ fun notProcessingRequestYet(
+ uiEvent: FaceAuthUiEvent?,
+ canRunAuth: Boolean,
+ canRunDetect: Boolean,
+ cancelInProgress: Boolean
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ bool1 = canRunAuth
+ bool2 = canRunDetect
+ bool3 = cancelInProgress
+ },
+ {
+ "Waiting to process request: reason: $str1, " +
+ "canRunAuth: $bool1, " +
+ "canRunDetect: $bool2, " +
+ "cancelInProgress: $bool3"
+ }
+ )
+ }
+ }
+
+ fun processingRequest(uiEvent: FaceAuthUiEvent?, fallbackToDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "${uiEvent?.reason}"
+ bool1 = fallbackToDetection
+ },
+ { "Processing face auth request: $str1, fallbackToDetect: $bool1" }
+ )
+ }
+
+ fun clearingPendingAuthRequest(
+ @CompileTimeConstant loggingContext: String,
+ uiEvent: FaceAuthUiEvent?,
+ fallbackToDetection: Boolean?
+ ) {
+ uiEvent?.let {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = uiEvent.reason
+ str2 = "$fallbackToDetection"
+ str3 = loggingContext
+ },
+ {
+ "Clearing pending auth: $str1, " +
+ "fallbackToDetection: $str2, " +
+ "reason: $str3"
+ }
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java b/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
index 2cfe5a7..d01917a 100644
--- a/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/logcat/LogAccessDialogActivity.java
@@ -43,7 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ILogAccessDialogCallback;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 60fd104..88bc064 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -46,7 +46,7 @@
import com.android.internal.widget.RecyclerView
import com.android.internal.widget.RecyclerViewAccessibilityDelegate
import com.android.internal.widget.ResolverDrawerLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -91,7 +91,13 @@
public override fun onCreate(bundle: Bundle?) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- component = componentFactory.create(activity = this, view = this, resultHandler = this)
+ component =
+ componentFactory.create(
+ hostUserHandle = hostUserHandle,
+ callingPackage = callingPackage,
+ view = this,
+ resultHandler = this
+ )
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
// Create a separate configuration controller for this activity as the configuration
@@ -274,7 +280,9 @@
recentsViewController.hasRecentTasks
}
- override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
+ override fun shouldShowStickyContentPreviewWhenEmpty() = shouldShowContentPreview()
+
+ override fun shouldShowServiceTargets() = false
private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
@@ -286,6 +294,18 @@
override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
recentsViewController.createView(parent)
+ private val hostUserHandle: UserHandle
+ get() {
+ val extras =
+ intent.extras
+ ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
+ return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
+ ?: error(
+ "MediaProjectionAppSelectorActivity should be provided with " +
+ "$EXTRA_HOST_APP_USER_HANDLE extra"
+ )
+ }
+
companion object {
const val TAG = "MediaProjectionAppSelectorActivity"
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index d403788..4de6278 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -51,7 +51,7 @@
import android.util.Log;
import android.view.Window;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
index 5315067..f5f5d38 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
@@ -22,7 +22,7 @@
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.media.controls.ui.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.surfaceFromScheme
import com.android.systemui.media.controls.ui.textPrimaryFromScheme
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
index b7a2522..b98e9c2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
@@ -21,7 +21,7 @@
import android.graphics.drawable.Icon
import android.media.session.MediaSession
import com.android.internal.logging.InstanceId
-import com.android.systemui.R
+import com.android.systemui.res.R
/** State of a media view. */
data class MediaData(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index 8b74263..1b14f75 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -25,7 +25,7 @@
import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.internal.widget.CachingIconView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
index 6eaff3f..3f4f347 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.Observer
import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.media.controls.ui.SquigglyProgress
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
index 258284e..8ac8a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
@@ -23,7 +23,7 @@
import android.widget.SeekBar
import android.widget.TextView
import com.android.internal.widget.CachingIconView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.ui.IlluminationDrawable
import com.android.systemui.util.animation.TransitionLayout
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index e79fc74..0f3e0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -60,7 +60,7 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index c90a3c1..1fe93ed 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -32,7 +32,7 @@
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
index b9cc772..5aa6824 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
@@ -37,7 +37,7 @@
import com.android.app.animation.Interpolators
import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.ColorUtils.blendARGB
-import com.android.systemui.R
+import com.android.systemui.res.R
import org.xmlpull.v1.XmlPullParser
private const val BACKGROUND_ANIM_DURATION = 370L
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
index 646d1d0..6ee072d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
@@ -37,7 +37,7 @@
import androidx.annotation.Keep
import com.android.app.animation.Interpolators
import com.android.internal.graphics.ColorUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import org.xmlpull.v1.XmlPullParser
private const val RIPPLE_ANIM_DURATION = 800L
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 38a6a8f..e59fc15 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -38,7 +38,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index ec0c40e..1261152 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -29,7 +29,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index a12bc2c..cce4cda 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -81,7 +81,7 @@
import com.android.internal.widget.CachingIconView;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
import com.android.systemui.bluetooth.BroadcastDialogController;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 0b30e59..ae3c912 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -35,7 +35,7 @@
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 1dd969f..b436f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -20,7 +20,7 @@
import android.content.res.Configuration
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index ea943be..20ea60f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -20,7 +20,7 @@
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.ui.MediaHierarchyManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
index 875a010..fbb84de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
@@ -19,7 +19,7 @@
import androidx.annotation.IntDef;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 26a7d04..ff9495d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -41,7 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index d8824983..5958b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -49,7 +49,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 3a1d8b0..0c5a14f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -58,7 +58,7 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 11a16a4..ac64300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -45,7 +45,7 @@
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.qrcode.QrCodeGenerator;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 7011ad9..426a497 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index e5a6bb5..4640a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,7 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSessionReleaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSessionReleaseDialog.java
index 2680a2f..87c9cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSessionReleaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSessionReleaseDialog.java
@@ -30,7 +30,7 @@
import android.widget.Button;
import android.widget.ImageView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
index 0ba5f28..9de2b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -26,7 +26,7 @@
import android.util.Slog;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index b29b588..2f0e129 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -21,7 +21,7 @@
import android.graphics.drawable.Drawable
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.TintedIcon
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index da8e106..6e9485e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -35,7 +35,7 @@
import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import com.android.internal.widget.CachingIconView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.internal.logging.InstanceId
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
index fbf7e25..fbd7fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
@@ -20,7 +20,7 @@
import android.view.View
import android.view.WindowManager
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index d3efae4..339145c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -21,7 +21,7 @@
import android.util.Log
import androidx.annotation.StringRes
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 87d0098..e827a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -25,7 +25,7 @@
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 11538fa..33d9cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,7 +24,6 @@
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -118,29 +117,8 @@
@Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
- fun provideCallerPackageName(activity: MediaProjectionAppSelectorActivity): String? =
- activity.callingPackage
-
- @Provides
- @MediaProjectionAppSelector
- @MediaProjectionAppSelectorScope
- fun bindConfigurationController(
- activity: MediaProjectionAppSelectorActivity
- ): ConfigurationController = ConfigurationControllerImpl(activity)
-
- @Provides
- @HostUserHandle
- @MediaProjectionAppSelectorScope
- fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
- val extras =
- activity.intent.extras
- ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
- return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
- ?: error(
- "MediaProjectionAppSelectorActivity should be provided with " +
- "$EXTRA_HOST_APP_USER_HANDLE extra"
- )
- }
+ fun bindConfigurationController(context: Context): ConfigurationController =
+ ConfigurationControllerImpl(context)
@Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
@@ -161,7 +139,8 @@
interface Factory {
/** Create a factory to inject the activity into the graph */
fun create(
- @BindsInstance activity: MediaProjectionAppSelectorActivity,
+ @BindsInstance @HostUserHandle hostUserHandle: UserHandle,
+ @BindsInstance @MediaProjectionAppSelector callingPackage: String?,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
): MediaProjectionAppSelectorComponent
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt
index 829b0dd..950b695 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt
@@ -21,7 +21,7 @@
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
import com.android.internal.app.ResolverListAdapter
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 5d732fb..fd1a683 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.mediaprojection.appselector.view
import android.app.ActivityOptions
+import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.IActivityTaskManager
import android.graphics.Rect
import android.os.Binder
@@ -25,7 +26,7 @@
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.data.RecentTask
@@ -129,6 +130,9 @@
view.width,
view.height
)
+ activityOptions.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
activityOptions.launchCookie = launchCookie
activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9b9d561..412c006 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -28,8 +28,7 @@
import android.view.WindowManager
import androidx.core.content.getSystemService
import androidx.core.content.res.use
-import com.android.internal.R as AndroidR
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
@@ -147,25 +146,14 @@
previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height)
val currentRotation: Int = display.rotation
- val displayWidthPx = windowMetrics.bounds.width()
- val displayHeightPx = windowMetrics.bounds.height()
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
val isLargeScreen = isLargeScreen(context)
- val taskbarSize =
- if (isLargeScreen) {
- resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
- } else {
- 0
- }
previewPositionHelper.updateThumbnailMatrix(
previewRect,
thumbnailData,
measuredWidth,
measuredHeight,
- displayWidthPx,
- displayHeightPx,
- taskbarSize,
isLargeScreen,
currentRotation,
isRtl
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
index a09935b..3fe040a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -21,7 +21,7 @@
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelector
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTask
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
index 6af50a0..78dbd92 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
@@ -20,7 +20,7 @@
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
index a6b3da0..fc45228 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.devicepolicy
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
/** Dialog that shows that screen capture is disabled on this device. */
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
index a437139..7840da9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
@@ -21,7 +21,7 @@
import android.app.NotificationManager
import android.content.Context
import android.util.Log
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index c6f73ef..d7e062f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -233,6 +233,9 @@
Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Secure.SEARCH_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
@@ -422,11 +425,17 @@
private void updateAssistantAvailability() {
boolean assistantAvailableForUser = mAssistManagerLazy.get()
.getAssistInfoForUser(mUserTracker.getUserId()) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+
+ boolean overrideLongPressHome = mAssistManagerLazy.get()
+ .shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+ boolean longPressDefault = mContext.getResources().getBoolean(overrideLongPressHome
+ ? com.android.internal.R.bool.config_searchLongPressHomeEnabledDefault
+ : com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ overrideLongPressHome ? Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+ : Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
mUserTracker.getUserId()) != 0;
+
boolean gestureDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
@@ -455,6 +464,7 @@
@Override
public void setAssistantOverridesRequested(int[] invocationTypes) {
mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes);
+ updateAssistantAvailability();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 62b22c5..cb52a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -112,7 +112,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 5a42028..a601d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -16,483 +16,58 @@
package com.android.systemui.navigationbar;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerGlobal;
-
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.back.BackAnimation;
-import com.android.wm.shell.pip.Pip;
-
-import java.io.PrintWriter;
-import java.util.Optional;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.phone.BarTransitions;
/** A controller to handle navigation bars. */
-@SysUISingleton
-public class NavigationBarController implements
- ConfigurationController.ConfigurationListener,
- NavigationModeController.ModeChangedListener,
- Dumpable {
-
- private static final String TAG = NavigationBarController.class.getSimpleName();
-
- private final Context mContext;
- private final Handler mHandler;
- private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
- private FeatureFlags mFeatureFlags;
- private final SecureSettings mSecureSettings;
- private final DisplayTracker mDisplayTracker;
- private final DisplayManager mDisplayManager;
- private final TaskbarDelegate mTaskbarDelegate;
- private final NavBarHelper mNavBarHelper;
- private int mNavMode;
- @VisibleForTesting boolean mIsLargeScreen;
-
- /** A displayId - nav bar maps. */
- @VisibleForTesting
- SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
-
- // Tracks config changes that will actually recreate the nav bar
- private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
- ActivityInfo.CONFIG_FONT_SCALE
- | ActivityInfo.CONFIG_UI_MODE);
-
- @Inject
- public NavigationBarController(Context context,
- OverviewProxyService overviewProxyService,
- NavigationModeController navigationModeController,
- SysUiState sysUiFlagsContainer,
- CommandQueue commandQueue,
- @Main Handler mainHandler,
- ConfigurationController configurationController,
- NavBarHelper navBarHelper,
- TaskbarDelegate taskbarDelegate,
- NavigationBarComponent.Factory navigationBarComponentFactory,
- DumpManager dumpManager,
- AutoHideController autoHideController,
- LightBarController lightBarController,
- TaskStackChangeListeners taskStackChangeListeners,
- Optional<Pip> pipOptional,
- Optional<BackAnimation> backAnimation,
- FeatureFlags featureFlags,
- SecureSettings secureSettings,
- DisplayTracker displayTracker) {
- mContext = context;
- mHandler = mainHandler;
- mNavigationBarComponentFactory = navigationBarComponentFactory;
- mFeatureFlags = featureFlags;
- mSecureSettings = secureSettings;
- mDisplayTracker = displayTracker;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
- commandQueue.addCallback(mCommandQueueCallbacks);
- configurationController.addCallback(this);
- mConfigChanges.applyNewConfig(mContext.getResources());
- mNavMode = navigationModeController.addListener(this);
- mNavBarHelper = navBarHelper;
- mTaskbarDelegate = taskbarDelegate;
- mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navBarHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController, lightBarController, pipOptional,
- backAnimation.orElse(null), taskStackChangeListeners);
- mIsLargeScreen = isLargeScreen(mContext);
- dumpManager.registerDumpable(this);
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- boolean isOldConfigLargeScreen = mIsLargeScreen;
- mIsLargeScreen = isLargeScreen(mContext);
- boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
- boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
- // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
- Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
- + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
- + " willApplyConfigToNavbars=" + willApplyConfig
- + " navBarCount=" + mNavigationBars.size());
- if (mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.onConfigurationChanged(newConfig);
- }
- // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
- if (largeScreenChanged && updateNavbarForTaskbar()) {
- return;
- }
-
- if (willApplyConfig) {
- for (int i = 0; i < mNavigationBars.size(); i++) {
- recreateNavigationBar(mNavigationBars.keyAt(i));
- }
- } else {
- for (int i = 0; i < mNavigationBars.size(); i++) {
- mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
- }
- }
- }
-
- @Override
- public void onNavigationModeChanged(int mode) {
- if (mNavMode == mode) {
- return;
- }
- final int oldMode = mNavMode;
- mNavMode = mode;
- updateAccessibilityButtonModeIfNeeded();
-
- mHandler.post(() -> {
- // create/destroy nav bar based on nav mode only in unfolded state
- if (oldMode != mNavMode) {
- updateNavbarForTaskbar();
- }
- for (int i = 0; i < mNavigationBars.size(); i++) {
- NavigationBar navBar = mNavigationBars.valueAt(i);
- if (navBar == null) {
- continue;
- }
- navBar.getView().updateStates();
- }
- });
- }
-
- private void updateAccessibilityButtonModeIfNeeded() {
- final int mode = mSecureSettings.getIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
- // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
- // mode, so we don't need to update it.
- if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return;
- }
-
- // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
- if (QuickStepContract.isGesturalMode(mNavMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- mSecureSettings.putIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
- UserHandle.USER_CURRENT);
- // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
- } else if (!QuickStepContract.isGesturalMode(mNavMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- mSecureSettings.putIntForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- }
- }
-
- private boolean shouldCreateNavBarAndTaskBar(int displayId) {
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
- try {
- return wms.hasNavigationBar(displayId);
- } catch (RemoteException e) {
- // Cannot get wms, just return false with warning message.
- Log.w(TAG, "Cannot get WindowManager.");
- return false;
- }
- }
-
- /** @see #initializeTaskbarIfNecessary() */
- private boolean updateNavbarForTaskbar() {
- boolean taskbarShown = initializeTaskbarIfNecessary();
- if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
- createNavigationBar(mContext.getDisplay(), null, null);
- }
- return taskbarShown;
- }
-
- /** @return {@code true} if taskbar is enabled, false otherwise */
- private boolean initializeTaskbarIfNecessary() {
- // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
- boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
- Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
-
- if (taskbarEnabled) {
- Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
- final int displayId = mContext.getDisplayId();
- // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
- mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
- // Remove navigation bar when taskbar is showing
- removeNavigationBar(displayId);
- mTaskbarDelegate.init(displayId);
- mNavBarHelper.setTogglingNavbarTaskbar(false);
- Trace.endSection();
-
- } else {
- mTaskbarDelegate.destroy();
- }
- return taskbarEnabled;
- }
-
- private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
- @Override
- public void onDisplayRemoved(int displayId) {
- removeNavigationBar(displayId);
- }
-
- @Override
- public void onDisplayReady(int displayId) {
- Display display = mDisplayManager.getDisplay(displayId);
- mIsLargeScreen = isLargeScreen(mContext);
- createNavigationBar(display, null /* savedState */, null /* result */);
- }
-
- @Override
- public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
- final NavigationBar navigationBar = getNavigationBar(displayId);
- if (navigationBar != null) {
- navigationBar.setNavigationBarLumaSamplingEnabled(enable);
- }
- }
-
- @Override
- public void showPinningEnterExitToast(boolean entering) {
- int displayId = mContext.getDisplayId();
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- navBarView.showPinningEnterExitToast(entering);
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEnterExitToast(entering);
- }
- }
-
- @Override
- public void showPinningEscapeToast() {
- int displayId = mContext.getDisplayId();
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- navBarView.showPinningEscapeToast();
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEscapeToast();
- }
- }
- };
-
- /**
- * Recreates the navigation bar for the given display.
- */
- private void recreateNavigationBar(int displayId) {
- // TODO: Improve this flow so that we don't need to create a new nav bar but just
- // the view
- Bundle savedState = new Bundle();
- NavigationBar bar = mNavigationBars.get(displayId);
- if (bar != null) {
- bar.onSaveInstanceState(savedState);
- }
- removeNavigationBar(displayId);
- createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
- }
-
- // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
- // CarStatusBar because they have their own nav bar. Think about a better way for it.
+public interface NavigationBarController {
/**
* Creates navigation bars when car/status bar initializes.
+ * <p>
+ * TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
+ * CarStatusBar because they have their own nav bar. Think about a better way for it.
*
* @param includeDefaultDisplay {@code true} to create navigation bar on default display.
*/
- public void createNavigationBars(final boolean includeDefaultDisplay,
- RegisterStatusBarResult result) {
- updateAccessibilityButtonModeIfNeeded();
+ void createNavigationBars(
+ boolean includeDefaultDisplay,
+ RegisterStatusBarResult result);
- // Don't need to create nav bar on the default display if we initialize TaskBar.
- final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
- && !initializeTaskbarIfNecessary();
- Display[] displays = mDisplayTracker.getAllDisplays();
- for (Display display : displays) {
- if (shouldCreateDefaultNavbar
- || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
- createNavigationBar(display, null /* savedState */, result);
- }
- }
- }
-
- /**
- * Adds a navigation bar on default display or an external display if the display supports
- * system decorations.
- *
- * @param display the display to add navigation bar on.
- */
- @VisibleForTesting
- void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
- if (display == null) {
- return;
- }
-
- final int displayId = display.getDisplayId();
- final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
-
- if (!shouldCreateNavBarAndTaskBar(displayId)) {
- return;
- }
-
- // We may show TaskBar on the default display for large screen device. Don't need to create
- // navigation bar for this case.
- if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
- return;
- }
-
- final Context context = isOnDefaultDisplay
- ? mContext
- : mContext.createDisplayContext(display);
- NavigationBarComponent component = mNavigationBarComponentFactory.create(
- context, savedState);
- NavigationBar navBar = component.getNavigationBar();
- navBar.init();
- mNavigationBars.put(displayId, navBar);
-
- navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (result != null) {
- navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
- result.mImeWindowVis, result.mImeBackDisposition,
- result.mShowImeSwitcher);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- v.removeOnAttachStateChangeListener(this);
- }
- });
- }
-
- void removeNavigationBar(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.destroyView();
- mNavigationBars.remove(displayId);
- }
- }
+ /** Removes the navigation bar for the given display ID. */
+ void removeNavigationBar(int displayId);
/** @see NavigationBar#checkNavBarModes() */
- public void checkNavBarModes(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.checkNavBarModes();
- }
- }
+ void checkNavBarModes(int displayId);
/** @see NavigationBar#finishBarAnimations() */
- public void finishBarAnimations(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.finishBarAnimations();
- }
- }
+ void finishBarAnimations(int displayId);
/** @see NavigationBar#touchAutoDim() */
- public void touchAutoDim(int displayId) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.touchAutoDim();
- }
- }
+ void touchAutoDim(int displayId);
/** @see NavigationBar#transitionTo(int, boolean) */
- public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.transitionTo(barMode, animate);
- }
- }
+ void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate);
/** @see NavigationBar#disableAnimationsDuringHide(long) */
- public void disableAnimationsDuringHide(int displayId, long delay) {
- NavigationBar navBar = mNavigationBars.get(displayId);
- if (navBar != null) {
- navBar.disableAnimationsDuringHide(delay);
- }
- }
+ void disableAnimationsDuringHide(int displayId, long delay);
/** @return {@link NavigationBarView} on the default display. */
- public @Nullable NavigationBarView getDefaultNavigationBarView() {
- return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
- }
+ @Nullable
+ NavigationBarView getDefaultNavigationBarView();
/**
* @param displayId the ID of display which Navigation bar is on
* @return {@link NavigationBarView} on the display with {@code displayId}.
* {@code null} if no navigation bar on that display.
*/
- public @Nullable NavigationBarView getNavigationBarView(int displayId) {
- NavigationBar navBar = getNavigationBar(displayId);
- return (navBar == null) ? null : navBar.getView();
- }
+ @Nullable
+ NavigationBarView getNavigationBarView(int displayId);
- private @Nullable NavigationBar getNavigationBar(int displayId) {
- return mNavigationBars.get(displayId);
- }
-
- public boolean isOverviewEnabled(int displayId) {
- final NavigationBarView navBarView = getNavigationBarView(displayId);
- if (navBarView != null) {
- return navBarView.isOverviewEnabled();
- } else {
- return mTaskbarDelegate.isOverviewEnabled();
- }
- }
+ boolean isOverviewEnabled(int displayId);
/** @return {@link NavigationBar} on the default display. */
@Nullable
- public NavigationBar getDefaultNavigationBar() {
- return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mIsLargeScreen=" + mIsLargeScreen);
- pw.println("mNavMode=" + mNavMode);
- for (int i = 0; i < mNavigationBars.size(); i++) {
- if (i > 0) {
- pw.println();
- }
- mNavigationBars.valueAt(i).dump(pw);
- }
- }
+ NavigationBar getDefaultNavigationBar();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
new file mode 100644
index 0000000..e73b078
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.navigationbar
+
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.BarTransitions
+import javax.inject.Inject
+
+/** A no-op version of [NavigationBarController] for variants like Arc and TV. */
+@SysUISingleton
+class NavigationBarControllerEmptyImpl @Inject constructor() : NavigationBarController {
+ override fun createNavigationBars(
+ includeDefaultDisplay: Boolean,
+ result: RegisterStatusBarResult?,
+ ) {}
+ override fun removeNavigationBar(displayId: Int) {}
+ override fun checkNavBarModes(displayId: Int) {}
+ override fun finishBarAnimations(displayId: Int) {}
+ override fun touchAutoDim(displayId: Int) {}
+ override fun transitionTo(
+ displayId: Int,
+ @BarTransitions.TransitionMode barMode: Int,
+ animate: Boolean,
+ ) {}
+ override fun disableAnimationsDuringHide(displayId: Int, delay: Long) {}
+ override fun getDefaultNavigationBarView(): NavigationBarView? = null
+ override fun getNavigationBarView(displayId: Int): NavigationBarView? = null
+ override fun isOverviewEnabled(displayId: Int) = false
+ override fun getDefaultNavigationBar(): NavigationBar? = null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
new file mode 100644
index 0000000..564e984
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.pip.Pip;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class NavigationBarControllerImpl implements
+ ConfigurationController.ConfigurationListener,
+ NavigationModeController.ModeChangedListener,
+ Dumpable, NavigationBarController {
+
+ private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
+ private FeatureFlags mFeatureFlags;
+ private final SecureSettings mSecureSettings;
+ private final DisplayTracker mDisplayTracker;
+ private final DisplayManager mDisplayManager;
+ private final TaskbarDelegate mTaskbarDelegate;
+ private final NavBarHelper mNavBarHelper;
+ private int mNavMode;
+ @VisibleForTesting boolean mIsLargeScreen;
+
+ /** A displayId - nav bar maps. */
+ @VisibleForTesting
+ SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
+
+ // Tracks config changes that will actually recreate the nav bar
+ private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
+ ActivityInfo.CONFIG_FONT_SCALE
+ | ActivityInfo.CONFIG_UI_MODE);
+
+ @Inject
+ public NavigationBarControllerImpl(Context context,
+ OverviewProxyService overviewProxyService,
+ NavigationModeController navigationModeController,
+ SysUiState sysUiFlagsContainer,
+ CommandQueue commandQueue,
+ @Main Handler mainHandler,
+ ConfigurationController configurationController,
+ NavBarHelper navBarHelper,
+ TaskbarDelegate taskbarDelegate,
+ NavigationBarComponent.Factory navigationBarComponentFactory,
+ DumpManager dumpManager,
+ AutoHideController autoHideController,
+ LightBarController lightBarController,
+ TaskStackChangeListeners taskStackChangeListeners,
+ Optional<Pip> pipOptional,
+ Optional<BackAnimation> backAnimation,
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings,
+ DisplayTracker displayTracker) {
+ mContext = context;
+ mHandler = mainHandler;
+ mNavigationBarComponentFactory = navigationBarComponentFactory;
+ mFeatureFlags = featureFlags;
+ mSecureSettings = secureSettings;
+ mDisplayTracker = displayTracker;
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ commandQueue.addCallback(mCommandQueueCallbacks);
+ configurationController.addCallback(this);
+ mConfigChanges.applyNewConfig(mContext.getResources());
+ mNavMode = navigationModeController.addListener(this);
+ mNavBarHelper = navBarHelper;
+ mTaskbarDelegate = taskbarDelegate;
+ mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager, autoHideController, lightBarController, pipOptional,
+ backAnimation.orElse(null), taskStackChangeListeners);
+ mIsLargeScreen = isLargeScreen(mContext);
+ dumpManager.registerDumpable(this);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ boolean isOldConfigLargeScreen = mIsLargeScreen;
+ mIsLargeScreen = isLargeScreen(mContext);
+ boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
+ boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
+ // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+ Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+ + " willApplyConfigToNavbars=" + willApplyConfig
+ + " navBarCount=" + mNavigationBars.size());
+ if (mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.onConfigurationChanged(newConfig);
+ }
+ // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
+ if (largeScreenChanged && updateNavbarForTaskbar()) {
+ return;
+ }
+
+ if (willApplyConfig) {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ recreateNavigationBar(mNavigationBars.keyAt(i));
+ }
+ } else {
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
+ }
+ }
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ if (mNavMode == mode) {
+ return;
+ }
+ final int oldMode = mNavMode;
+ mNavMode = mode;
+ updateAccessibilityButtonModeIfNeeded();
+
+ mHandler.post(() -> {
+ // create/destroy nav bar based on nav mode only in unfolded state
+ if (oldMode != mNavMode) {
+ updateNavbarForTaskbar();
+ }
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ NavigationBar navBar = mNavigationBars.valueAt(i);
+ if (navBar == null) {
+ continue;
+ }
+ navBar.getView().updateStates();
+ }
+ });
+ }
+
+ private void updateAccessibilityButtonModeIfNeeded() {
+ final int mode = mSecureSettings.getIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ mSecureSettings.putIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+ mSecureSettings.putIntForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ }
+ }
+
+ private boolean shouldCreateNavBarAndTaskBar(int displayId) {
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ return wms.hasNavigationBar(displayId);
+ } catch (RemoteException e) {
+ // Cannot get wms, just return false with warning message.
+ Log.w(TAG, "Cannot get WindowManager.");
+ return false;
+ }
+ }
+
+ /** @see #initializeTaskbarIfNecessary() */
+ private boolean updateNavbarForTaskbar() {
+ boolean taskbarShown = initializeTaskbarIfNecessary();
+ if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+ createNavigationBar(mContext.getDisplay(), null, null);
+ }
+ return taskbarShown;
+ }
+
+ /** @return {@code true} if taskbar is enabled, false otherwise */
+ private boolean initializeTaskbarIfNecessary() {
+ // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
+ boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
+ Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+
+ if (taskbarEnabled) {
+ Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
+ final int displayId = mContext.getDisplayId();
+ // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
+ mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
+ // Remove navigation bar when taskbar is showing
+ removeNavigationBar(displayId);
+ mTaskbarDelegate.init(displayId);
+ mNavBarHelper.setTogglingNavbarTaskbar(false);
+ Trace.endSection();
+
+ } else {
+ mTaskbarDelegate.destroy();
+ }
+ return taskbarEnabled;
+ }
+
+ private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ removeNavigationBar(displayId);
+ }
+
+ @Override
+ public void onDisplayReady(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ mIsLargeScreen = isLargeScreen(mContext);
+ createNavigationBar(display, null /* savedState */, null /* result */);
+ }
+
+ @Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ final NavigationBar navigationBar = getNavigationBar(displayId);
+ if (navigationBar != null) {
+ navigationBar.setNavigationBarLumaSamplingEnabled(enable);
+ }
+ }
+
+ @Override
+ public void showPinningEnterExitToast(boolean entering) {
+ int displayId = mContext.getDisplayId();
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEnterExitToast(entering);
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEnterExitToast(entering);
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ int displayId = mContext.getDisplayId();
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEscapeToast();
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEscapeToast();
+ }
+ }
+ };
+
+ /**
+ * Recreates the navigation bar for the given display.
+ */
+ private void recreateNavigationBar(int displayId) {
+ // TODO: Improve this flow so that we don't need to create a new nav bar but just
+ // the view
+ Bundle savedState = new Bundle();
+ NavigationBar bar = mNavigationBars.get(displayId);
+ if (bar != null) {
+ bar.onSaveInstanceState(savedState);
+ }
+ removeNavigationBar(displayId);
+ createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
+ }
+
+ @Override
+ public void createNavigationBars(final boolean includeDefaultDisplay,
+ RegisterStatusBarResult result) {
+ updateAccessibilityButtonModeIfNeeded();
+
+ // Don't need to create nav bar on the default display if we initialize TaskBar.
+ final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
+ && !initializeTaskbarIfNecessary();
+ Display[] displays = mDisplayTracker.getAllDisplays();
+ for (Display display : displays) {
+ if (shouldCreateDefaultNavbar
+ || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
+ createNavigationBar(display, null /* savedState */, result);
+ }
+ }
+ }
+
+ /**
+ * Adds a navigation bar on default display or an external display if the display supports
+ * system decorations.
+ *
+ * @param display the display to add navigation bar on.
+ */
+ @VisibleForTesting
+ void createNavigationBar(Display display, Bundle savedState,
+ RegisterStatusBarResult result) {
+ if (display == null) {
+ return;
+ }
+
+ final int displayId = display.getDisplayId();
+ final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
+
+ if (!shouldCreateNavBarAndTaskBar(displayId)) {
+ return;
+ }
+
+ // We may show TaskBar on the default display for large screen device. Don't need to create
+ // navigation bar for this case.
+ if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
+ return;
+ }
+
+ final Context context = isOnDefaultDisplay
+ ? mContext
+ : mContext.createDisplayContext(display);
+ NavigationBarComponent component = mNavigationBarComponentFactory.create(
+ context, savedState);
+ NavigationBar navBar = component.getNavigationBar();
+ navBar.init();
+ mNavigationBars.put(displayId, navBar);
+
+ navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ if (result != null) {
+ navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.removeOnAttachStateChangeListener(this);
+ }
+ });
+ }
+
+ @Override
+ public void removeNavigationBar(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.destroyView();
+ mNavigationBars.remove(displayId);
+ }
+ }
+
+ @Override
+ public void checkNavBarModes(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.checkNavBarModes();
+ }
+ }
+
+ @Override
+ public void finishBarAnimations(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.finishBarAnimations();
+ }
+ }
+
+ @Override
+ public void touchAutoDim(int displayId) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.touchAutoDim();
+ }
+ }
+
+ @Override
+ public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.transitionTo(barMode, animate);
+ }
+ }
+
+ @Override
+ public void disableAnimationsDuringHide(int displayId, long delay) {
+ NavigationBar navBar = mNavigationBars.get(displayId);
+ if (navBar != null) {
+ navBar.disableAnimationsDuringHide(delay);
+ }
+ }
+
+ @Override
+ public @Nullable NavigationBarView getDefaultNavigationBarView() {
+ return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
+ }
+
+ @Override
+ public @Nullable NavigationBarView getNavigationBarView(int displayId) {
+ NavigationBar navBar = getNavigationBar(displayId);
+ return (navBar == null) ? null : navBar.getView();
+ }
+
+ private @Nullable NavigationBar getNavigationBar(int displayId) {
+ return mNavigationBars.get(displayId);
+ }
+
+ @Override
+ public boolean isOverviewEnabled(int displayId) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ return navBarView.isOverviewEnabled();
+ } else {
+ return mTaskbarDelegate.isOverviewEnabled();
+ }
+ }
+
+ @Override
+ @Nullable
+ public NavigationBar getDefaultNavigationBar() {
+ return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mIsLargeScreen=" + mIsLargeScreen);
+ pw.println("mNavMode=" + mNavMode);
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ if (i > 0) {
+ pw.println();
+ }
+ mNavigationBars.valueAt(i).dump(pw);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
new file mode 100644
index 0000000..448f280
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing an instance of [NavigationBarController]. */
+@Module
+abstract class NavigationBarControllerModule {
+ @Binds
+ abstract fun navigationBarController(impl: NavigationBarControllerImpl): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
index 2a7704f..c1d98c9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
@@ -36,7 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.navigationbar.buttons.ReverseLinearLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index f6bfd6c..3ef5094 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -21,7 +21,7 @@
import android.view.View;
import android.view.WindowManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 1d73bc20..3e34318 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -24,7 +24,7 @@
import android.util.SparseArray;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.settings.DisplayTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 146b5f5..4d6d95a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -62,7 +62,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.ContextualButton;
@@ -359,6 +359,7 @@
public void setBackgroundExecutor(Executor bgExecutor) {
mBgExecutor = bgExecutor;
+ mRotationButtonController.setBgExecutor(bgExecutor);
}
public void setDisplayTracker(DisplayTracker displayTracker) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
new file mode 100644
index 0000000..b59912a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing a no-op instance of [NavigationBarController]. */
+@Module
+abstract class NoopNavigationBarControllerModule {
+ @Binds
+ abstract fun navigationBarController(
+ impl: NavigationBarControllerEmptyImpl
+ ): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java b/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java
index ac7baf5..939c096 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java
@@ -21,7 +21,7 @@
import android.util.Slog;
import android.widget.Toast;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysUIToast;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
index 9305d05..bd3a0c1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
@@ -26,7 +26,7 @@
import android.view.Surface;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
index 702be72..686facc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
@@ -40,7 +40,7 @@
import android.view.View;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Drawable for {@link KeyButtonView}s that supports tinting between two colors, rotation and shows
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index 3529142..dcf1a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,7 +58,7 @@
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
index 8ff7b7a..d780f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
@@ -28,7 +28,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.Comparator;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index b21b001..3dfd292 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -69,7 +69,7 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 9ddb78a..439b7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -5,7 +5,7 @@
import androidx.core.animation.Interpolator
import androidx.core.animation.PathInterpolator
import androidx.dynamicanimation.animation.SpringForce
-import com.android.systemui.R
+import com.android.systemui.res.R
data class EdgePanelParams(private var resources: Resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index ff22398..380846e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -51,7 +51,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.settings.DisplayTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
index 913b6528..5a22c38 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
@@ -28,7 +28,7 @@
import android.view.View;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.navigationbar.buttons.ButtonInterface;
public class NavigationHandle extends View implements ButtonInterface {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java
index 88622aa..b55de27 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java
@@ -21,7 +21,7 @@
import android.graphics.RectF;
import android.view.Surface;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/** Temporarily shown view when using QuickSwitch to switch between apps of different rotations */
public class QuickswitchOrientedNavHandle extends NavigationHandle {
diff --git a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
index be3168c..c216337 100644
--- a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
@@ -33,7 +33,7 @@
import android.util.Log;
import android.view.WindowManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Notify user that a {@link NetworkTemplate} is over its
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 0842fe0..093d098 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,7 +37,7 @@
import android.provider.Settings
import android.widget.Toast
import androidx.annotation.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
@@ -321,7 +321,7 @@
// When switched to a secondary user, the sysUI is still running in the main user, we will
// need to update the shortcut in the secondary user.
if (user == getCurrentRunningUser()) {
- updateNoteTaskAsUserInternal(user)
+ launchUpdateNoteTaskAsUser(user)
} else {
// TODO(b/278729185): Replace fire and forget service with a bounded service.
val intent = NoteTaskControllerUpdateService.createIntent(context)
@@ -330,23 +330,25 @@
}
@InternalNoteTaskApi
- fun updateNoteTaskAsUserInternal(user: UserHandle) {
- if (!userManager.isUserUnlocked(user)) {
- debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
- return
- }
+ fun launchUpdateNoteTaskAsUser(user: UserHandle) {
+ applicationScope.launch {
+ if (!userManager.isUserUnlocked(user)) {
+ debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
+ return@launch
+ }
- val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
- val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
+ val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
+ val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
- setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
+ setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
- if (hasNotesRoleHolder) {
- shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
- val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
- shortcutManager.updateShortcuts(listOf(updatedShortcut))
- } else {
- shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+ if (hasNotesRoleHolder) {
+ shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
+ val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
+ shortcutManager.updateShortcuts(listOf(updatedShortcut))
+ } else {
+ shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
index 3e352af..486fde1 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
@@ -44,7 +44,7 @@
override fun onCreate() {
super.onCreate()
// TODO(b/278729185): Replace fire and forget service with a bounded service.
- controller.updateNoteTaskAsUserInternal(user)
+ controller.launchUpdateNoteTaskAsUser(user)
stopSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index fe1034a..338d3ed 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -23,6 +23,7 @@
import android.view.KeyEvent
import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
+import android.view.ViewConfiguration
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.dagger.qualifiers.Background
@@ -65,12 +66,6 @@
* [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
*/
private fun initializeHandleSystemKey() {
- val callbacks =
- object : CommandQueue.Callbacks {
- override fun handleSystemKey(key: KeyEvent) {
- key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
- }
- }
commandQueue.addCallback(callbacks)
}
@@ -134,15 +129,39 @@
controller.updateNoteTaskForCurrentUserAndManagedProfiles()
}
}
-}
-/**
- * Maps a [KeyEvent] to a [NoteTaskEntryPoint]. If the [KeyEvent] does not represent a
- * [NoteTaskEntryPoint], returns null.
- */
-private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
- when {
- keyCode == KEYCODE_STYLUS_BUTTON_TAIL -> TAIL_BUTTON
- keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
- else -> null
+ /**
+ * Tracks a [KeyEvent], and determines if it should trigger an action to show the note task.
+ * Returns a [NoteTaskEntryPoint] if an action should be taken, and null otherwise.
+ */
+ private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
+ when {
+ keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
+ keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
+ else -> null
+ }
+
+ private var lastStylusButtonTailUpEventTime: Long = -MULTI_PRESS_TIMEOUT
+
+ /**
+ * Perform gesture detection for the stylus tail button to make sure we only show the note task
+ * when there is a single press. Long presses and multi-presses are ignored for now.
+ */
+ private fun KeyEvent.isTailButtonNotesGesture(): Boolean {
+ if (keyCode != KEYCODE_STYLUS_BUTTON_TAIL || action != KeyEvent.ACTION_UP) {
+ return false
+ }
+
+ val isMultiPress = (downTime - lastStylusButtonTailUpEventTime) < MULTI_PRESS_TIMEOUT
+ val isLongPress = (eventTime - downTime) >= LONG_PRESS_TIMEOUT
+ lastStylusButtonTailUpEventTime = eventTime
+ // For now, trigger action immediately on UP of a single press, without waiting for
+ // the multi-press timeout to expire.
+ return !isMultiPress && !isLongPress
}
+
+ companion object {
+ val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong()
+ val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt
index 63d4634..9785446 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt
@@ -24,7 +24,7 @@
import android.graphics.drawable.Icon
import android.os.PersistableBundle
import android.os.UserHandle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
/** Extension functions for [RoleManager] used **internally** by note task. */
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index f02d362..6d55d05 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -27,7 +27,7 @@
import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
index 36b435b0..59c76ad 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -27,7 +27,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* PeopleSpaceTileView renders an individual person's tile with associated status.
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index c6c9aca..188e867 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -55,7 +55,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.MessagingMessage;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 67e9664..733383e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -74,7 +74,7 @@
import androidx.core.math.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.people.data.model.PeopleTileModel;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
index 46c8d35..c567d56 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
@@ -31,7 +31,7 @@
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.people.PeopleSpaceTileView
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
index e27bfb3..ed7c21b 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
@@ -23,7 +23,7 @@
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.people.PeopleSpaceUtils
import com.android.systemui.people.PeopleTileViewHelper
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index fbf1a0e..1f66b84 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -21,7 +21,7 @@
import android.content.pm.PackageManager;
import android.os.Build;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.PluginModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.plugins.PluginActionManager;
diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
index 03d1f15..1cd5d91 100644
--- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
+++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
@@ -29,7 +29,7 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* View that shows a warning shortly before the device goes into sleep
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a765702..38204ab 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -65,7 +65,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.utils.PowerUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e1e1aae..1534653 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -48,7 +48,7 @@
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 310d234..8e1b00d 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -26,7 +26,7 @@
import android.widget.ImageView
import android.widget.LinearLayout
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableFrameLayout
import com.android.systemui.statusbar.events.BackgroundAnimatableView
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index eec69f98..4952d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -15,7 +15,7 @@
package com.android.systemui.privacy
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 35a7cf1..ac9a590 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -29,7 +29,7 @@
import android.widget.ImageView
import android.widget.TextView
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
index c202f14..b26ae6c 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogV2.kt
@@ -40,7 +40,7 @@
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.maybeForceFullscreen
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 8b41000..d09fbb3 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -15,7 +15,7 @@
package com.android.systemui.privacy
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
typealias Privacy = PrivacyType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
deleted file mode 100644
index e473dd2..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-
-/**
- * Creates AlphaControlledSlashImageView instead of SlashImageView
- */
-public class AlphaControlledSignalTileView extends SignalTileView {
- public AlphaControlledSignalTileView(Context context) {
- super(context);
- }
-
- @Override
- protected SlashImageView createSlashImageView(Context context) {
- return new AlphaControlledSlashImageView(context);
- }
-
- /**
- * Creates AlphaControlledSlashDrawable instead of regular SlashDrawables
- */
- public static class AlphaControlledSlashImageView extends SlashImageView {
- public AlphaControlledSlashImageView(Context context) {
- super(context);
- }
-
- public void setFinalImageTintList(ColorStateList tint) {
- super.setImageTintList(tint);
- final SlashDrawable slash = getSlash();
- if (slash != null) {
- ((AlphaControlledSlashDrawable)slash).setFinalTintList(tint);
- }
- }
-
- @Override
- protected void ensureSlashDrawable() {
- if (getSlash() == null) {
- final SlashDrawable slash = new AlphaControlledSlashDrawable(getDrawable());
- setSlash(slash);
- slash.setAnimationEnabled(getAnimationEnabled());
- setImageViewDrawable(slash);
- }
- }
- }
-
- /**
- * SlashDrawable that disobeys orders to change its drawable's tint except when you tell
- * it not to disobey. The slash still will animate its alpha.
- */
- public static class AlphaControlledSlashDrawable extends SlashDrawable {
- AlphaControlledSlashDrawable(Drawable d) {
- super(d);
- }
-
- @Override
- protected void setDrawableTintList(@Nullable ColorStateList tint) {
- }
-
- /**
- * Set a target tint list instead of
- */
- public void setFinalTintList(ColorStateList tint) {
- super.setDrawableTintList(tint);
- }
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
index afce69e..1b22f98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
@@ -25,7 +25,7 @@
import android.view.View;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class DataUsageGraph extends View {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index a3b901b..6f35cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -54,7 +54,7 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index e822dd5..4770d52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -16,7 +16,7 @@
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ddd9463..826f75f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -27,7 +27,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 768598a..f040435 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -24,7 +24,7 @@
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.ref.WeakReference;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 37e750b..b0f54ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -28,7 +28,7 @@
import android.widget.FrameLayout;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
index 26399d8..235fc0d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
@@ -29,7 +29,7 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 61905ae..c908e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -35,7 +35,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 85f557b..ffbd06f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -23,7 +23,7 @@
import android.widget.TextView;
import android.widget.Toast;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 596d024..fd81e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -47,7 +47,7 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 856c64a..1ab64b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -20,10 +20,8 @@
import android.os.Build;
import android.provider.Settings;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSFactory;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
@@ -63,12 +61,7 @@
void removeTiles(Collection<String> specs);
List<String> getSpecs();
- /**
- * Create a view for a tile, iterating over all possible {@link QSFactory}.
- *
- * @see QSFactory#createTileView
- */
- QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView);
+
/** Create a {@link QSTile} of a {@code tileSpec} type.
*
* This should only be called by classes that need to create one-off instances of tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index cb87e3c..2a36fdb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -23,7 +23,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.external.TileServiceRequestController
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -176,14 +175,6 @@
}
}
- override fun createTileView(
- themedContext: Context?,
- tile: QSTile?,
- collapsedView: Boolean
- ): QSTileView {
- return qsTileHost.createTileView(themedContext, tile, collapsedView)
- }
-
override fun createTile(tileSpec: String): QSTile? {
return qsTileHost.createTile(tileSpec)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b936c41..3227f75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -39,7 +39,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessSliderController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 81e3a2f..ef81674 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -44,6 +44,9 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
@@ -53,9 +56,6 @@
import javax.inject.Named;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
/**
* Controller for QSPanel views.
*
@@ -250,8 +250,8 @@
}
private void addTile(final QSTile tile, boolean collapsedView) {
- final TileRecord r =
- new TileRecord(tile, mHost.createTileView(getContext(), tile, collapsedView));
+ final QSTileViewImpl tileView = new QSTileViewImpl(getContext(), collapsedView);
+ final TileRecord r = new TileRecord(tile, tileView);
// TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
// b/250618218.
try {
@@ -490,7 +490,6 @@
public QSTile tile;
public com.android.systemui.plugins.qs.QSTileView tileView;
- public boolean scanState;
@Nullable
public QSTile.Callback callback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
index 001cbba..77a5ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
@@ -26,7 +26,7 @@
import androidx.core.widget.NestedScrollView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.touch.OverScroll;
import com.android.systemui.qs.touch.SwipeDetector;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index b70b94b..5c96e98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -73,7 +73,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.animation.Expandable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index e57db56..9a9626d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -31,7 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.ProtoDumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.nano.SystemUIProtoDump;
@@ -39,7 +39,6 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.external.TileLifecycleManager;
@@ -513,18 +512,6 @@
return null;
}
- @Override
- public QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView) {
- for (int i = 0; i < mQsFactories.size(); i++) {
- QSTileView view = mQsFactories.get(i)
- .createTileView(themedContext, tile, collapsedView);
- if (view != null) {
- return view;
- }
- }
- throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
- }
-
/**
* Check if a particular {@link CustomTile} has been added for a user and has not been removed
* since.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index d889979..6c32ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -25,10 +25,8 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.qs.QSTile.State;
/**
* Version of QSPanel that only shows N Quick Tiles in the QS Header.
@@ -91,19 +89,6 @@
return !mExpanded;
}
- @Override
- protected void drawTile(QSPanelControllerBase.TileRecord r, State state) {
- if (state instanceof SignalState) {
- SignalState copy = new SignalState();
- state.copyTo(copy);
- // No activity shown in the quick panel.
- copy.activityIn = false;
- copy.activityOut = false;
- state = copy;
- }
- super.drawTile(r, state);
- }
-
public void setMaxTiles(int maxTiles) {
mMaxTiles = maxTiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 585136a..099d19d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -24,7 +24,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 691a1a1..67c4208 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -26,7 +26,7 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.util.LargeScreenUtils;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 1ba11ef..b52554d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.util.AttributeSet
-import com.android.systemui.R
+import com.android.systemui.res.R
open class SideLabelTileLayout(
context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
deleted file mode 100644
index b609df5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-/** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/
-public class SignalTileView extends QSIconViewImpl {
- private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
- private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
-
- protected FrameLayout mIconFrame;
- protected ImageView mSignal;
- private ImageView mOverlay;
- private ImageView mIn;
- private ImageView mOut;
-
- private int mWideOverlayIconStartPadding;
- private int mSignalIndicatorToIconFrameSpacing;
-
- public SignalTileView(Context context) {
- super(context);
-
- mIn = addTrafficView(R.drawable.ic_qs_signal_in);
- mOut = addTrafficView(R.drawable.ic_qs_signal_out);
-
- setClipChildren(false);
- setClipToPadding(false);
-
- mWideOverlayIconStartPadding = context.getResources().getDimensionPixelSize(
- R.dimen.wide_type_icon_start_padding_qs);
- mSignalIndicatorToIconFrameSpacing = context.getResources().getDimensionPixelSize(
- R.dimen.signal_indicator_to_icon_frame_spacing);
- }
-
- private ImageView addTrafficView(int icon) {
- final ImageView traffic = new ImageView(mContext);
- traffic.setImageResource(icon);
- traffic.setAlpha(0f);
- addView(traffic);
- return traffic;
- }
-
- @Override
- protected View createIcon() {
- mIconFrame = new FrameLayout(mContext);
- mSignal = createSlashImageView(mContext);
- mIconFrame.addView(mSignal);
- mOverlay = new ImageView(mContext);
- mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- return mIconFrame;
- }
-
- protected SlashImageView createSlashImageView(Context context) {
- return new SlashImageView(context);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int hs = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.EXACTLY);
- int ws = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.AT_MOST);
- mIn.measure(ws, hs);
- mOut.measure(ws, hs);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- layoutIndicator(mIn);
- layoutIndicator(mOut);
- }
-
- @Override
- protected int getIconMeasureMode() {
- return MeasureSpec.AT_MOST;
- }
-
- private void layoutIndicator(View indicator) {
- boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- int left, right;
- if (isRtl) {
- right = getLeft() - mSignalIndicatorToIconFrameSpacing;
- left = right - indicator.getMeasuredWidth();
- } else {
- left = getRight() + mSignalIndicatorToIconFrameSpacing;
- right = left + indicator.getMeasuredWidth();
- }
- indicator.layout(
- left,
- mIconFrame.getBottom() - indicator.getMeasuredHeight(),
- right,
- mIconFrame.getBottom());
- }
-
- @Override
- public void setIcon(State state, boolean allowAnimations) {
- final SignalState s = (SignalState) state;
- setIcon(mSignal, s, allowAnimations);
-
- if (s.overlayIconId > 0) {
- mOverlay.setVisibility(VISIBLE);
- mOverlay.setImageResource(s.overlayIconId);
- } else {
- mOverlay.setVisibility(GONE);
- }
- if (s.overlayIconId > 0 && s.isOverlayIconWide) {
- mSignal.setPaddingRelative(mWideOverlayIconStartPadding, 0, 0, 0);
- } else {
- mSignal.setPaddingRelative(0, 0, 0, 0);
- }
- final boolean shouldAnimate = allowAnimations && isShown();
- // Do not show activity indicators
-// setVisibility(mIn, shouldAnimate, s.activityIn);
-// setVisibility(mOut, shouldAnimate, s.activityOut);
- }
-
- private void setVisibility(View view, boolean shown, boolean visible) {
- final float newAlpha = shown && visible ? 1 : 0;
- if (view.getAlpha() == newAlpha) return;
- if (shown) {
- view.animate()
- .setDuration(visible ? SHORT_DURATION : DEFAULT_DURATION)
- .alpha(newAlpha)
- .start();
- } else {
- view.setAlpha(newAlpha);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
deleted file mode 100644
index 9011853..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.ColorInt;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.FloatProperty;
-
-public class SlashDrawable extends Drawable {
-
- public static final float CORNER_RADIUS = 1f;
-
- private final Path mPath = new Path();
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- // These values are derived in un-rotated (vertical) orientation
- private static final float SLASH_WIDTH = 1.8384776f;
- private static final float SLASH_HEIGHT = 28f;
- private static final float CENTER_X = 10.65f;
- private static final float CENTER_Y = 11.869239f;
- private static final float SCALE = 24f;
-
- // Bottom is derived during animation
- private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
- private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
- private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
- // Draw the slash washington-monument style; rotate to no-u-turn style
- private static final float DEFAULT_ROTATION = -45f;
-
- private Drawable mDrawable;
- private final RectF mSlashRect = new RectF(0, 0, 0, 0);
- private float mRotation;
- private boolean mSlashed;
- @Nullable
- private Mode mTintMode;
- @Nullable
- private ColorStateList mTintList;
- private boolean mAnimationEnabled = true;
-
- public SlashDrawable(Drawable d) {
- mDrawable = d;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mDrawable != null ? mDrawable.getIntrinsicHeight(): 0;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mDrawable != null ? mDrawable.getIntrinsicWidth(): 0;
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mDrawable.setBounds(bounds);
- }
-
- public void setDrawable(Drawable d) {
- mDrawable = d;
- mDrawable.setCallback(getCallback());
- mDrawable.setBounds(getBounds());
- if (mTintMode != null) mDrawable.setTintMode(mTintMode);
- if (mTintList != null) mDrawable.setTintList(mTintList);
- invalidateSelf();
- }
-
- public void setRotation(float rotation) {
- if (mRotation == rotation) return;
- mRotation = rotation;
- invalidateSelf();
- }
-
- public void setAnimationEnabled(boolean enabled) {
- mAnimationEnabled = enabled;
- }
-
- // Animate this value on change
- private float mCurrentSlashLength;
- private final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
- @Override
- public void setValue(SlashDrawable object, float value) {
- object.mCurrentSlashLength = value;
- }
-
- @Override
- public Float get(SlashDrawable object) {
- return object.mCurrentSlashLength;
- }
- };
-
- public void setSlashed(boolean slashed) {
- if (mSlashed == slashed) return;
-
- mSlashed = slashed;
-
- final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
- final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
-
- if (mAnimationEnabled) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
- anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
- anim.setDuration(QS_ANIM_LENGTH);
- anim.start();
- } else {
- mCurrentSlashLength = end;
- invalidateSelf();
- }
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- canvas.save();
- Matrix m = new Matrix();
- final int width = getBounds().width();
- final int height = getBounds().height();
- final float radiusX = scale(CORNER_RADIUS, width);
- final float radiusY = scale(CORNER_RADIUS, height);
- updateRect(
- scale(LEFT, width),
- scale(TOP, height),
- scale(RIGHT, width),
- scale(TOP + mCurrentSlashLength, height)
- );
-
- mPath.reset();
- // Draw the slash vertically
- mPath.addRoundRect(mSlashRect, radiusX, radiusY, Direction.CW);
- // Rotate -45 + desired rotation
- m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
- mPath.transform(m);
- canvas.drawPath(mPath, mPaint);
-
- // Rotate back to vertical
- m.setRotate(-mRotation - DEFAULT_ROTATION, width / 2, height / 2);
- mPath.transform(m);
-
- // Draw another rect right next to the first, for clipping
- m.setTranslate(mSlashRect.width(), 0);
- mPath.transform(m);
- mPath.addRoundRect(mSlashRect, 1.0f * width, 1.0f * height, Direction.CW);
- m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
- mPath.transform(m);
- canvas.clipOutPath(mPath);
-
- mDrawable.draw(canvas);
- canvas.restore();
- }
-
- private float scale(float frac, int width) {
- return frac * width;
- }
-
- private void updateRect(float left, float top, float right, float bottom) {
- mSlashRect.left = left;
- mSlashRect.top = top;
- mSlashRect.right = right;
- mSlashRect.bottom = bottom;
- }
-
- @Override
- public void setTint(@ColorInt int tintColor) {
- super.setTint(tintColor);
- mDrawable.setTint(tintColor);
- mPaint.setColor(tintColor);
- }
-
- @Override
- public void setTintList(@Nullable ColorStateList tint) {
- mTintList = tint;
- super.setTintList(tint);
- setDrawableTintList(tint);
- mPaint.setColor(tint.getDefaultColor());
- invalidateSelf();
- }
-
- protected void setDrawableTintList(@Nullable ColorStateList tint) {
- mDrawable.setTintList(tint);
- }
-
- @Override
- public void setTintMode(@NonNull Mode tintMode) {
- mTintMode = tintMode;
- super.setTintMode(tintMode);
- mDrawable.setTintMode(tintMode);
- }
-
- @Override
- public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
- mDrawable.setAlpha(alpha);
- mPaint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
- mDrawable.setColorFilter(colorFilter);
- mPaint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getOpacity() {
- return 255;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 19bf018..9dc6aee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -15,7 +15,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
index a316e6a..edc16be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
@@ -18,17 +18,11 @@
import android.content.Context
import android.text.TextUtils
-import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.tileimpl.QSTileViewImpl
-/**
- * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
- */
-class CustomizeTileView(
- context: Context,
- icon: QSIconView
-) : QSTileViewImpl(context, icon, collapsed = false) {
+/** Class for displaying tiles in [QSCustomizer] with the new design (labels on the side). */
+class CustomizeTileView(context: Context) : QSTileViewImpl(context, collapsed = false) {
var showAppLabel = false
set(value) {
@@ -68,4 +62,4 @@
fun changeState(state: QSTile.State) {
handleStateChanged(state)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index d511d8a..7888f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,7 +33,7 @@
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 4002ac3..ce504b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -34,7 +34,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 596475e..c9a24d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -44,7 +44,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.TileLayout;
@@ -54,7 +54,6 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.dagger.QSThemedContext;
import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import java.util.ArrayList;
@@ -297,7 +296,7 @@
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- View view = new CustomizeTileView(context, new QSIconViewImpl(context));
+ View view = new CustomizeTileView(context);
frame.addView(view);
return new Holder(frame);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
index 1e426ad..92f17f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
@@ -23,7 +23,7 @@
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index d9f4484..a6226b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -33,7 +33,7 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index ce6b8d4..bcd9803 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -23,7 +23,7 @@
import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSContainerImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
index 1caab5a..8b6d914 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
@@ -21,7 +21,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class TileColorPicker {
@VisibleForTesting static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 3432628..1659c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -22,10 +22,9 @@
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
-import com.android.systemui.qs.tileimpl.QSIconViewImpl
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
import com.android.systemui.qs.tileimpl.QSTileViewImpl
@@ -68,7 +67,7 @@
private fun createTileView(tileData: TileData): QSTileView {
val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
- val tile = QSTileViewImpl(themedContext, QSIconViewImpl(themedContext), true)
+ val tile = QSTileViewImpl(themedContext, true)
val state = QSTile.BooleanState().apply {
label = tileData.label
handlesLongClick = false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
index d9e5580..899d0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -25,7 +25,7 @@
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.internal.statusbar.IAddTileResultCallback
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QSHost
import com.android.systemui.statusbar.CommandQueue
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt
index e969d4c6..5fa75ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt
@@ -22,7 +22,7 @@
import android.os.UserManager
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 3c53d77..d09b210 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -30,7 +30,7 @@
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 32146b5..769cb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 39745c8..38e7972 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -222,16 +222,8 @@
str2 = state.label?.toString()
str3 = state.icon?.toString()
int1 = state.state
- if (state is QSTile.SignalState) {
- bool1 = true
- bool2 = state.activityIn
- bool3 = state.activityOut
- }
},
- {
- "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
- if (bool1) " Activity in/out=$bool2/$bool3" else ""
- }
+ { "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 18f59b1..47c99f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -21,7 +21,7 @@
import android.database.ContentObserver
import android.provider.Settings
import android.util.SparseArray
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingList.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingList.kt
index b1c7433..83c8bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingList.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingList.kt
@@ -18,7 +18,7 @@
import android.content.res.Resources
import android.util.Log
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
import com.android.systemui.qs.pipeline.shared.TileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
index 58a31bc..88d7f06 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
@@ -20,7 +20,7 @@
import android.content.pm.PackageManager
import android.content.res.Resources
import android.text.TextUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 6b23f5d..9c7a734 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,7 +14,6 @@
package com.android.systemui.qs.tileimpl;
-import android.content.Context;
import android.os.Build;
import android.util.Log;
@@ -22,20 +21,18 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.util.leak.GarbageMonitor;
+import dagger.Lazy;
+
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
-import dagger.Lazy;
-
/**
* A factory that creates Quick Settings tiles based on a tileSpec
*
@@ -96,10 +93,4 @@
Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
-
- @Override
- public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
- QSIconView icon = tile.createTileView(context);
- return new QSTileViewImpl(context, icon, collapsedView);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 7e45491..d04e4f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -34,11 +34,10 @@
import android.widget.ImageView.ScaleType;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
import java.util.Objects;
@@ -114,8 +113,7 @@
protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
- if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
- || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
+ if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))) {
boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
mLastIcon = icon;
Drawable d = icon != null
@@ -135,15 +133,9 @@
((Animatable2) lastDrawable).clearAnimationCallbacks();
}
- if (iv instanceof SlashImageView) {
- ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
- ((SlashImageView) iv).setState(null, d);
- } else {
- iv.setImageDrawable(d);
- }
+ iv.setImageDrawable(d);
iv.setTag(R.id.qs_icon_tag, icon);
- iv.setTag(R.id.qs_slash_tag, state.slash);
iv.setPadding(0, padding, 0, padding);
if (d instanceof Animatable2) {
Animatable2 a = (Animatable2) d;
@@ -177,12 +169,7 @@
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
} else {
- if (iv instanceof AlphaControlledSlashImageView) {
- ((AlphaControlledSlashImageView)iv)
- .setFinalImageTintList(ColorStateList.valueOf(color));
- } else {
- setTint(iv, color);
- }
+ setTint(iv, color);
updateIcon(iv, state, allowAnimations);
}
} else {
@@ -195,11 +182,7 @@
}
private void animateGrayScale(int fromColor, int toColor, ImageView iv,
- final Runnable endRunnable) {
- if (iv instanceof AlphaControlledSlashImageView) {
- ((AlphaControlledSlashImageView)iv)
- .setFinalImageTintList(ColorStateList.valueOf(toColor));
- }
+ final Runnable endRunnable) {
mColorAnimator.cancel();
if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
PropertyValuesHolder values = PropertyValuesHolder.ofInt("color", fromColor, toColor);
@@ -229,7 +212,7 @@
}
protected View createIcon() {
- final ImageView icon = new SlashImageView(mContext);
+ final ImageView icon = new ImageView(mContext);
icon.setId(android.R.id.icon);
icon.setScaleType(ScaleType.FIT_CENTER);
return icon;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1ca2a96..70df09d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -60,7 +60,6 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -262,16 +261,6 @@
}
/**
- * Return the {@link QSIconView} to be used by this tile's view.
- *
- * @param context view context for the view
- * @return icon view for this tile
- */
- public QSIconView createTileView(Context context) {
- return new QSIconViewImpl(context);
- }
-
- /**
* Is a startup check whether this device currently supports this tile.
* Should not be used to conditionally hide tiles. Only checked on tile
* creation or whether should be shown in edit screen.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 764ef68..5a9c0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -44,7 +44,7 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
import com.android.systemui.plugins.qs.QSIconView
@@ -58,7 +58,6 @@
private const val TAG = "QSTileViewImpl"
open class QSTileViewImpl @JvmOverloads constructor(
context: Context,
- private val _icon: QSIconView,
private val collapsed: Boolean = false
) : QSTileView(context), HeightOverrideable, LaunchableView {
@@ -73,10 +72,11 @@
internal const val TILE_STATE_RES_PREFIX = "tile_states_"
}
- private var _position: Int = INVALID
+ private val icon: QSIconViewImpl = QSIconViewImpl(context)
+ private var position: Int = INVALID
override fun setPosition(position: Int) {
- _position = position
+ this.position = position
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -173,7 +173,7 @@
setPaddingRelative(startPadding, padding, padding, padding)
val iconSize = resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- addView(_icon, LayoutParams(iconSize, iconSize))
+ addView(icon, LayoutParams(iconSize, iconSize))
createAndAddLabels()
createAndAddSideView()
@@ -204,7 +204,7 @@
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- _icon.layoutParams.apply {
+ icon.layoutParams.apply {
height = iconSize
width = iconSize
}
@@ -291,7 +291,7 @@
}
override fun getIcon(): QSIconView {
- return _icon
+ return icon
}
override fun getIconWithBackground(): View {
@@ -425,16 +425,16 @@
}
}
}
- if (_position != INVALID) {
+ if (position != INVALID) {
info.collectionItemInfo =
- AccessibilityNodeInfo.CollectionItemInfo(_position, 1, 0, 1, false)
+ AccessibilityNodeInfo.CollectionItemInfo(position, 1, 0, 1, false)
}
}
override fun toString(): String {
val sb = StringBuilder(javaClass.simpleName).append('[')
sb.append("locInScreen=(${locInScreen[0]}, ${locInScreen[1]})")
- sb.append(", iconView=$_icon")
+ sb.append(", iconView=$icon")
sb.append(", tileState=$tileState")
sb.append("]")
return sb.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
deleted file mode 100644
index f1e82b6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.plugins.qs.QSTile.SlashState;
-import com.android.systemui.qs.SlashDrawable;
-
-public class SlashImageView extends ImageView {
-
- @Nullable
- @VisibleForTesting
- protected SlashDrawable mSlash;
- private boolean mAnimationEnabled = true;
-
- public SlashImageView(Context context) {
- super(context);
- }
-
- @Nullable
- protected SlashDrawable getSlash() {
- return mSlash;
- }
-
- protected void setSlash(SlashDrawable slash) {
- mSlash = slash;
- }
-
- protected void ensureSlashDrawable() {
- if (mSlash == null) {
- mSlash = new SlashDrawable(getDrawable());
- mSlash.setAnimationEnabled(mAnimationEnabled);
- super.setImageDrawable(mSlash);
- }
- }
-
- @Override
- public void setImageDrawable(@Nullable Drawable drawable) {
- if (drawable == null) {
- mSlash = null;
- super.setImageDrawable(null);
- } else if (mSlash == null) {
- setImageLevel(drawable.getLevel());
- super.setImageDrawable(drawable);
- } else {
- mSlash.setAnimationEnabled(mAnimationEnabled);
- mSlash.setDrawable(drawable);
- }
- }
-
- protected void setImageViewDrawable(SlashDrawable slash) {
- super.setImageDrawable(slash);
- }
-
- public void setAnimationEnabled(boolean enabled) {
- mAnimationEnabled = enabled;
- }
-
- public boolean getAnimationEnabled() {
- return mAnimationEnabled;
- }
-
- private void setSlashState(@NonNull SlashState slashState) {
- ensureSlashDrawable();
- mSlash.setRotation(slashState.rotation);
- mSlash.setSlashed(slashState.isSlashed);
- }
-
- public void setState(@Nullable SlashState state, @Nullable Drawable drawable) {
- if (state != null) {
- setImageDrawable(drawable);
- setSlashState(state);
- } else {
- mSlash = null;
- setImageDrawable(drawable);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index f672e51..168fa08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.qs.tileimpl
-import com.android.systemui.R
+import com.android.systemui.res.R
/** Return the subtitle resource Id of the given tile. */
object SubtitleArrayMapping {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 30765f7..fb71cef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -36,7 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index c709969..d98141f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -13,7 +13,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 9f10e6f..18d472b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,7 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 26912f8..d862f56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -39,9 +39,11 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -50,6 +52,7 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.List;
@@ -72,6 +75,10 @@
private final Executor mExecutor;
+ private final BluetoothTileDialogViewModel mDialogViewModel;
+
+ private final FeatureFlags mFeatureFlags;
+
@Inject
public BluetoothTile(
QSHost host,
@@ -83,13 +90,17 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- BluetoothController bluetoothController
+ BluetoothController bluetoothController,
+ FeatureFlags featureFlags,
+ BluetoothTileDialogViewModel dialogViewModel
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController;
mController.observe(getLifecycle(), mCallback);
mExecutor = new HandlerExecutor(mainHandler);
+ mFeatureFlags = featureFlags;
+ mDialogViewModel = dialogViewModel;
}
@Override
@@ -99,11 +110,15 @@
@Override
protected void handleClick(@Nullable View view) {
- // Secondary clicks are header clicks, just toggle.
- final boolean isEnabled = mState.value;
- // Immediately enter transient enabling state when turning bluetooth on.
- refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
- mController.setBluetoothEnabled(!isEnabled);
+ if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
+ mDialogViewModel.showDialog(mContext, view);
+ } else {
+ // Secondary clicks are header clicks, just toggle.
+ final boolean isEnabled = mState.value;
+ // Immediately enter transient enabling state when turning bluetooth on.
+ refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
+ mController.setBluetoothEnabled(!isEnabled);
+ }
}
@Override
@@ -151,10 +166,6 @@
}
state.dualTarget = true;
state.value = enabled;
- if (state.slash == null) {
- state.slash = new SlashState();
- }
- state.slash.isSlashed = !enabled;
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.secondaryLabel = TextUtils.emptyIfNull(
getSecondaryLabel(enabled, connecting, connected, state.isTransient));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 65ef6b9..b393f39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -30,7 +30,7 @@
import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index e9a2428..5c6e902 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -38,7 +38,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index cf9e346..ee57b2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -28,8 +28,8 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
-import com.android.systemui.R.drawable;
+import com.android.systemui.res.R;
+import com.android.systemui.res.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 4ecde61..993ada6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -29,8 +29,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.R.drawable;
+import com.android.systemui.res.R;
+import com.android.systemui.res.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index e769b5e..ccf7afb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -28,7 +28,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
index 5578558..c15289f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
@@ -28,7 +28,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.DataUsageGraph;
import java.text.DecimalFormat;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index ddaff3b..91b2d97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -25,7 +25,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9bb192b..0617b30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -45,7 +45,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.notification.EnableZenModeDialog;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index b5e6a0f..a08b3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -38,7 +38,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 2c986da..9fab51f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -29,7 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 1f9979a..64e3f16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -22,7 +22,7 @@
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.accessibility.fontscaling.FontScalingDialog
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 7c76c1a..9ee417e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -33,7 +33,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index ffe5489..1b504a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -41,15 +41,13 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
@@ -68,7 +66,7 @@
import javax.inject.Inject;
/** Quick settings tile: Internet **/
-public class InternetTile extends QSTileImpl<SignalState> {
+public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
public static final String TILE_SPEC = "internet";
@@ -114,18 +112,13 @@
}
@Override
- public SignalState newTileState() {
- SignalState s = new SignalState();
+ public BooleanState newTileState() {
+ BooleanState s = new BooleanState();
s.forceExpandIcon = true;
return s;
}
@Override
- public QSIconView createTileView(Context context) {
- return new AlphaControlledSignalTileView(context);
- }
-
- @Override
public Intent getLongClickIntent() {
return WIFI_SETTINGS;
}
@@ -453,7 +446,7 @@
}
@Override
- protected void handleUpdateState(SignalState state, Object arg) {
+ protected void handleUpdateState(BooleanState state, Object arg) {
mQSLogger.logInternetTileUpdate(
getTileSpec(), mLastTileState, arg == null ? "null" : arg.toString());
if (arg instanceof CellularCallbackInfo) {
@@ -506,18 +499,13 @@
}
}
- private void handleUpdateWifiState(SignalState state, Object arg) {
+ private void handleUpdateWifiState(BooleanState state, Object arg) {
WifiCallbackInfo cb = (WifiCallbackInfo) arg;
if (DEBUG) {
Log.d(TAG, "handleUpdateWifiState: " + "WifiCallbackInfo = " + cb.toString());
}
boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
- if (state.slash == null) {
- state.slash = new SlashState();
- state.slash.rotation = 6;
- }
- state.slash.isSlashed = false;
state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid));
state.state = Tile.STATE_ACTIVE;
state.dualTarget = true;
@@ -555,7 +543,6 @@
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
} else if (!state.value) {
- state.slash.isSlashed = true;
state.state = Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
} else if (wifiConnected) {
@@ -580,11 +567,11 @@
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
if (DEBUG) {
- Log.d(TAG, "handleUpdateWifiState: " + "SignalState = " + state.toString());
+ Log.d(TAG, "handleUpdateWifiState: " + "BooleanState = " + state.toString());
}
}
- private void handleUpdateCellularState(SignalState state, Object arg) {
+ private void handleUpdateCellularState(BooleanState state, Object arg) {
CellularCallbackInfo cb = (CellularCallbackInfo) arg;
if (DEBUG) {
Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
@@ -623,11 +610,11 @@
state.stateDescription = state.secondaryLabel;
}
if (DEBUG) {
- Log.d(TAG, "handleUpdateCellularState: " + "SignalState = " + state.toString());
+ Log.d(TAG, "handleUpdateCellularState: " + "BooleanState = " + state.toString());
}
}
- private void handleUpdateEthernetState(SignalState state, Object arg) {
+ private void handleUpdateEthernetState(BooleanState state, Object arg) {
EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
if (DEBUG) {
Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
@@ -641,7 +628,7 @@
state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
state.secondaryLabel = cb.mEthernetContentDescription;
if (DEBUG) {
- Log.d(TAG, "handleUpdateEthernetState: " + "SignalState = " + state.toString());
+ Log.d(TAG, "handleUpdateEthernetState: " + "BooleanState = " + state.toString());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 3b2f8b7..8103152 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -16,22 +16,19 @@
package com.android.systemui.qs.tiles
-import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.view.View
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.AlphaControlledSignalTileView
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
@@ -59,7 +56,7 @@
private val internetDialogFactory: InternetDialogFactory,
private val accessPointController: AccessPointController,
) :
- QSTileImpl<QSTile.SignalState>(
+ QSTileImpl<QSTile.BooleanState>(
host,
uiEventLogger,
backgroundLooper,
@@ -79,14 +76,11 @@
}
}
- override fun createTileView(context: Context): QSIconView =
- AlphaControlledSignalTileView(context)
-
override fun getTileLabel(): CharSequence =
mContext.getString(R.string.quick_settings_internet_label)
- override fun newTileState(): QSTile.SignalState {
- return QSTile.SignalState().also { it.forceExpandIcon = true }
+ override fun newTileState(): QSTile.BooleanState {
+ return QSTile.BooleanState().also { it.forceExpandIcon = true }
}
override fun handleClick(view: View?) {
@@ -100,7 +94,7 @@
}
}
- override fun handleUpdateState(state: QSTile.SignalState, arg: Any?) {
+ override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
model.applyTo(state, mContext)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 83c5688..9ddcf295 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -29,7 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -112,9 +112,6 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- if (state.slash == null) {
- state.slash = new SlashState();
- }
final boolean locationEnabled = mController.isLocationEnabled();
// Work around for bug 15916487: don't show location tile on top of lock screen. After the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 86a6a8c..7a7dbbc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -30,7 +30,7 @@
import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 29ccb76..a239c28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -34,7 +34,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 405e139..7e0fa07 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -36,7 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 1eb317a..78af976 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -130,10 +130,6 @@
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_onehanded_label);
state.icon = mIcon;
- if (state.slash == null) {
- state.slash = new SlashState();
- }
- state.slash.isSlashed = !state.value;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 1ba377b..f70e27d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -27,7 +27,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 544e6ad..3a247c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -20,6 +20,7 @@
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -41,7 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -210,7 +211,7 @@
public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
Log.i(TAG, "Successfully retrieved wallet cards.");
mIsWalletUpdating = false;
- List<WalletCard> cards = response.getWalletCards();
+ List<WalletCard> cards = getPaymentCards(response.getWalletCards());
if (cards.isEmpty()) {
Log.d(TAG, "No wallet cards exist.");
mCardViewDrawable = null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 2e04afb..b9d902a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -30,7 +30,7 @@
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R.drawable;
+import com.android.systemui.res.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 2d9f7dd..ea162fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -36,7 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 2d4652d..959afd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -30,7 +30,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 7c4f097..5832217 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -31,7 +31,7 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 9ffcba6..300cc56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -31,7 +31,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 5840a3d..7426615 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -32,7 +32,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.UserAvatarView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index b6b657e..32deb30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -34,7 +34,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 21da596..7a9384a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -32,7 +32,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -122,10 +122,6 @@
onManagedProfileRemoved();
}
- if (state.slash == null) {
- state.slash = new SlashState();
- }
-
if (arg instanceof Boolean) {
state.value = (Boolean) arg;
} else {
@@ -133,11 +129,6 @@
}
state.icon = mIcon;
- if (state.value) {
- state.slash.isSlashed = false;
- } else {
- state.slash.isSlashed = true;
- }
state.label = getTileLabel();
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
new file mode 100644
index 0000000..e9f907c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.qs.tiles.base.actions
+
+import android.content.Intent
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/**
+ * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard
+ * dismissing and tile from-view animations.
+ */
+@SysUISingleton
+class QSTileIntentUserActionHandler
+@Inject
+constructor(private val activityStarter: ActivityStarter) {
+
+ fun handle(userAction: QSTileUserAction, intent: Intent) {
+ val animationController: ActivityLaunchAnimator.Controller? =
+ userAction.view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ )
+ }
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
new file mode 100644
index 0000000..d6c9705
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import androidx.annotation.WorkerThread
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+
+interface QSTileDataToStateMapper<DATA_TYPE> {
+
+ /**
+ * Maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer. It's called
+ * on a background thread, so it's safe to perform long running operations there.
+ */
+ @WorkerThread fun map(config: QSTileConfig, data: DATA_TYPE): QSTileState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
new file mode 100644
index 0000000..c2a75fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -0,0 +1,185 @@
+package com.android.systemui.qs.tiles.base.viewmodel
+
+import androidx.annotation.CallSuper
+import androidx.annotation.VisibleForTesting
+import com.android.internal.util.Preconditions
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides a hassle-free way to implement new tiles according to current System UI architecture
+ * standards. THis ViewModel is cheap to instantiate and does nothing until it's moved to
+ * [QSTileLifecycle.ALIVE] state.
+ */
+abstract class BaseQSTileViewModel<DATA_TYPE>
+@VisibleForTesting
+constructor(
+ final override val config: QSTileConfig,
+ private val userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+ private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+ private val mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ private val backgroundDispatcher: CoroutineDispatcher,
+ private val tileScope: CoroutineScope,
+) : QSTileViewModel {
+
+ /**
+ * @param config contains all the static information (like TileSpec) about the tile.
+ * @param userActionInteractor encapsulates user input processing logic. Use it to start
+ * activities, show dialogs or otherwise update the tile state.
+ * @param tileDataInteractor provides [DATA_TYPE] and its availability.
+ * @param backgroundDispatcher is used to run the internal [DATA_TYPE] processing and call
+ * interactors methods. This should likely to be @Background CoroutineDispatcher.
+ * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer.
+ * It's called in [backgroundDispatcher], so it's safe to perform long running operations
+ * there.
+ */
+ constructor(
+ config: QSTileConfig,
+ userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+ tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+ mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ backgroundDispatcher: CoroutineDispatcher,
+ ) : this(
+ config,
+ userActionInteractor,
+ tileDataInteractor,
+ mapper,
+ backgroundDispatcher,
+ CoroutineScope(SupervisorJob())
+ )
+
+ private val userInputs: MutableSharedFlow<QSTileUserAction> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ private val userIds: MutableSharedFlow<Int> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ private val forceUpdates: MutableSharedFlow<Unit> =
+ MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ private lateinit var tileData: SharedFlow<DATA_TYPE>
+
+ override lateinit var state: SharedFlow<QSTileState>
+ override val isAvailable: StateFlow<Boolean> =
+ tileDataInteractor
+ .availability()
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ false,
+ )
+
+ private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
+
+ @CallSuper
+ override fun forceUpdate() {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ forceUpdates.tryEmit(Unit)
+ }
+
+ @CallSuper
+ override fun onUserIdChanged(userId: Int) {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ userIds.tryEmit(userId)
+ }
+
+ @CallSuper
+ override fun onActionPerformed(userAction: QSTileUserAction) {
+ Preconditions.checkState(tileData.replayCache.isNotEmpty())
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ userInputs.tryEmit(userAction)
+ }
+
+ @CallSuper
+ override fun onLifecycle(lifecycle: QSTileLifecycle) {
+ when (lifecycle) {
+ QSTileLifecycle.ALIVE -> {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.DEAD)
+ tileData = createTileDataFlow()
+ state =
+ tileData
+ // TODO(b/299908705): log data and corresponding tile state
+ .map { mapper.map(config, it) }
+ .flowOn(backgroundDispatcher)
+ .shareIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ replay = 1,
+ )
+ }
+ QSTileLifecycle.DEAD -> {
+ Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+ tileScope.coroutineContext.cancelChildren()
+ }
+ }
+ currentLifeState = lifecycle
+ }
+
+ private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+ userIds
+ .flatMapLatest { userId ->
+ merge(
+ userInputFlow(),
+ forceUpdates.map { StateUpdateTrigger.ForceUpdate },
+ )
+ .onStart { emit(StateUpdateTrigger.InitialRequest) }
+ .map { trigger -> QSTileDataRequest(userId, trigger) }
+ }
+ .flatMapLatest { request ->
+ // 1) get an updated data source
+ // 2) process user input, possibly triggering new data to be emitted
+ // This handles the case when the data isn't buffered in the interactor
+ // TODO(b/299908705): Log events that trigger data flow to update
+ val dataFlow = tileDataInteractor.tileData(request)
+ if (request.trigger is StateUpdateTrigger.UserAction<*>) {
+ userActionInteractor.handleInput(
+ request.trigger.action,
+ request.trigger.tileData as DATA_TYPE,
+ )
+ }
+ dataFlow
+ }
+ .flowOn(backgroundDispatcher)
+ .shareIn(
+ tileScope,
+ SharingStarted.WhileSubscribed(),
+ replay = 1, // we only care about the most recent value
+ )
+
+ private fun userInputFlow(): Flow<StateUpdateTrigger> {
+ data class StateWithData<T>(val state: QSTileState, val data: T)
+
+ // Skip the input until there is some data
+ return userInputs.sample(
+ state.combine(tileData) { state, data -> StateWithData(state, data) }
+ ) { input, stateWithData ->
+ StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 176983e..b866dda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -36,7 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.settingslib.wifi.WifiUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index c67078f..2350b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -59,7 +59,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f026d20..f516f55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,7 +75,7 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
new file mode 100644
index 0000000..efad9ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothAdapter.STATE_OFF
+import android.bluetooth.BluetoothAdapter.STATE_ON
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Holds business logic for the Bluetooth Dialog's bluetooth and device connection state */
+@SysUISingleton
+internal class BluetoothStateInteractor
+@Inject
+constructor(
+ private val localBluetoothManager: LocalBluetoothManager?,
+ @Application private val coroutineScope: CoroutineScope,
+) {
+
+ internal val updateBluetoothStateFlow: StateFlow<Boolean?> =
+ conflatedCallbackFlow {
+ val listener =
+ object : BluetoothCallback {
+ override fun onBluetoothStateChanged(bluetoothState: Int) {
+ if (bluetoothState == STATE_ON || bluetoothState == STATE_OFF) {
+ super.onBluetoothStateChanged(bluetoothState)
+ trySendWithFailureLogging(
+ bluetoothState == STATE_ON,
+ TAG,
+ "onBluetoothStateChanged"
+ )
+ }
+ }
+ }
+ localBluetoothManager?.eventManager?.registerCallback(listener)
+ awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) }
+ }
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
+ initialValue = null
+ )
+
+ internal var isBluetoothEnabled: Boolean
+ get() = localBluetoothManager?.bluetoothAdapter?.isEnabled == true
+ set(value) {
+ if (isBluetoothEnabled != value) {
+ localBluetoothManager?.bluetoothAdapter?.apply {
+ if (value) enable() else disable()
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "BtStateInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
new file mode 100644
index 0000000..6815a73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.Switch
+import android.widget.TextView
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Dialog for showing active, connected and saved bluetooth devices. */
+@SysUISingleton
+internal class BluetoothTileDialog
+constructor(
+ private val bluetoothToggleInitialValue: Boolean,
+ private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
+ private val uiEventLogger: UiEventLogger,
+ context: Context,
+) : SystemUIDialog(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK) {
+
+ private val mutableBluetoothStateSwitchedFlow: MutableStateFlow<Boolean> =
+ MutableStateFlow(bluetoothToggleInitialValue)
+ internal val bluetoothStateSwitchedFlow
+ get() = mutableBluetoothStateSwitchedFlow.asStateFlow()
+
+ private val mutableClickedFlow: MutableSharedFlow<Pair<DeviceItem, Int>> =
+ MutableSharedFlow(extraBufferCapacity = 1)
+ internal val deviceItemClickedFlow
+ get() = mutableClickedFlow.asSharedFlow()
+
+ private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback)
+
+ private lateinit var toggleView: Switch
+ private lateinit var doneButton: View
+ private lateinit var seeAllViewGroup: View
+ private lateinit var pairNewDeviceViewGroup: View
+ private lateinit var seeAllText: View
+ private lateinit var pairNewDeviceText: View
+ private lateinit var deviceListView: RecyclerView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
+
+ setContentView(LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null))
+
+ toggleView = requireViewById(R.id.bluetooth_toggle)
+ doneButton = requireViewById(R.id.done_button)
+ seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
+ pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
+ seeAllText = requireViewById(R.id.see_all_text)
+ pairNewDeviceText = requireViewById(R.id.pair_new_device_text)
+ deviceListView = requireViewById<RecyclerView>(R.id.device_list)
+
+ setupToggle()
+ setupRecyclerView()
+
+ doneButton.setOnClickListener { dismiss() }
+ seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceText.setOnClickListener {
+ bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
+ }
+ }
+
+ // TODO(b/298124674): use DiffUtil or AsyncListDiffer to avoid updating the whole list
+ internal fun onDeviceItemUpdated(
+ deviceItem: List<DeviceItem>,
+ showSeeAll: Boolean,
+ showPairNewDevice: Boolean
+ ) {
+ seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ deviceItemAdapter.refreshDeviceItemList(deviceItem)
+ }
+
+ internal fun onDeviceItemUpdatedAtPosition(deviceItem: DeviceItem, position: Int) {
+ deviceItemAdapter.refreshDeviceItem(deviceItem, position)
+ }
+
+ internal fun onBluetoothStateUpdated(isEnabled: Boolean) {
+ toggleView.isChecked = isEnabled
+ }
+
+ private fun setupToggle() {
+ toggleView.isChecked = bluetoothToggleInitialValue
+ toggleView.setOnCheckedChangeListener { _, isChecked ->
+ mutableBluetoothStateSwitchedFlow.value = isChecked
+ uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TOGGLE_CLICKED)
+ }
+ }
+
+ private fun setupRecyclerView() {
+ deviceListView.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = deviceItemAdapter
+ }
+ }
+
+ internal inner class Adapter(private val onClickCallback: BluetoothTileDialogCallback) :
+ RecyclerView.Adapter<Adapter.DeviceItemViewHolder>() {
+
+ private val deviceItem: MutableList<DeviceItem> = mutableListOf()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceItemViewHolder {
+ val view =
+ LayoutInflater.from(parent.context)
+ .inflate(R.layout.bluetooth_device_item, parent, false)
+ return DeviceItemViewHolder(view)
+ }
+
+ override fun getItemCount() = deviceItem.size
+
+ override fun onBindViewHolder(holder: DeviceItemViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item, position, onClickCallback)
+ }
+
+ internal fun getItem(position: Int) = deviceItem[position]
+
+ internal fun refreshDeviceItemList(updated: List<DeviceItem>) {
+ deviceItem.clear()
+ deviceItem.addAll(updated)
+ notifyDataSetChanged()
+ }
+
+ internal fun refreshDeviceItem(updated: DeviceItem, position: Int) {
+ deviceItem[position] = updated
+ notifyItemChanged(position)
+ }
+
+ internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ private val container = view.requireViewById<View>(R.id.bluetooth_device_row)
+ private val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
+ private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name)
+ private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary)
+ private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon)
+ private val gearView = view.requireViewById<View>(R.id.gear_icon)
+
+ internal fun bind(
+ item: DeviceItem,
+ position: Int,
+ deviceItemOnClickCallback: BluetoothTileDialogCallback
+ ) {
+ container.apply {
+ isEnabled = item.isEnabled
+ alpha = item.alpha
+ background = item.background
+ }
+ deviceView.setOnClickListener {
+ mutableClickedFlow.tryEmit(Pair(item, position))
+ uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
+ }
+ nameView.text = item.deviceName
+ summaryView.text = item.connectionSummary
+ iconView.apply {
+ item.iconWithDescription?.let {
+ setImageDrawable(it.first)
+ contentDescription = it.second
+ }
+ }
+ gearView.setOnClickListener {
+ deviceItemOnClickCallback.onDeviceItemGearClicked(item, it)
+ }
+ }
+ }
+ }
+
+ internal companion object {
+ const val ENABLED_ALPHA = 1.0f
+ const val DISABLED_ALPHA = 0.3f
+ const val MAX_DEVICE_ITEM_ENTRY = 3
+ const val ACTION_BLUETOOTH_DEVICE_DETAILS =
+ "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
+ const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
+ "com.android.settings.PREVIOUSLY_CONNECTED_DEVICE"
+ const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt
new file mode 100644
index 0000000..ea51bee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Repository to get CachedBluetoothDevices for the Bluetooth Dialog. */
+@SysUISingleton
+internal class BluetoothTileDialogRepository
+@Inject
+constructor(
+ private val localBluetoothManager: LocalBluetoothManager?,
+ private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
+) {
+ internal val cachedDevices: Collection<CachedBluetoothDevice>
+ get() {
+ return if (
+ localBluetoothManager == null ||
+ bluetoothAdapter == null ||
+ !bluetoothAdapter.isEnabled
+ ) {
+ emptyList()
+ } else {
+ localBluetoothManager.cachedDeviceManager.cachedDevicesCopy
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
new file mode 100644
index 0000000..2865ad7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** UI Events for the bluetooth tile dialog. */
+enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The bluetooth tile dialog is shown") BLUETOOTH_TILE_DIALOG_SHOWN(1493),
+ @UiEvent(doc = "The master toggle is clicked") BLUETOOTH_TOGGLE_CLICKED(1494),
+ @UiEvent(doc = "Pair new device is clicked") PAIR_NEW_DEVICE_CLICKED(1495),
+ @UiEvent(doc = "See all is clicked") SEE_ALL_CLICKED(1496),
+ @UiEvent(doc = "Gear icon clicked") DEVICE_GEAR_CLICKED(1497),
+ @UiEvent(doc = "Device clicked") DEVICE_CLICKED(1498),
+ @UiEvent(doc = "Connected device clicked to active") CONNECTED_DEVICE_SET_ACTIVE(1499),
+ @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500);
+
+ override fun getId() = metricId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
new file mode 100644
index 0000000..012484f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PAIR_NEW_DEVICE
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.MAX_DEVICE_ITEM_ENTRY
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+/** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */
+@SysUISingleton
+internal class BluetoothTileDialogViewModel
+@Inject
+constructor(
+ private val deviceItemInteractor: DeviceItemInteractor,
+ private val bluetoothStateInteractor: BluetoothStateInteractor,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val activityStarter: ActivityStarter,
+ private val uiEventLogger: UiEventLogger,
+ @Application private val coroutineScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+) : BluetoothTileDialogCallback {
+
+ private var job: Job? = null
+
+ @VisibleForTesting internal var dialog: BluetoothTileDialog? = null
+
+ /**
+ * Shows the dialog.
+ *
+ * @param context The context in which the dialog is displayed.
+ * @param view The view from which the dialog is shown.
+ */
+ fun showDialog(context: Context, view: View?) {
+ dismissDialog()
+
+ var updateDeviceItemJob: Job? = null
+
+ job =
+ coroutineScope.launch(mainDispatcher) {
+ dialog = createBluetoothTileDialog(context)
+ view?.let { dialogLaunchAnimator.showFromView(dialog!!, it) } ?: dialog!!.show()
+ updateDeviceItemJob?.cancel()
+ updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) }
+
+ bluetoothStateInteractor.updateBluetoothStateFlow
+ .filterNotNull()
+ .onEach {
+ dialog!!.onBluetoothStateUpdated(it)
+ updateDeviceItemJob?.cancel()
+ updateDeviceItemJob = launch {
+ deviceItemInteractor.updateDeviceItems(context)
+ }
+ }
+ .launchIn(this)
+
+ deviceItemInteractor.updateDeviceItemsFlow
+ .onEach {
+ updateDeviceItemJob?.cancel()
+ updateDeviceItemJob = launch {
+ deviceItemInteractor.updateDeviceItems(context)
+ }
+ }
+ .launchIn(this)
+
+ deviceItemInteractor.deviceItemFlow
+ .filterNotNull()
+ .onEach {
+ dialog!!.onDeviceItemUpdated(
+ it.take(MAX_DEVICE_ITEM_ENTRY),
+ showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
+ showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
+ )
+ }
+ .launchIn(this)
+
+ dialog!!
+ .bluetoothStateSwitchedFlow
+ .onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
+ .launchIn(this)
+
+ dialog!!
+ .deviceItemClickedFlow
+ .onEach {
+ if (deviceItemInteractor.updateDeviceItemOnClick(it.first)) {
+ dialog!!.onDeviceItemUpdatedAtPosition(it.first, it.second)
+ }
+ }
+ .launchIn(this)
+ }
+ }
+
+ private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog {
+ return BluetoothTileDialog(
+ bluetoothStateInteractor.isBluetoothEnabled,
+ this@BluetoothTileDialogViewModel,
+ uiEventLogger,
+ context
+ )
+ .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } }
+ }
+
+ override fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_GEAR_CLICKED)
+ val intent =
+ Intent(ACTION_BLUETOOTH_DEVICE_DETAILS).apply {
+ putExtra(
+ ":settings:show_fragment_args",
+ Bundle().apply {
+ putString("device_address", deviceItem.cachedBluetoothDevice.address)
+ }
+ )
+ }
+ startSettingsActivity(intent, view)
+ }
+
+ override fun onSeeAllClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SEE_ALL_CLICKED)
+ startSettingsActivity(Intent(ACTION_PREVIOUSLY_CONNECTED_DEVICE), view)
+ }
+
+ override fun onPairNewDeviceClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
+ startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
+ }
+
+ private fun dismissDialog() {
+ job?.cancel()
+ job = null
+ dialog?.dismiss()
+ dialog = null
+ }
+
+ private fun startSettingsActivity(intent: Intent, view: View) {
+ dialog?.run {
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+ activityStarter.postStartActivityDismissingKeyguard(
+ intent,
+ 0,
+ dialogLaunchAnimator.createActivityLaunchController(view)
+ )
+ }
+ }
+}
+
+internal interface BluetoothTileDialogCallback {
+ fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View)
+ fun onSeeAllClicked(view: View)
+ fun onPairNewDeviceClicked(view: View)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
new file mode 100644
index 0000000..03ae5e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.graphics.drawable.Drawable
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA
+
+enum class DeviceItemType {
+ AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ CONNECTED_BLUETOOTH_DEVICE,
+ SAVED_BLUETOOTH_DEVICE,
+}
+
+data class DeviceItem(
+ val type: DeviceItemType,
+ val cachedBluetoothDevice: CachedBluetoothDevice,
+ val deviceName: String = "",
+ val connectionSummary: String = "",
+ val iconWithDescription: Pair<Drawable, String>? = null,
+ val background: Drawable? = null,
+ var isEnabled: Boolean = true,
+ var alpha: Float = ENABLED_ALPHA
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
new file mode 100644
index 0000000..a16a9f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothDevice
+import android.content.Context
+import android.media.AudioManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.res.R
+
+private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
+private val connected = R.string.quick_settings_bluetooth_device_connected
+private val saved = R.string.quick_settings_bluetooth_device_saved
+
+/** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */
+internal abstract class DeviceItemFactory {
+ abstract fun isFilterMatched(
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager?
+ ): Boolean
+
+ abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem
+}
+
+internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
+ override fun isFilterMatched(
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager?
+ ): Boolean {
+ return BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
+ }
+
+ // TODO(b/298124674): move create() to the abstract class to reduce duplicate code
+ override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+ return DeviceItem(
+ type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedDevice,
+ deviceName = cachedDevice.name,
+ connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(connected),
+ iconWithDescription =
+ BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
+ Pair(p.first, p.second)
+ },
+ background = context.getDrawable(backgroundOn),
+ isEnabled = !cachedDevice.isBusy,
+ alpha =
+ if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
+ else BluetoothTileDialog.ENABLED_ALPHA,
+ )
+ }
+}
+
+internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
+ override fun isFilterMatched(
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager?
+ ): Boolean {
+ return BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
+ }
+
+ override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+ return DeviceItem(
+ type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedDevice,
+ deviceName = cachedDevice.name,
+ connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(connected),
+ iconWithDescription =
+ BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
+ Pair(p.first, p.second)
+ },
+ isEnabled = !cachedDevice.isBusy,
+ alpha =
+ if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
+ else BluetoothTileDialog.ENABLED_ALPHA,
+ )
+ }
+}
+
+internal class SavedDeviceItemFactory : DeviceItemFactory() {
+ override fun isFilterMatched(
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager?
+ ): Boolean {
+ return cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
+ }
+
+ override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+ return DeviceItem(
+ type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedDevice,
+ deviceName = cachedDevice.name,
+ connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(saved),
+ iconWithDescription =
+ BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
+ Pair(p.first, p.second)
+ },
+ isEnabled = !cachedDevice.isBusy,
+ alpha =
+ if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
+ else BluetoothTileDialog.ENABLED_ALPHA,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
new file mode 100644
index 0000000..fcd0ce6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.content.Context
+import android.media.AudioManager
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.withContext
+
+/** Holds business logic for the Bluetooth Dialog after clicking on the Bluetooth QS tile. */
+@SysUISingleton
+internal class DeviceItemInteractor
+@Inject
+constructor(
+ private val bluetoothTileDialogRepository: BluetoothTileDialogRepository,
+ private val audioManager: AudioManager,
+ private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter(),
+ private val localBluetoothManager: LocalBluetoothManager?,
+ private val uiEventLogger: UiEventLogger,
+ @Application private val coroutineScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ private val mutableDeviceItemFlow: MutableStateFlow<List<DeviceItem>?> = MutableStateFlow(null)
+ internal val deviceItemFlow
+ get() = mutableDeviceItemFlow.asStateFlow()
+
+ internal val updateDeviceItemsFlow: SharedFlow<Unit> =
+ conflatedCallbackFlow {
+ val listener =
+ object : BluetoothCallback {
+ override fun onActiveDeviceChanged(
+ activeDevice: CachedBluetoothDevice?,
+ bluetoothProfile: Int
+ ) {
+ super.onActiveDeviceChanged(activeDevice, bluetoothProfile)
+ trySendWithFailureLogging(Unit, TAG, "onActiveDeviceChanged")
+ }
+
+ override fun onConnectionStateChanged(
+ cachedDevice: CachedBluetoothDevice?,
+ state: Int
+ ) {
+ super.onConnectionStateChanged(cachedDevice, state)
+ trySendWithFailureLogging(Unit, TAG, "onConnectionStateChanged")
+ }
+
+ override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) {
+ super.onDeviceAdded(cachedDevice)
+ trySendWithFailureLogging(Unit, TAG, "onDeviceAdded")
+ }
+
+ override fun onProfileConnectionStateChanged(
+ cachedDevice: CachedBluetoothDevice,
+ state: Int,
+ bluetoothProfile: Int
+ ) {
+ super.onProfileConnectionStateChanged(
+ cachedDevice,
+ state,
+ bluetoothProfile
+ )
+ trySendWithFailureLogging(Unit, TAG, "onProfileConnectionStateChanged")
+ }
+ }
+ localBluetoothManager?.eventManager?.registerCallback(listener)
+ awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) }
+ }
+ .shareIn(coroutineScope, SharingStarted.WhileSubscribed(replayExpirationMillis = 0))
+
+ private var deviceItemFactoryList: List<DeviceItemFactory> =
+ listOf(
+ AvailableMediaDeviceItemFactory(),
+ ConnectedDeviceItemFactory(),
+ SavedDeviceItemFactory()
+ )
+
+ private var displayPriority: List<DeviceItemType> =
+ listOf(
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+ )
+
+ internal suspend fun updateDeviceItems(context: Context) {
+ withContext(backgroundDispatcher) {
+ val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices
+
+ mutableDeviceItemFlow.value =
+ bluetoothTileDialogRepository.cachedDevices
+ .mapNotNull { cachedDevice ->
+ deviceItemFactoryList
+ .firstOrNull { it.isFilterMatched(cachedDevice, audioManager) }
+ ?.create(context, cachedDevice)
+ }
+ .sort(displayPriority, mostRecentlyConnectedDevices)
+ }
+ }
+
+ private fun List<DeviceItem>.sort(
+ displayPriority: List<DeviceItemType>,
+ mostRecentlyConnectedDevices: List<BluetoothDevice>?
+ ): List<DeviceItem> {
+ return this.sortedWith(
+ compareBy<DeviceItem> { displayPriority.indexOf(it.type) }
+ .thenBy {
+ mostRecentlyConnectedDevices?.indexOf(it.cachedBluetoothDevice.device) ?: 0
+ }
+ )
+ }
+
+ internal fun updateDeviceItemOnClick(deviceItem: DeviceItem): Boolean {
+ var isClicked = false
+ when (deviceItem.type) {
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+ if (!BluetoothUtils.isActiveMediaDevice(deviceItem.cachedBluetoothDevice)) {
+ deviceItem.cachedBluetoothDevice.setActive()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
+ isClicked = true
+ }
+ }
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {}
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
+ deviceItem.cachedBluetoothDevice.connect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ isClicked = true
+ }
+ }
+ if (isClicked) {
+ deviceItem.isEnabled = false
+ deviceItem.alpha = BluetoothTileDialog.DISABLED_ALPHA
+ }
+ return isClicked
+ }
+
+ internal fun setDeviceItemFactoryListForTesting(list: List<DeviceItemFactory>) {
+ deviceItemFactoryList = list
+ }
+
+ internal fun setDisplayPriorityForTesting(list: List<DeviceItemType>) {
+ displayPriority = list
+ }
+
+ companion object {
+ private const val TAG = "DeviceItemInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
index 39db703..1d5c1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
@@ -1,6 +1,6 @@
package com.android.systemui.qs.tiles.viewmodel
enum class QSTileLifecycle {
- ON_CREATE,
- ON_DESTROY,
+ ALIVE,
+ DEAD,
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index 49077f3..d66d0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -1,28 +1,35 @@
package com.android.systemui.qs.tiles.viewmodel
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
/**
* Represents tiles behaviour logic. This ViewModel is a connection between tile view and data
- * layers.
+ * layers. All direct inheritors must be added to the [QSTileViewModelInterfaceComplianceTest] class
+ * to pass compliance tests.
+ *
+ * All methods of this view model should be considered running on the main thread. This means no
+ * synchronous long running operations are permitted in any method.
*/
interface QSTileViewModel {
/**
- * State of the tile to be shown by the view. Favor reactive consumption over the
- * [StateFlow.value], because there is no guarantee that current value would be available at any
- * time.
+ * State of the tile to be shown by the view. It's guaranteed that it's only accessed between
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD].
*/
- val state: StateFlow<QSTileState>
+ val state: SharedFlow<QSTileState>
val config: QSTileConfig
- val isAvailable: Flow<Boolean>
+ /**
+ * Specifies whether this device currently supports this tile. This might be called outside of
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds (for example in Edit Mode).
+ */
+ val isAvailable: StateFlow<Boolean>
/**
* Handles ViewModel lifecycle. Implementations should be inactive outside of
- * [QSTileLifecycle.ON_CREATE] and [QSTileLifecycle.ON_DESTROY] bounds.
+ * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds.
*/
fun onLifecycle(lifecycle: QSTileLifecycle)
@@ -33,9 +40,17 @@
*/
fun onUserIdChanged(userId: Int)
- /** Triggers emit of the new [QSTileState] in [state]. */
+ /** Triggers the emission of the new [QSTileState] in a [state]. */
fun forceUpdate()
/** Notifies underlying logic about user input. */
fun onActionPerformed(userAction: QSTileUserAction)
}
+
+/**
+ * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor
+ * reactive consumption over the [currentState], because there is no guarantee that current value
+ * would be available at any time.
+ */
+val QSTileViewModel.currentState: QSTileState?
+ get() = state.replayCache.lastOrNull()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 4c9c99c..acd7510 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -26,7 +26,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 6912114..4b21e44 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -29,7 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.CommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index d23beda..4465504 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -104,7 +104,6 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
@@ -117,7 +116,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -146,11 +144,11 @@
private final SceneContainerFlags mSceneContainerFlags;
private final Executor mMainExecutor;
private final ShellInterface mShellInterface;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Lazy<ShadeViewController> mShadeViewControllerLazy;
private SysUiState mSysUiState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
+ private final ScreenPinningRequest mScreenPinningRequest;
private final NotificationShadeWindowController mStatusBarWinController;
private final Provider<SceneInteractor> mSceneInteractor;
@@ -185,9 +183,7 @@
@Override
public void startScreenPinning(int taskId) {
verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
- mCentralSurfacesOptionalLazy.get().ifPresent(
- statusBar -> statusBar.showScreenPinningRequest(taskId,
- false /* allowCancel */)));
+ mScreenPinningRequest.showPrompt(taskId, false /* allowCancel */));
}
@Override
@@ -206,40 +202,38 @@
public void onStatusBarTouchEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
// TODO move this logic to message queue
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
- if (event.getActionMasked() == ACTION_DOWN) {
- mShadeViewControllerLazy.get().startExpandLatencyTracking();
+ if (event.getActionMasked() == ACTION_DOWN) {
+ mShadeViewControllerLazy.get().startExpandLatencyTracking();
+ }
+ mHandler.post(() -> {
+ int action = event.getActionMasked();
+ if (action == ACTION_DOWN) {
+ mInputFocusTransferStarted = true;
+ mInputFocusTransferStartY = event.getY();
+ mInputFocusTransferStartMillis = event.getEventTime();
+
+ // If scene framework is enabled, set the scene container window to
+ // visible and let the touch "slip" into that window.
+ if (mSceneContainerFlags.isEnabled()) {
+ mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+ } else {
+ mShadeViewControllerLazy.get().startInputFocusTransfer();
+ }
}
- mHandler.post(() -> {
- int action = event.getActionMasked();
- if (action == ACTION_DOWN) {
- mInputFocusTransferStarted = true;
- mInputFocusTransferStartY = event.getY();
- mInputFocusTransferStartMillis = event.getEventTime();
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mInputFocusTransferStarted = false;
- // If scene framework is enabled, set the scene container window to
- // visible and let the touch "slip" into that window.
- if (mSceneContainerFlags.isEnabled()) {
- mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+ if (!mSceneContainerFlags.isEnabled()) {
+ float velocity = (event.getY() - mInputFocusTransferStartY)
+ / (event.getEventTime() - mInputFocusTransferStartMillis);
+ if (action == ACTION_CANCEL) {
+ mShadeViewControllerLazy.get().cancelInputFocusTransfer();
} else {
- centralSurfaces.onInputFocusTransfer(
- mInputFocusTransferStarted, false /* cancel */,
- 0 /* velocity */);
+ mShadeViewControllerLazy.get().finishInputFocusTransfer(velocity);
}
}
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- mInputFocusTransferStarted = false;
-
- if (!mSceneContainerFlags.isEnabled()) {
- float velocity = (event.getY() - mInputFocusTransferStartY)
- / (event.getEventTime() - mInputFocusTransferStartMillis);
- centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
- action == ACTION_CANCEL,
- velocity);
- }
- }
- event.recycle();
- });
+ }
+ event.recycle();
});
});
}
@@ -247,8 +241,7 @@
@Override
public void onStatusBarTrackpadEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () ->
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces ->
- centralSurfaces.onStatusBarTrackpadEvent(event)));
+ mShadeViewControllerLazy.get().handleExternalTouch(event));
}
@Override
@@ -493,9 +486,9 @@
notifySystemUiStateFlags(mSysUiState.getFlags());
notifyConnectionChanged();
- if (mLatchForOnUserChanging != null) {
- mLatchForOnUserChanging.countDown();
- mLatchForOnUserChanging = null;
+ if (mDoneUserChanging != null) {
+ mDoneUserChanging.run();
+ mDoneUserChanging = null;
}
}
@@ -551,14 +544,14 @@
}
};
- private CountDownLatch mLatchForOnUserChanging;
+ private Runnable mDoneUserChanging;
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
public void onUserChanging(int newUser, @NonNull Context userContext,
- CountDownLatch latch) {
+ @NonNull Runnable resultCallback) {
mConnectionBackoffAttempts = 0;
- mLatchForOnUserChanging = latch;
+ mDoneUserChanging = resultCallback;
internalConnectToCurrentUser("User changed");
}
};
@@ -570,8 +563,8 @@
CommandQueue commandQueue,
ShellInterface shellInterface,
Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<ShadeViewController> shadeViewControllerLazy,
+ ScreenPinningRequest screenPinningRequest,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController,
SysUiState sysUiState,
@@ -597,10 +590,10 @@
mSceneContainerFlags = sceneContainerFlags;
mMainExecutor = mainExecutor;
mShellInterface = shellInterface;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeViewControllerLazy = shadeViewControllerLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
+ mScreenPinningRequest = screenPinningRequest;
mStatusBarWinController = statusBarWinController;
mSceneInteractor = sceneInteractor;
mUserTracker = userTracker;
@@ -771,10 +764,8 @@
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
- mInputFocusTransferStarted = false;
- centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
- });
+ mInputFocusTransferStarted = false;
+ mShadeViewControllerLazy.get().cancelInputFocusTransfer();
});
}
startConnectionToCurrentUser();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
index f57bfad..77a4b9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -18,7 +18,7 @@
import android.content.Context;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.ContextComponentHelper;
import dagger.Binds;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 346acf9..3e574e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -53,29 +53,30 @@
import androidx.annotation.NonNull;
-import com.android.systemui.R;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
import dagger.Lazy;
+@SysUISingleton
public class ScreenPinningRequest implements View.OnClickListener,
- NavigationModeController.ModeChangedListener {
+ NavigationModeController.ModeChangedListener, CoreStartable {
private static final String TAG = "ScreenPinningRequest";
private final Context mContext;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
-
+ private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -98,12 +99,12 @@
@Inject
public ScreenPinningRequest(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
+ Lazy<NavigationBarController> navigationBarControllerLazy,
BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker) {
mContext = context;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mNavigationBarControllerLazy = navigationBarControllerLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mWindowManager = (WindowManager)
@@ -113,6 +114,9 @@
mUserTracker = userTracker;
}
+ @Override
+ public void start() {}
+
public void clearPrompt() {
if (mRequestWindow != null) {
mWindowManager.removeView(mRequestWindow);
@@ -144,7 +148,8 @@
mNavBarMode = mode;
}
- public void onConfigurationChanged() {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
if (mRequestWindow != null) {
mRequestWindow.onConfigurationChanged();
}
@@ -282,15 +287,14 @@
.setVisibility(View.INVISIBLE);
}
- final Optional<CentralSurfaces> centralSurfacesOptional =
- mCentralSurfacesOptionalLazy.get();
- boolean recentsVisible =
- centralSurfacesOptional.map(CentralSurfaces::isOverviewEnabled).orElse(false);
+ int displayId = mContext.getDisplayId();
+ boolean overviewEnabled =
+ mNavigationBarControllerLazy.get().isOverviewEnabled(displayId);
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
int descriptionStringResId;
if (QuickStepContract.isGesturalMode(mNavBarMode)) {
descriptionStringResId = R.string.screen_pinning_description_gestural;
- } else if (recentsVisible) {
+ } else if (overviewEnabled) {
mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
@@ -307,7 +311,7 @@
}
NavigationBarView navigationBarView =
- centralSurfacesOptional.map(CentralSurfaces::getNavigationBarView).orElse(null);
+ mNavigationBarControllerLazy.get().getNavigationBarView(displayId);
if (navigationBarView != null) {
((ImageView) mLayout.findViewById(R.id.screen_pinning_back_icon))
.setImageDrawable(navigationBarView.getBackDrawable());
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index b36ec32..0f3acaf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -33,6 +33,7 @@
includes =
[
BouncerSceneModule::class,
+ CommunalSceneModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
@@ -65,6 +66,7 @@
sceneKeys =
listOf(
SceneKey.Gone,
+ SceneKey.Communal,
SceneKey.Lockscreen,
SceneKey.Bouncer,
SceneKey.Shade,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 291d273..f0650d3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -60,6 +60,8 @@
Flags.MIGRATE_LOCK_ICON,
Flags.MIGRATE_NSSL,
Flags.MIGRATE_KEYGUARD_STATUS_VIEW,
+ Flags.NOTIFICATION_SHELF_REFACTOR,
+ Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 9a30aa6..3927873 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -32,5 +32,16 @@
val fromScene: SceneKey,
val toScene: SceneKey,
val progress: Flow<Float>,
+
+ /**
+ * Whether the transition was originally triggered by user input rather than being
+ * programmatic. If this value is initially true, it will remain true until the transition
+ * fully completes, even if the user input that triggered the transition has ended. Any
+ * sub-transitions launched by this one will inherit this value. For example, if the user
+ * drags a pointer but does not exceed the threshold required to transition to another
+ * scene, this value will remain true after the pointer is no longer touching the screen and
+ * will be true in any transition created to animate back to the original position.
+ */
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 31597c1..4bc93a8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -16,9 +16,7 @@
package com.android.systemui.scene.shared.model
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
/**
* Defines interface for classes that can describe a "scene".
@@ -34,31 +32,26 @@
val key: SceneKey
/**
- * Returns a mapping between [UserAction] and flows that emit a [SceneModel].
+ * The mapping between [UserAction] and destination [SceneModel]s.
*
- * When the scene framework detects the user action, it starts a transition to the scene
- * described by the latest value in the flow that's mapped from that user action.
+ * When the scene framework detects a user action, if the current scene has a map entry for that
+ * user action, the framework starts a transition to the scene in the map.
*
- * Once the [Scene] becomes the current one, the scene framework will invoke this method and set
- * up collectors to watch for new values emitted to each of the flows. If a value is added to
- * the map at a given [UserAction], the framework will set up user input handling for that
- * [UserAction] and, if such a user action is detected, the framework will initiate a transition
- * to that [SceneModel].
+ * Once the [Scene] becomes the current one, the scene framework will read this property and set
+ * up a collector to watch for new mapping values. If every map entry provided by the scene, the
+ * framework will set up user input handling for its [UserAction] and, if such a user action is
+ * detected, initiate a transition to the specified [SceneModel].
*
- * Note that calling this method does _not_ mean that the given user action has occurred.
- * Instead, the method is called before any user action/gesture is detected so that the
- * framework can decide whether to set up gesture/input detectors/listeners for that type of
- * user action.
+ * Note that reading from this method does _not_ mean that any user action has occurred.
+ * Instead, the property is read before any user action/gesture is detected so that the
+ * framework can decide whether to set up gesture/input detectors/listeners in case user actions
+ * of the given types ever occur.
*
* Note that a missing value for a specific [UserAction] means that the user action of the given
* type is not currently active in the scene and should be ignored by the framework, while the
* current scene is this one.
- *
- * The API is designed such that it's possible to emit ever-changing values for each
- * [UserAction] to enable, disable, or change the destination scene of a given user action.
*/
- fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
- MutableStateFlow(emptyMap<UserAction, SceneModel>()).asStateFlow()
+ val destinationScenes: StateFlow<Map<UserAction, SceneModel>>
}
/** Enumerates all scene framework supported user actions. */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
index e7811e3..609d2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
@@ -16,7 +16,11 @@
package com.android.systemui.scene.shared.model
-/** Keys of all known scenes. */
+/**
+ * Keys of all known scenes.
+ *
+ * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
+ */
sealed class SceneKey(
private val loggingName: String,
) {
@@ -26,6 +30,9 @@
*/
object Bouncer : SceneKey("bouncer")
+ /** The communal scene shows the glanceable hub when device is locked and docked. */
+ object Communal : SceneKey("communal")
+
/**
* "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
* content from the scene framework.
@@ -35,14 +42,14 @@
/** The lockscreen is the scene that shows when the device is locked. */
object Lockscreen : SceneKey("lockscreen")
+ /** The quick settings scene shows the quick setting tiles. */
+ object QuickSettings : SceneKey("quick_settings")
+
/**
* The shade is the scene whose primary purpose is to show a scrollable list of notifications.
*/
object Shade : SceneKey("shade")
- /** The quick settings scene shows the quick setting tiles. */
- object QuickSettings : SceneKey("quick_settings")
-
override fun toString(): String {
return loggingName
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 3e76607..3fb886d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -27,7 +27,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.model.Scene
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index ef688a8..7cff7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,7 +25,7 @@
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.compose.ComposeFacade
/** A view that can serve as the root of the main SysUI window. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 8dd25bc..64006fe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -32,7 +32,7 @@
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
/** Base permission dialog for screen share and recording */
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index 8cbc4aab..47e28d8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.media.projection.MediaProjectionConfig
import android.os.Bundle
-import com.android.systemui.R
+import com.android.systemui.res.R
/** Dialog to select screen recording options */
class MediaProjectionPermissionDialog(
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index acb6d96..7a0c087 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -151,10 +151,9 @@
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(context, getHostUserHandle(), this,
- activityStarter, dialogLaunchAnimator, mUserContextProvider,
- onStartRecordingClicked)
- : new ScreenRecordDialog(context, this, activityStarter,
- mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
+ activityStarter, mUserContextProvider, onStartRecordingClicked)
+ : new ScreenRecordDialog(context, this, mUserContextProvider,
+ onStartRecordingClicked);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 2c4555a..7cdb655 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -41,7 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaProjectionCaptureTarget;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 2a21aaa..f0ce8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,6 @@
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.media.MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER;
import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
@@ -28,13 +27,11 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
import android.view.Gravity;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
@@ -44,14 +41,8 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.media.MediaProjectionAppSelectorActivity;
+import com.android.systemui.res.R;
import com.android.systemui.media.MediaProjectionCaptureTarget;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -71,23 +62,15 @@
private final UserContextProvider mUserContextProvider;
@Nullable
private final Runnable mOnStartRecordingClicked;
- private final ActivityStarter mActivityStarter;
- private final FeatureFlags mFlags;
- private final DialogLaunchAnimator mDialogLaunchAnimator;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
public ScreenRecordDialog(Context context, RecordingController controller,
- ActivityStarter activityStarter, UserContextProvider userContextProvider,
- FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator,
- @Nullable Runnable onStartRecordingClicked) {
+ UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
super(context);
mController = controller;
mUserContextProvider = userContextProvider;
- mActivityStarter = activityStarter;
- mDialogLaunchAnimator = dialogLaunchAnimator;
- mFlags = flags;
mOnStartRecordingClicked = onStartRecordingClicked;
}
@@ -120,31 +103,6 @@
dismiss();
});
- if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
- TextView appBtn = findViewById(R.id.button_app);
-
- appBtn.setVisibility(View.VISIBLE);
- appBtn.setOnClickListener(v -> {
- Intent intent = new Intent(getContext(), MediaProjectionAppSelectorActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- // We can't start activity for result here so we use result receiver to get
- // the selected target to capture
- intent.putExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
- new CaptureTargetResultReceiver());
-
- ActivityLaunchAnimator.Controller animationController =
- mDialogLaunchAnimator.createActivityLaunchController(appBtn);
-
- if (animationController == null) {
- dismiss();
- }
-
- mActivityStarter.startActivity(intent, /* dismissShade= */ true,
- animationController);
- });
- }
-
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 9c5da10..b5b7043 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -32,8 +32,7 @@
import android.widget.Spinner
import android.widget.Switch
import androidx.annotation.LayoutRes
-import com.android.systemui.R
-import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.res.R
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.media.MediaProjectionCaptureTarget
import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +44,6 @@
private val hostUserHandle: UserHandle,
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
- private val dialogLaunchAnimator: DialogLaunchAnimator,
private val userContextProvider: UserContextProvider,
private val onStartRecordingClicked: Runnable?
) :
@@ -85,12 +83,7 @@
MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
hostUserHandle
)
-
- val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
- if (animationController == null) {
- dismiss()
- }
- activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
+ activityStarter.startActivity(intent, /* dismissShade= */ true)
}
dismiss()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
index 9962c91..eb58b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
@@ -25,7 +25,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index ab2a8d9..8e9769ab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -23,7 +23,7 @@
import android.content.Context
import android.content.Intent
import android.net.Uri
-import com.android.systemui.R
+import com.android.systemui.res.R
object ActionIntentCreator {
/** @return a chooser intent to share the given URI. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 6f2256e..2f411ea 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -45,7 +45,7 @@
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
index bdbc470..b55bba9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -36,7 +36,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* ConstraintLayout that is draggable when touched in a specific region
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index bf536a4..9325e18 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -44,7 +44,7 @@
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.view.OneShotPreDrawListener;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
index 7873732..0c543cd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -34,7 +34,7 @@
import androidx.annotation.Nullable;
import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index b4ffabd..5cbea90 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -10,7 +10,7 @@
import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.Guideline
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
index 9e8ea3a..0588fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
@@ -29,7 +29,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index cfd9a49..31086d8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -47,7 +47,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 87f6e44..f08eb14 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -92,7 +92,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
index 253f07d..be39d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
@@ -24,7 +24,7 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import javax.inject.Inject
class ScreenshotDetectionController
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 35f2a39..4344fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -30,7 +30,7 @@
import android.view.WindowManager;
import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
index 522f729..070fb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.view.WindowManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Performs a number of miscellaneous, non-system-critical actions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 03e1e15..31c9284 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -86,7 +86,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.system.InputChannelCompat;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index c5bc2fb..03c3f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -150,16 +150,18 @@
}
/**
- * Returns a [RequestCallback] that calls [RequestCallback.onFinish] only when all callbacks for
- * id created have finished.
+ * Returns a [RequestCallback] that wraps [originalCallback].
*
- * If any callback created calls [reportError], then following [onFinish] are not considered.
+ * Each [RequestCallback] created with [createCallbackForId] is expected to be used with either
+ * [reportError] or [onFinish]. Once they are both called:
+ * - If any finished with an error, [reportError] of [originalCallback] is called
+ * - Otherwise, [onFinish] is called.
*/
private class MultiResultCallbackWrapper(
private val originalCallback: RequestCallback,
) {
private val idsPending = mutableSetOf<Int>()
- private var errorReported = false
+ private val idsWithErrors = mutableSetOf<Int>()
/**
* Creates a callback for [id].
@@ -172,23 +174,32 @@
idsPending += id
return object : RequestCallback {
override fun reportError() {
- Log.d(TAG, "ReportError id=$id")
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
- Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "reportError id=$id")
- originalCallback.reportError()
- errorReported = true
+ endTrace("reportError id=$id")
+ idsWithErrors += id
+ idsPending -= id
+ reportToOriginalIfNeeded()
}
override fun onFinish() {
- Log.d(TAG, "onFinish id=$id")
- if (errorReported) return
+ endTrace("onFinish id=$id")
idsPending -= id
- Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "onFinish id=$id")
- if (idsPending.isEmpty()) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
- originalCallback.onFinish()
- }
+ reportToOriginalIfNeeded()
}
+
+ private fun endTrace(reason: String) {
+ Log.d(TAG, "Finished waiting for id=$id. $reason")
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, reason)
+ }
+ }
+ }
+
+ private fun reportToOriginalIfNeeded() {
+ if (idsPending.isNotEmpty()) return
+ if (idsWithErrors.isEmpty()) {
+ originalCallback.onFinish()
+ } else {
+ originalCallback.reportError()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 1e8542f..0be2265 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -53,7 +53,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotRequest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
@@ -99,7 +99,11 @@
/** Informs about coarse grained state of the Controller. */
public interface RequestCallback {
- /** Respond to the current request indicating the screenshot request failed. */
+ /**
+ * Respond to the current request indicating the screenshot request failed.
+ * <p>
+ * After this, the service will be disconnected and all visible UI is removed.
+ */
void reportError();
/** The controller has completed handling this request UI has been removed */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
index 798c490..c801ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
@@ -28,7 +28,7 @@
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 2f96f6c..aa23d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -51,7 +51,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.screenshot.CropView;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
index dce8c81..06c0b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
@@ -37,7 +37,7 @@
import androidx.annotation.Nullable;
import com.android.internal.statusbar.IAppClipsService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.flags.FeatureFlags;
import com.android.wm.shell.bubbles.Bubbles;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index 6e5cef4..a31b301 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -48,7 +48,7 @@
import com.android.internal.infra.ServiceConnector;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IAppClipsService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
index 71c5fad..9d1278f 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
@@ -9,7 +9,7 @@
import android.view.WindowManager
import android.widget.ImageView
import com.android.internal.widget.DialogTitle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
class SensorUseDialog(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 93a3e90..bd592c9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/**
@@ -68,8 +67,8 @@
interface Callback {
/**
- * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
- * auto-decremented after the completion of this method.
+ * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
+ * called automatically after the completion of this method.
*/
fun onUserChanging(newUser: Int, userContext: Context) {}
@@ -78,12 +77,12 @@
* Override this method to run things while the screen is frozen for the user switch.
* Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
* screen further. Please be aware that code executed in this callback will lengthen the
- * user switch duration. When overriding this method, countDown() MUST be called on the
- * latch once execution is complete.
+ * user switch duration. When overriding this method, resultCallback#run() MUST be called
+ * once the execution is complete.
*/
- fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
+ fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
onUserChanging(newUser, userContext)
- latch.countDown()
+ resultCallback.run()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 5fb3c01..2f87301 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -33,11 +33,22 @@
import androidx.annotation.WorkerThread
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.util.Assert
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
import java.io.PrintWriter
import java.lang.ref.WeakReference
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
+import javax.inject.Provider
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -58,20 +69,26 @@
*/
open class UserTrackerImpl internal constructor(
private val context: Context,
+ private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
private val userManager: UserManager,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
- private val backgroundHandler: Handler
+ private val appScope: CoroutineScope,
+ private val backgroundContext: CoroutineDispatcher,
+ private val backgroundHandler: Handler,
) : UserTracker, Dumpable, BroadcastReceiver() {
companion object {
private const val TAG = "UserTrackerImpl"
+ private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec
}
var initialized = false
private set
private val mutex = Any()
+ private val isBackgroundUserSwitchEnabled: Boolean get() =
+ featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
override var userId: Int by SynchronizedDelegate(context.userId)
protected set
@@ -103,6 +120,10 @@
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
+ private var beforeUserSwitchingJob: Job? = null
+ private var userSwitchingJob: Job? = null
+ private var afterUserSwitchingJob: Job? = null
+
open fun initialize(startingUser: Int) {
if (initialized) {
return
@@ -119,7 +140,7 @@
addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
}
- context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+ context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
registerUserSwitchObserver()
@@ -162,16 +183,39 @@
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onBeforeUserSwitching(newUserId: Int) {
- handleBeforeUserSwitching(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ beforeUserSwitchingJob?.cancel()
+ beforeUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleBeforeUserSwitching(newUserId)
+ }
+ } else {
+ handleBeforeUserSwitching(newUserId)
+ }
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
- handleUserSwitching(newUserId)
- reply?.sendResult(null)
+ if (isBackgroundUserSwitchEnabled) {
+ userSwitchingJob?.cancel()
+ userSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchingCoroutines(newUserId) {
+ reply?.sendResult(null)
+ }
+ }
+ } else {
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
+ }
}
override fun onUserSwitchComplete(newUserId: Int) {
- handleUserSwitchComplete(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ afterUserSwitchingJob?.cancel()
+ afterUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchComplete(newUserId)
+ }
+ } else {
+ handleUserSwitchComplete(newUserId)
+ }
}
}, TAG)
}
@@ -195,7 +239,7 @@
val callback = it.callback.get()
if (callback != null) {
it.executor.execute {
- callback.onUserChanging(userId, userContext, latch)
+ callback.onUserChanging(userId, userContext) { latch.countDown() }
}
} else {
latch.countDown()
@@ -205,6 +249,28 @@
}
@WorkerThread
+ protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
+ coroutineScope {
+ Assert.isNotMainThread()
+ Log.i(TAG, "Switching to user $newUserId")
+
+ for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+ val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+ launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+ val mutex = Mutex(true)
+ val thresholdLogJob = launch(backgroundContext) {
+ delay(USER_CHANGE_THRESHOLD)
+ Log.e(TAG, "Failed to finish $callback in time")
+ }
+ callback.onUserChanging(userId, userContext) { mutex.unlock() }
+ mutex.lock()
+ thresholdLogJob.cancel()
+ }.join()
+ }
+ onDone()
+ }
+
+ @WorkerThread
protected open fun handleUserSwitchComplete(newUserId: Int) {
Assert.isNotMainThread()
Log.i(TAG, "Switched to user $newUserId")
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 38b1f14..6783afa 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.util.concurrency.DelayableExecutor;
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 d0d37c4..b715237 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -16,6 +16,8 @@
package com.android.systemui.settings.brightness;
+import static com.android.systemui.flags.Flags.HAPTIC_BRIGHTNESS_SLIDER;
+
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -28,14 +30,21 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
/**
* {@code ViewController} for a {@code BrightnessSliderView}
*
@@ -55,12 +64,21 @@
private final FalsingManager mFalsingManager;
private final UiEventLogger mUiEventLogger;
+ private final BrightnessSliderHapticPlugin mBrightnessSliderHapticPlugin;
+
private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
+ if (mBrightnessSliderHapticPlugin.getVelocityTracker() != null) {
+ mBrightnessSliderHapticPlugin.getVelocityTracker().clear();
+ }
+ } else if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ if (mBrightnessSliderHapticPlugin.getVelocityTracker() != null) {
+ mBrightnessSliderHapticPlugin.getVelocityTracker().addMovement(ev);
+ }
}
return false;
@@ -75,10 +93,12 @@
BrightnessSliderController(
BrightnessSliderView brightnessSliderView,
FalsingManager falsingManager,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ BrightnessSliderHapticPlugin brightnessSliderHapticPlugin) {
super(brightnessSliderView);
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
+ mBrightnessSliderHapticPlugin = brightnessSliderHapticPlugin;
}
/**
@@ -93,6 +113,7 @@
protected void onViewAttached() {
mView.setOnSeekBarChangeListener(mSeekListener);
mView.setOnInterceptListener(mOnInterceptListener);
+ mBrightnessSliderHapticPlugin.start();
}
@Override
@@ -100,6 +121,7 @@
mView.setOnSeekBarChangeListener(null);
mView.setOnDispatchTouchEventListener(null);
mView.setOnInterceptListener(null);
+ mBrightnessSliderHapticPlugin.stop();
}
@Override
@@ -204,6 +226,11 @@
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mListener != null) {
mListener.onChanged(mTracking, progress, false);
+ SeekableSliderEventProducer eventProducer =
+ mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
+ if (eventProducer != null) {
+ eventProducer.onProgressChanged(seekBar, progress, fromUser);
+ }
}
}
@@ -213,6 +240,11 @@
mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), false);
+ SeekableSliderEventProducer eventProducer =
+ mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
+ if (eventProducer != null) {
+ eventProducer.onStartTrackingTouch(seekBar);
+ }
}
if (mMirrorController != null) {
@@ -227,6 +259,11 @@
mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH);
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), true);
+ SeekableSliderEventProducer eventProducer =
+ mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
+ if (eventProducer != null) {
+ eventProducer.onStopTrackingTouch(seekBar);
+ }
}
if (mMirrorController != null) {
@@ -242,11 +279,26 @@
private final FalsingManager mFalsingManager;
private final UiEventLogger mUiEventLogger;
+ private final FeatureFlagsClassic mFeatureFlags;
+ private final VibratorHelper mVibratorHelper;
+ private final SystemClock mSystemClock;
+ private final CoroutineDispatcher mMainDispatcher;
@Inject
- public Factory(FalsingManager falsingManager, UiEventLogger uiEventLogger) {
+ public Factory(
+ FalsingManager falsingManager,
+ UiEventLogger uiEventLogger,
+ VibratorHelper vibratorHelper,
+ SystemClock clock,
+ FeatureFlagsClassic featureFlags,
+ @Main CoroutineDispatcher mainDispatcher
+ ) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
+ mFeatureFlags = featureFlags;
+ mVibratorHelper = vibratorHelper;
+ mSystemClock = clock;
+ mMainDispatcher = mainDispatcher;
}
/**
@@ -262,7 +314,17 @@
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
- return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger);
+ BrightnessSliderHapticPlugin plugin;
+ if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) {
+ plugin = new BrightnessSliderHapticPluginImpl(
+ mVibratorHelper,
+ mSystemClock,
+ mMainDispatcher
+ );
+ } else {
+ plugin = new BrightnessSliderHapticPlugin() {};
+ }
+ return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger, plugin);
}
/** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt
new file mode 100644
index 0000000..f775114
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.settings.brightness
+
+import android.view.VelocityTracker
+import com.android.systemui.haptics.slider.SeekableSliderEventProducer
+
+/** Plugin component for the System UI brightness slider to incorporate dynamic haptics */
+interface BrightnessSliderHapticPlugin {
+
+ /** Finger velocity tracker */
+ val velocityTracker: VelocityTracker?
+ get() = null
+
+ /** Producer of slider events from the underlying [android.widget.SeekBar] */
+ val seekableSliderEventProducer: SeekableSliderEventProducer?
+ get() = null
+
+ /**
+ * Start the plugin.
+ *
+ * This starts the tracking of slider states, events and triggering of haptic feedback.
+ */
+ fun start() {}
+
+ /**
+ * Stop the plugin
+ *
+ * This stops the tracking of slider states, events and triggers of haptic feedback.
+ */
+ fun stop() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt
new file mode 100644
index 0000000..32561f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.settings.brightness
+
+import android.view.VelocityTracker
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.haptics.slider.SeekableSliderEventProducer
+import com.android.systemui.haptics.slider.SeekableSliderTracker
+import com.android.systemui.haptics.slider.SliderHapticFeedbackProvider
+import com.android.systemui.haptics.slider.SliderTracker
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineDispatcher
+
+/**
+ * Implementation of the [BrightnessSliderHapticPlugin].
+ *
+ * For the specifics of the brightness slider in System UI, a [SeekableSliderEventProducer] is used
+ * as the producer of slider events, a [SliderHapticFeedbackProvider] is used as the listener of
+ * slider states to play haptic feedback depending on the state, and a [SeekableSliderTracker] is
+ * used as the state machine handler that tracks and manipulates the slider state.
+ */
+class BrightnessSliderHapticPluginImpl
+@JvmOverloads
+constructor(
+ vibratorHelper: VibratorHelper,
+ systemClock: SystemClock,
+ @Main mainDispatcher: CoroutineDispatcher,
+ override val velocityTracker: VelocityTracker = VelocityTracker.obtain(),
+ override val seekableSliderEventProducer: SeekableSliderEventProducer =
+ SeekableSliderEventProducer(),
+) : BrightnessSliderHapticPlugin {
+
+ private val sliderHapticFeedbackProvider: SliderHapticFeedbackProvider =
+ SliderHapticFeedbackProvider(vibratorHelper, velocityTracker, clock = systemClock)
+ private val sliderTracker: SliderTracker =
+ SeekableSliderTracker(
+ sliderHapticFeedbackProvider,
+ seekableSliderEventProducer,
+ mainDispatcher,
+ )
+
+ val isTracking: Boolean
+ get() = sliderTracker.isTracking
+
+ override fun start() {
+ if (!sliderTracker.isTracking) {
+ sliderTracker.startTracking()
+ }
+ }
+
+ override fun stop() {
+ sliderTracker.stopTracking()
+ }
+}
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 38cb441..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,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}.
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index e9a1dd7..a0dd924 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -25,8 +25,10 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.DisplayTrackerImpl;
import com.android.systemui.settings.UserContentResolverProvider;
@@ -42,6 +44,11 @@
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import javax.inject.Provider;
+
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
+
/**
* Dagger Module for classes found within the com.android.systemui.settings package.
*/
@@ -60,14 +67,17 @@
@Provides
static UserTracker provideUserTracker(
Context context,
+ Provider<FeatureFlagsClassic> featureFlagsProvider,
UserManager userManager,
IActivityManager iActivityManager,
DumpManager dumpManager,
+ @Application CoroutineScope appScope,
+ @Background CoroutineDispatcher backgroundDispatcher,
@Background Handler handler
) {
int startingUser = ActivityManager.getCurrentUser();
- UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
- dumpManager, handler);
+ UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
+ iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
tracker.initialize(startingUser);
return tracker;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
index a9b3d0a..b2e4736 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
@@ -18,7 +18,7 @@
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index 5850a84..14659e7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 15a0972..d5934e6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -114,7 +114,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
@@ -205,7 +205,6 @@
import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
@@ -381,7 +380,6 @@
private int mMaxAllowedKeyguardNotifications;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
- private KeyguardStatusBarView mKeyguardStatusBar;
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
private final LockIconViewController mLockIconViewController;
@@ -588,6 +586,8 @@
private boolean mGestureWaitForTouchSlop;
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
+ private boolean mUseExternalTouch = false;
+
/**
* Whether we're waking up and will play the delayed doze animation in
* {@link NotificationWakeUpCoordinator}. If so, we'll want to keep the clock centered until the
@@ -1035,7 +1035,6 @@
@VisibleForTesting
void onFinishInflate() {
loadDimens();
- mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
FrameLayout userAvatarContainer = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -1053,7 +1052,7 @@
mKeyguardStatusBarViewController =
mKeyguardStatusBarViewComponentFactory.build(
- mKeyguardStatusBar,
+ mView.findViewById(R.id.keyguard_header),
mShadeViewStateProvider)
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
@@ -1226,7 +1225,7 @@
private void updateViewControllers(
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
- updateStatusBarViewController();
+ updateStatusViewController();
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
// Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1257,7 +1256,7 @@
}
/** Updates the StatusBarViewController and updates any that depend on it. */
- public void updateStatusBarViewController() {
+ public void updateStatusViewController() {
// Re-associate the KeyguardStatusViewController
if (mKeyguardStatusViewController != null) {
mKeyguardStatusViewController.onDestroy();
@@ -1407,13 +1406,6 @@
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) && !mFeatureFlags.isEnabled(
- Flags.LAZY_INFLATE_KEYGUARD)) {
- attachSplitShadeMediaPlayerContainer(
- mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.status_view_media_container));
- }
-
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1683,6 +1675,10 @@
@ClockSize
private int computeDesiredClockSize() {
+ if (shouldForceSmallClock()) {
+ return SMALL;
+ }
+
if (mSplitShadeEnabled) {
return computeDesiredClockSizeForSplitShade();
}
@@ -1715,6 +1711,13 @@
return LARGE;
}
+ private boolean shouldForceSmallClock() {
+ return mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)
+ && !isOnAod()
+ // True on small landscape screens
+ && mResources.getBoolean(R.bool.force_small_clock_on_lockscreen);
+ }
+
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
ConstraintLayout layout;
@@ -1842,6 +1845,9 @@
/** Returns extra space available to show the shelf on lockscreen */
@VisibleForTesting
float getVerticalSpaceForLockscreenShelf() {
+ if (mSplitShadeEnabled) {
+ return 0f;
+ }
final float lockIconPadding = getLockIconPadding();
final float noShelfOverlapBottomPadding =
@@ -2246,7 +2252,10 @@
}
@Override
- public void startWaitingForExpandGesture() {
+ public void startInputFocusTransfer() {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
if (!isFullyCollapsed()) {
return;
}
@@ -2256,16 +2265,36 @@
}
@Override
- public void stopWaitingForExpandGesture(boolean cancel, final float velocity) {
+ public void cancelInputFocusTransfer() {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
if (mExpectingSynthesizedDown) {
mExpectingSynthesizedDown = false;
- if (cancel) {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- } else {
- // Window never will receive touch events that typically trigger haptic on open.
- maybeVibrateOnOpening(false /* openingWithTouch */);
- fling(velocity > 1f ? 1000f * velocity : 0 /* expand */);
- }
+ collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ onTrackingStopped(false);
+ }
+ }
+
+ /**
+ * There are two scenarios behind this function call. First, input focus transfer has
+ * successfully happened and this view already received synthetic DOWN event.
+ * (mExpectingSynthesizedDown == false). Do nothing.
+ *
+ * Second, before input focus transfer finished, user may have lifted finger in previous window
+ * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
+ * this case, we use the velocity to trigger fling event.
+ */
+ @Override
+ public void finishInputFocusTransfer(final float velocity) {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
+ if (mExpectingSynthesizedDown) {
+ mExpectingSynthesizedDown = false;
+ // Window never will receive touch events that typically trigger haptic on open.
+ maybeVibrateOnOpening(false /* openingWithTouch */);
+ fling(velocity > 1f ? 1000f * velocity : 0 /* expand */);
onTrackingStopped(false);
}
}
@@ -2604,6 +2633,7 @@
if (mPanelExpanded != isExpanded) {
mPanelExpanded = isExpanded;
updateSystemUiStateFlags();
+ mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(mPanelExpanded);
mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded) {
mQsController.closeQsCustomizer();
@@ -3730,8 +3760,6 @@
expand = true;
mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
forceCancel, expand);
- } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
- expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
@@ -4123,12 +4151,22 @@
/** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
boolean handleExternalInterceptTouch(MotionEvent event) {
- return mTouchHandler.onInterceptTouchEvent(event);
+ try {
+ mUseExternalTouch = true;
+ return mTouchHandler.onInterceptTouchEvent(event);
+ } finally {
+ mUseExternalTouch = false;
+ }
}
@Override
public boolean handleExternalTouch(MotionEvent event) {
- return mTouchHandler.onTouchEvent(event);
+ try {
+ mUseExternalTouch = true;
+ return mTouchHandler.onTouchEvent(event);
+ } finally {
+ mUseExternalTouch = false;
+ }
}
@Override
@@ -4715,9 +4753,20 @@
public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
private long mLastTouchDownTime = -1L;
- /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
+ /**
+ * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+ * originates with the parent window through {@link #handleExternalTouch}. This allows for
+ * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+ * handling.
+ *
+ * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+ */
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ return false;
+ }
+
mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
if (mQsController.disallowTouches()) {
mShadeLog.logMotionEvent(event,
@@ -4870,8 +4919,20 @@
return onTouchEvent(event);
}
+ /**
+ * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+ * originates with the parent window through {@link #handleExternalTouch}. This allows for
+ * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+ * handling.
+ *
+ * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+ */
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+ return false;
+ }
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (event.getDownTime() == mLastTouchDownTime) {
// An issue can occur when swiping down after unlock, where multiple down
@@ -4891,10 +4952,9 @@
return false;
}
- // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
+ // Do not allow panel expansion if bouncer is scrimmed,
// otherwise user would be able to pull down QS or expand the shade.
- if (mCentralSurfaces.isBouncerShowingScrimmed()
- || mCentralSurfaces.isBouncerShowingOverDream()) {
+ if (mCentralSurfaces.isBouncerShowingScrimmed()) {
mShadeLog.logMotionEvent(event,
"onTouch: ignore touch, bouncer scrimmed or showing over dream");
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 2dbcbc9..c803e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -48,7 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 880ba92..6cf4ff5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -36,7 +36,7 @@
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
@@ -267,6 +267,9 @@
}
mView.setLayoutInsetsController(mNotificationInsetsController);
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
+ boolean mUseDragDownHelperForTouch = false;
+ boolean mLastInterceptWasDragDownHelper = false;
+
@Override
public Boolean handleDispatchTouchEvent(MotionEvent ev) {
if (mStatusBarViewController == null) { // Fix for b/192490822
@@ -360,10 +363,8 @@
);
// In case we start outside of the view bounds (below the status bar), we need to
- // dispatch
- // the touch manually as the view system can't accommodate for touches outside of
- // the
- // regular view bounds.
+ // dispatch the touch manually as the view system can't accommodate for touches
+ // outside of the regular view bounds.
if (isDown && ev.getY() >= mView.getBottom()) {
mExpandingBelowNotch = true;
expandingBelowNotch = true;
@@ -405,6 +406,15 @@
@Override
public boolean shouldInterceptTouchEvent(MotionEvent ev) {
+ boolean intercepted = shouldInterceptTouchEventInternal(ev);
+ if (intercepted) {
+ mUseDragDownHelperForTouch = mLastInterceptWasDragDownHelper;
+ }
+ return intercepted;
+ }
+
+ private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
+ mLastInterceptWasDragDownHelper = false;
if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
&& !mDockManager.isDocked()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -431,19 +441,36 @@
}
if (mNotificationPanelViewController.isFullyExpanded()
- && mDragDownHelper.isDragDownEnabled()
&& !mService.isBouncerShowing()
&& !mStatusBarStateController.isDozing()) {
- boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (result) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
+ if (mDragDownHelper.isDragDownEnabled()) {
+ // This handles drag down over lockscreen
+ boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (result) {
+ mLastInterceptWasDragDownHelper = true;
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
+ }
+ } else if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
+ }
+ } else {
+ if (result) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
+ }
+ }
+ }
+ return result;
+ } else {
+ // This else handles interactions on the full shade while unlocked
+ if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
}
}
- return result;
- } else {
- return false;
}
+ return false;
}
@Override
@@ -451,7 +478,9 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+ }
cancellation.recycle();
}
@@ -461,18 +490,27 @@
if (mStatusBarStateController.isDozing()) {
handled = !mDozeServiceHost.isPulsing();
}
-
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
-
- if (mDragDownHelper.isDragDownEnabled()
- || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- return mDragDownHelper.onTouchEvent(ev) || handled;
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
+ // we still want to finish our drag down gesture when locking the screen
+ handled |= mDragDownHelper.onTouchEvent(ev) || handled;
+ }
+ if (!handled && mNotificationPanelViewController.handleExternalTouch(ev)) {
+ return true;
+ }
} else {
- return handled;
+ if (mDragDownHelper.isDragDownEnabled()
+ || mDragDownHelper.isDraggingDown()) {
+ // we still want to finish our drag down gesture when locking the screen
+ return mDragDownHelper.onTouchEvent(ev) || handled;
+ } else {
+ return handled;
+ }
}
+ return handled;
}
@Override
@@ -520,6 +558,20 @@
mDepthController.onPanelExpansionChanged(currentState);
}
+ private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
+ // to also ask NotificationPanelViewController directly, in order to process swipe up
+ // events originating from notifications
+ if (mNotificationPanelViewController.handleExternalInterceptTouch(ev)) {
+ mShadeLogger.d("NSWVC: NPVC intercepted");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public NotificationShadeWindowView getView() {
return mView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index bdf114e..cc46b23 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -26,7 +26,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -129,6 +129,9 @@
mView.setStackScroller(notificationStackScrollLayoutController.getView())
mView.setMigratingNSSL(featureFlags.isEnabled(Flags.MIGRATE_NSSL))
+ if (featureFlags.isEnabled(Flags.QS_CONTAINER_GRAPH_OPTIMIZER)){
+ mView.enableGraphOptimization()
+ }
}
public override fun onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index a4e439b..af44c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -16,6 +16,8 @@
package com.android.systemui.shade;
+import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAPH;
+
import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
@@ -24,14 +26,13 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowInsets;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -183,6 +184,10 @@
mIsMigratingNSSL = isMigrating;
}
+ void enableGraphOptimization() {
+ setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 3eec7fa0e..1fcb70c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -2,7 +2,7 @@
import android.content.Context
import android.view.DisplayCutout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index ff0d78f8..e8be40e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -65,11 +65,12 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -1776,7 +1777,9 @@
// Dragging down on the lockscreen statusbar should prohibit other interactions
// immediately, otherwise we'll wait on the touchslop. This is to allow
// dragging down to expanded quick settings directly on the lockscreen.
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ }
}
if (mExpansionAnimator != null) {
mInitialHeightOnTouch = mExpansionHeight;
@@ -1819,7 +1822,9 @@
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+ }
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
setTracking(true);
traceQsJank(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 9a356ad..25bd8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,7 +38,7 @@
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -484,11 +484,13 @@
if (largeScreenActive) {
logInstantEvent("Large screen constraints set")
header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
+ systemIconsHoverContainer.isClickable = true
systemIconsHoverContainer.setOnClickListener { shadeCollapseAction?.run() }
} else {
logInstantEvent("Small screen constraints set")
header.setTransition(HEADER_TRANSITION_ID)
systemIconsHoverContainer.setOnClickListener(null)
+ systemIconsHoverContainer.isClickable = false
}
header.jumpToState(header.startState)
updatePosition()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index b553f0f..6ee6cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -43,24 +43,6 @@
/** Cancels the views current animation. */
fun cancelAnimation()
- /** Input focus transfer is about to happen. */
- fun startWaitingForExpandGesture()
-
- /**
- * Called when this view is no longer waiting for input focus transfer.
- *
- * There are two scenarios behind this function call. First, input focus transfer has
- * successfully happened and this view already received synthetic DOWN event.
- * (mExpectingSynthesizedDown == false). Do nothing.
- *
- * Second, before input focus transfer finished, user may have lifted finger in previous window
- * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
- * this case, we use the velocity to trigger fling event.
- *
- * @param velocity unit is in px / millis
- */
- fun stopWaitingForExpandGesture(cancel: Boolean, velocity: Float)
-
/** Animates the view from its current alpha to zero then runs the runnable. */
fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index b3f6e16..fdc049c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -239,10 +239,35 @@
)
fun isFullyExpanded(): Boolean
- /** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
+ /**
+ * Sends an external (e.g. Status Bar) touch event to the Shade touch handler.
+ *
+ * This is different from [startInputFocusTransfer] as it doesn't rely on setting the launcher
+ * window slippery to allow the frameworks to route those events after passing the initial
+ * threshold.
+ */
fun handleExternalTouch(event: MotionEvent): Boolean
/**
+ * Triggered when an input focus transfer gesture has started.
+ *
+ * Used to dispatch initial touch events before crossing the threshold to pull down the
+ * notification shade. After that, since the launcher window is set to slippery, input
+ * frameworks take care of routing the events to the notification shade.
+ */
+ fun startInputFocusTransfer()
+
+ /** Triggered when the input focus transfer was cancelled. */
+ fun cancelInputFocusTransfer()
+
+ /**
+ * Triggered when the input focus transfer has finished successfully.
+ *
+ * @param velocity unit is in px / millis
+ */
+ fun finishInputFocusTransfer(velocity: Float)
+
+ /**
* Performs haptic feedback from a view with a haptic feedback constant.
*
* The implementation of this method should use the [android.view.View.performHapticFeedback]
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index b8a4101..2ed62dd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -85,6 +85,9 @@
override fun handleExternalTouch(event: MotionEvent): Boolean {
return false
}
+ override fun startInputFocusTransfer() {}
+ override fun cancelInputFocusTransfer() {}
+ override fun finishInputFocusTransfer(velocity: Float) {}
override fun performHapticFeedback(constant: Int) {}
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 3f7512a..38b358f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -22,7 +22,7 @@
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleView
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
index 8612cdf..6ca0ad4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
@@ -33,7 +33,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
import com.android.systemui.util.LargeScreenUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
index 68561d1..e84fb48 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
@@ -24,7 +24,7 @@
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Displays Carrier name and network status in the shade header
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 98d8a53..3349345 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -39,7 +39,7 @@
import com.android.keyguard.CarrierTextManager;
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 52a99af..024c8e3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -74,6 +74,21 @@
*/
@Deprecated("Use ShadeInteractor instead") val legacyQsTracking: StateFlow<Boolean>
+ /**
+ * NotificationPanelViewController.mPanelExpanded as a flow. This value is true whenever the
+ * expansion fraction is greater than zero or NPVC is about to accept an input transfer from the
+ * status bar, home screen, or trackpad.
+ */
+ @Deprecated("Use ShadeInteractor instead")
+ val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean>
+
+ /**
+ * Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input
+ * transfer from the status bar, home screen, or trackpad.
+ */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
+
/** Sets whether the user is moving Quick Settings with a pointer */
fun setLegacyQsTracking(legacyQsTracking: Boolean)
@@ -155,6 +170,18 @@
@Deprecated("Use ShadeInteractor instead")
override val legacyQsTracking: StateFlow<Boolean> = _legacyQsTracking.asStateFlow()
+ private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> =
+ _legacyExpandedOrAwaitingInputTransfer.asStateFlow()
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyExpandedOrAwaitingInputTransfer(
+ legacyExpandedOrAwaitingInputTransfer: Boolean
+ ) {
+ _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+ }
+
@Deprecated("Should only be called by NPVC and tests")
override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
_legacyQsTracking.value = legacyQsTracking
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 95a072c..251cc16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -35,15 +35,19 @@
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.isActive
/** Business logic for shade interactions. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -54,6 +58,7 @@
@Application scope: CoroutineScope,
disableFlagsRepository: DisableFlagsRepository,
sceneContainerFlags: SceneContainerFlags,
+ // TODO(b/300258424) convert to direct reference instead of provider
sceneInteractorProvider: Provider<SceneInteractor>,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
@@ -72,7 +77,7 @@
* Whether split shade, the combined notifications and quick settings shade used for large
* screens, is enabled.
*/
- val splitShadeEnabled: Flow<Boolean> =
+ val isSplitShadeEnabled: Flow<Boolean> =
sharedNotificationContainerInteractor.configurationBasedDimensions
.map { dimens -> dimens.useSplitShade }
.distinctUntilChanged()
@@ -87,7 +92,7 @@
keyguardRepository.statusBarState,
repository.legacyShadeExpansion,
repository.qsExpansion,
- splitShadeEnabled
+ isSplitShadeEnabled
) {
lockscreenShadeExpansion,
statusBarState,
@@ -119,18 +124,66 @@
repository.qsExpansion
}
- /** The amount [0-1] either QS or the shade has been opened */
+ /** The amount [0-1] either QS or the shade has been opened. */
val anyExpansion: StateFlow<Float> =
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
/** Whether either the shade or QS is expanding from a fully collapsed state. */
- val anyExpanding =
+ val isAnyExpanding: Flow<Boolean> =
anyExpansion
.pairwise(1f)
.map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f }
.distinctUntilChanged()
+ /**
+ * Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
+ * this time, this is not simply a matter of checking if either value in shadeExpansion and
+ * qsExpansion is greater than zero, because it includes the legacy concept of whether input
+ * transfer is about to occur. If the scene container flag is enabled, it just checks whether
+ * either expansion value is positive.
+ *
+ * TODO(b/300258424) remove all but the first sentence of this comment
+ */
+ val isAnyExpanded: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ anyExpansion.map { it > 0f }.distinctUntilChanged()
+ } else {
+ repository.legacyExpandedOrAwaitingInputTransfer
+ }
+
+ /**
+ * Whether the user is expanding or collapsing the shade with user input. This will be true even
+ * if the user's input gesture has ended but a transition they initiated is animating.
+ */
+ val isUserInteractingWithShade: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
+ } else {
+ userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion)
+ }
+
+ /**
+ * Whether the user is expanding or collapsing quick settings with user input. This will be true
+ * even if the user's input gesture has ended but a transition they initiated is still
+ * animating.
+ */
+ val isUserInteractingWithQs: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+ } else {
+ userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+ }
+
+ /**
+ * Whether the user is expanding or collapsing either the shade or quick settings with user
+ * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
+ * but a transition they initiated is still animating.
+ */
+ val isUserInteracting: Flow<Boolean> =
+ combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
+ .distinctUntilChanged()
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
@@ -169,4 +222,42 @@
}
}
.distinctUntilChanged()
+
+ fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.isUserInputDriven &&
+ (state.toScene == sceneKey || state.fromScene == sceneKey)
+ }
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Return a flow for whether a user is interacting with an expandable shade component using
+ * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
+ * [expansion.first] checks the current value of the flow.
+ */
+ private fun userInteractingFlow(
+ tracking: Flow<Boolean>,
+ expansion: StateFlow<Float>
+ ): Flow<Boolean> {
+ return flow {
+ // initial value is false
+ emit(false)
+ while (currentCoroutineContext().isActive) {
+ // wait for tracking to become true
+ tracking.first { it }
+ emit(true)
+ // wait for tracking to become false
+ tracking.first { !it }
+ // wait for expansion to complete in either direction
+ expansion.first { it <= 0f || it >= 1f }
+ // interaction complete
+ emit(false)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index c6c664d..51276c6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -22,7 +22,7 @@
import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.os.UserHandle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index ba92c45..f54d0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -25,7 +25,7 @@
import android.widget.ImageView;
import android.widget.RemoteViews.RemoteView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
@RemoteView
public class AnimatedImageView extends ImageView {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BatteryStatusChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BatteryStatusChip.kt
index 5209767..2281ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BatteryStatusChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BatteryStatusChip.kt
@@ -23,7 +23,7 @@
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.statusbar.events.BackgroundAnimatableView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index a0cbdf3..6bbd4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -29,7 +29,7 @@
import android.view.ViewRootImpl
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CastDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/CastDrawable.java
index 2f385d0..b1acc64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CastDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CastDrawable.java
@@ -24,7 +24,7 @@
import android.graphics.drawable.DrawableWrapper;
import android.util.AttributeSet;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6304c1e..670fb12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -375,15 +375,8 @@
/**
* @see IStatusBar#showTransient(int, int, boolean).
*/
- default void showTransient(int displayId, @InsetsType int types) { }
-
- /**
- * @see IStatusBar#showTransient(int, int, boolean).
- */
default void showTransient(int displayId, @InsetsType int types,
- boolean isGestureOnSystemBar) {
- showTransient(displayId, types);
- }
+ boolean isGestureOnSystemBar) {}
/**
* @see IStatusBar#abortTransient(int, int).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ConnectedDisplayChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ConnectedDisplayChip.kt
index 76636ab8..4c0f6f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ConnectedDisplayChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ConnectedDisplayChip.kt
@@ -19,7 +19,7 @@
import android.content.res.Configuration
import android.util.AttributeSet
import android.widget.FrameLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.events.BackgroundAnimatableView
/** Chip that appears in the status bar when an external display is connected. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index c1ebf12..d24f9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,7 +19,7 @@
import android.view.View;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 24c66eb..33c42e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -29,7 +29,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 4d933d9..0715dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -26,7 +26,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AlphaOptimizedLinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index a7ec02f..95768e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -74,7 +74,7 @@
import android.widget.ImageView;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutAppItemLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutAppItemLayout.java
index 5377dee..3e854a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutAppItemLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutAppItemLayout.java
@@ -23,7 +23,7 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Layout used for displaying keyboard shortcut items inside an alert dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index ec66e99..68c48b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -70,7 +70,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index ae3d41e..61eaff9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -67,7 +67,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 1a32d70..1198d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -39,7 +39,7 @@
import android.widget.ImageView;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.wm.shell.animation.FlingAnimationUtils;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index feb0258..2f1b589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -84,7 +84,6 @@
import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -103,6 +102,7 @@
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -309,7 +309,7 @@
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
- com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
+ com.android.systemui.res.R.array.config_face_help_msgs_when_fingerprint_enrolled);
for (int msgId : msgIds) {
mCoExFaceAcquisitionMsgIdsToShow.add(msgId);
}
@@ -704,7 +704,7 @@
.setTextColor(Utils.getColorAttr(
mContext, com.android.internal.R.attr.textColorOnAccent))
.setBackground(mContext.getDrawable(
- com.android.systemui.R.drawable.logout_button_background))
+ com.android.systemui.res.R.drawable.logout_button_background))
.setClickListener((view) -> {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) {
return;
@@ -1256,8 +1256,6 @@
if (biometricSourceType == FACE) {
mFaceAcquiredMessageDeferral.reset();
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
@@ -1278,8 +1276,6 @@
} else if (biometricSourceType == FINGERPRINT) {
onFingerprintAuthError(msgId, errString);
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
private void onFaceAuthError(int msgId, String errString) {
@@ -1351,8 +1347,6 @@
showActionToUnlock();
}
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 238317c..62c9980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -3,7 +3,7 @@
import android.content.Context
import android.util.IndentingPrintWriter
import android.util.MathUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.shade.ShadeViewController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
index 5f3d757..4d0552e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
@@ -21,7 +21,7 @@
import android.util.MathUtils
import androidx.annotation.FloatRange
import androidx.annotation.Px
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
index af4a1aa..77a0960 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
@@ -3,7 +3,7 @@
import android.content.Context
import android.util.IndentingPrintWriter
import android.util.MathUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 29ca0f4..143e620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -16,7 +16,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8089fd9..befc64d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -48,7 +48,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index c760227..09ad55e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -37,7 +37,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 2c0741e..3640ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -31,7 +31,7 @@
import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
index 006b219..2b7e9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
@@ -26,7 +26,7 @@
import android.graphics.drawable.DrawableWrapper;
import android.util.AttributeSet;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
index f1e51e2..603e863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
@@ -3,7 +3,7 @@
import android.content.Context
import android.content.res.Configuration
import android.util.MathUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index 3d574ca..e47c914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -7,7 +7,7 @@
import android.util.MathUtils
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index fbbee53..8baab25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -57,7 +57,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.util.drawable.DrawableSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 6dd24ea..e632214 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
@@ -31,13 +29,7 @@
import android.util.FloatProperty;
import android.util.Log;
import android.view.Choreographer;
-import android.view.InsetsFlags;
import android.view.View;
-import android.view.ViewDebug;
-import android.view.WindowInsets;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -51,7 +43,7 @@
import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -554,34 +546,6 @@
}
@Override
- public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
- @InsetsType int requestedVisibleTypes, String packageName) {
- boolean isFullscreen = (requestedVisibleTypes & WindowInsets.Type.statusBars()) == 0
- || (requestedVisibleTypes & WindowInsets.Type.navigationBars()) == 0;
- if (mIsFullscreen != isFullscreen) {
- mIsFullscreen = isFullscreen;
- synchronized (mListeners) {
- for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onFullscreenStateChanged(isFullscreen);
- }
- }
- }
-
- // TODO (b/190543382): Finish the logging logic.
- // This section can be removed if we don't need to print it on logcat.
- if (DEBUG_IMMERSIVE_APPS) {
- boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
- String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
- String requestedVisibleTypesString = WindowInsets.Type.toString(requestedVisibleTypes);
- if (requestedVisibleTypesString.isEmpty()) {
- requestedVisibleTypesString = "none";
- }
- Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
- + " requested visible types: " + requestedVisibleTypesString);
- }
- }
-
- @Override
public void setPulsing(boolean pulsing) {
if (mPulsing != pulsing) {
mPulsing = pulsing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 4043fce..aa32d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -20,9 +20,6 @@
import android.annotation.IntDef;
import android.view.View;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -151,12 +148,6 @@
boolean isKeyguardRequested();
/**
- * Set the system bar attributes
- */
- void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
- @InsetsType int requestedVisibleTypes, String packageName);
-
- /**
* Set pulsing
*/
void setPulsing(boolean visibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
index 8849d6e..10e7573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
@@ -13,7 +13,7 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 645595c..d089252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -118,6 +118,16 @@
}
/**
+ * @see Vibrator#vibrate(VibrationEffect, VibrationAttributes)
+ */
+ public void vibrate(@NonNull VibrationEffect effect, @NonNull VibrationAttributes attributes) {
+ if (!hasVibrator()) {
+ return;
+ }
+ mExecutor.execute(() -> mVibrator.vibrate(effect, attributes));
+ }
+
+ /**
* @see Vibrator#hasVibrator()
*/
public boolean hasVibrator() {
@@ -154,6 +164,17 @@
}
/**
+ * @see Vibrator#getPrimitiveDurations(int...)
+ */
+ public int[] getPrimitiveDurations(int... primitiveIds) {
+ if (!hasVibrator()) {
+ return new int[]{0};
+ } else {
+ return mVibrator.getPrimitiveDurations(primitiveIds);
+ }
+ }
+
+ /**
* Perform a vibration using a view and the one-way API with flags
* @see View#performHapticFeedback(int feedbackConstant, int flags)
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 67ab060..092e5b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -26,7 +26,7 @@
import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
index 196aad9..9734e26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.connectivity;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
class EthernetIcons {
static final int[][] ETHERNET_ICONS = {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index c523d22..d6df987 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -43,7 +43,7 @@
import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index f381b37..9db61c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -61,7 +61,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index f84b96c..08defcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -34,7 +34,7 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 80274bd..4056e7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.core
import android.app.Fragment
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 3dfe068..d058d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.dagger;
-import android.app.IActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -38,7 +37,6 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
@@ -57,12 +55,10 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -73,23 +69,15 @@
import com.android.systemui.statusbar.phone.StatusBarIconList;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.time.SystemClock;
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
/**
* This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
* this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -230,51 +218,6 @@
}
/**
- */
- @Provides
- @SysUISingleton
- static OngoingCallController provideOngoingCallController(
- Context context,
- CommonNotifCollection notifCollection,
- SystemClock systemClock,
- ActivityStarter activityStarter,
- @Main Executor mainExecutor,
- IActivityManager iActivityManager,
- OngoingCallLogger logger,
- DumpManager dumpManager,
- StatusBarWindowController statusBarWindowController,
- SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler,
- StatusBarStateController statusBarStateController,
- OngoingCallFlags ongoingCallFlags) {
-
- boolean ongoingCallInImmersiveEnabled = ongoingCallFlags.isInImmersiveEnabled();
- Optional<StatusBarWindowController> windowController =
- ongoingCallInImmersiveEnabled
- ? Optional.of(statusBarWindowController)
- : Optional.empty();
- Optional<SwipeStatusBarAwayGestureHandler> gestureHandler =
- ongoingCallInImmersiveEnabled
- ? Optional.of(swipeStatusBarAwayGestureHandler)
- : Optional.empty();
- OngoingCallController ongoingCallController =
- new OngoingCallController(
- context,
- notifCollection,
- ongoingCallFlags,
- systemClock,
- activityStarter,
- mainExecutor,
- iActivityManager,
- logger,
- dumpManager,
- windowController,
- gestureHandler,
- statusBarStateController);
- ongoingCallController.init();
- return ongoingCallController;
- }
-
- /**
* {@link NotificationPanelViewController} implements two interfaces:
* - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
* needing access to the shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
new file mode 100644
index 0000000..d1464ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.statusbar.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+/**
+ * A module for **only** classes related to the status bar **UI element**. This module specifically
+ * should **not** include:
+ * - Classes in the `statusbar` package that are unrelated to the status bar UI.
+ * - Status bar classes that are already provided by other modules
+ * ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule],
+ * [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
+ */
+@Module
+abstract class StatusBarModule {
+ @Binds
+ abstract fun bindStatusBarModeRepository(
+ impl: StatusBarModeRepositoryImpl
+ ): StatusBarModeRepository
+
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBarModeRepositoryImpl::class)
+ abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(OngoingCallController::class)
+ abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
new file mode 100644
index 0000000..9d73071
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A repository for the current mode of the status bar on the homescreen (translucent, transparent,
+ * opaque, lights out, hidden, etc.).
+ *
+ * Note: These status bar modes are status bar *window* states that are sent to us from
+ * WindowManager, not determined internally.
+ */
+interface StatusBarModeRepository {
+ /**
+ * True if the status bar window is showing transiently and will disappear soon, and false
+ * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
+ * persistently shown.)
+ *
+ * This behavior is controlled by WindowManager via
+ * [android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE], *not* calculated
+ * internally. SysUI merely obeys the behavior sent to us.
+ */
+ val isTransientShown: StateFlow<Boolean>
+
+ /**
+ * True the focused window is fullscreen (aka immersive) and false otherwise.
+ *
+ * Typically, the only time the status bar window is hidden is when the focused window is
+ * fullscreen.
+ */
+ val isInFullscreenMode: StateFlow<Boolean>
+
+ /**
+ * Requests for the status bar to be shown transiently.
+ *
+ * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+ * determined internally instead.
+ */
+ fun showTransient()
+
+ /**
+ * Requests for the status bar to be no longer showing transiently.
+ *
+ * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+ * determined internally instead.
+ */
+ fun clearTransient()
+}
+
+@SysUISingleton
+class StatusBarModeRepositoryImpl
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ @DisplayId thisDisplayId: Int,
+ private val commandQueue: CommandQueue,
+) : StatusBarModeRepository, CoreStartable {
+
+ private val commandQueueCallback =
+ object : CommandQueue.Callbacks {
+ override fun showTransient(
+ displayId: Int,
+ @WindowInsets.Type.InsetsType types: Int,
+ isGestureOnSystemBar: Boolean,
+ ) {
+ if (isTransientRelevant(displayId, types)) {
+ _isTransientShown.value = true
+ }
+ }
+
+ override fun abortTransient(displayId: Int, @WindowInsets.Type.InsetsType types: Int) {
+ if (isTransientRelevant(displayId, types)) {
+ _isTransientShown.value = false
+ }
+ }
+
+ private fun isTransientRelevant(
+ displayId: Int,
+ @WindowInsets.Type.InsetsType types: Int,
+ ): Boolean {
+ return displayId == thisDisplayId && (types and WindowInsets.Type.statusBars() != 0)
+ }
+
+ override fun onSystemBarAttributesChanged(
+ displayId: Int,
+ @WindowInsetsController.Appearance appearance: Int,
+ appearanceRegions: Array<AppearanceRegion>,
+ navbarColorManagedByIme: Boolean,
+ @WindowInsetsController.Behavior behavior: Int,
+ @WindowInsets.Type.InsetsType requestedVisibleTypes: Int,
+ packageName: String,
+ letterboxDetails: Array<LetterboxDetails>,
+ ) {
+ if (displayId != thisDisplayId) return
+ _originalStatusBarAttributes.value =
+ StatusBarAttributes(
+ requestedVisibleTypes,
+ )
+ }
+ }
+
+ override fun start() {
+ commandQueue.addCallback(commandQueueCallback)
+ }
+
+ private val _isTransientShown = MutableStateFlow(false)
+ override val isTransientShown: StateFlow<Boolean> = _isTransientShown.asStateFlow()
+
+ private val _originalStatusBarAttributes = MutableStateFlow<StatusBarAttributes?>(null)
+
+ override val isInFullscreenMode: StateFlow<Boolean> =
+ _originalStatusBarAttributes
+ .map { params ->
+ val requestedVisibleTypes = params?.requestedVisibleTypes ?: return@map false
+ // When the status bar is not requested visible, we assume we're in fullscreen mode.
+ requestedVisibleTypes and WindowInsets.Type.statusBars() == 0
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override fun showTransient() {
+ _isTransientShown.value = true
+ }
+
+ override fun clearTransient() {
+ _isTransientShown.value = false
+ }
+
+ /**
+ * Internal class keeping track of the raw status bar attributes received from the callback.
+ * Should never be exposed.
+ */
+ private data class StatusBarAttributes(
+ @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index d1e0a71..5af7cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -25,7 +25,7 @@
import android.view.View
import android.widget.FrameLayout
import com.android.internal.annotations.GuardedBy
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 1ad4620..dccc23f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -31,7 +31,7 @@
import androidx.core.animation.AnimatorSet
import androidx.core.animation.ValueAnimator
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index 2403920..7d866df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -20,7 +20,7 @@
import android.content.Context
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_PRIVACY
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index d667b91..0190d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -39,7 +39,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index fcff437..8cefc56d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -20,7 +20,7 @@
import android.util.Property;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.function.BiConsumer;
import java.util.function.Function;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index 420dd3f..24088d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -155,17 +155,17 @@
int feedbackStatus = getFeedbackStatus(entry);
switch (feedbackStatus) {
case STATUS_ALERTED:
- return com.android.systemui.R.string.notification_channel_summary_automatic_alerted;
+ return com.android.systemui.res.R.string.notification_channel_summary_automatic_alerted;
case STATUS_SILENCED:
- return com.android.systemui.R.string
+ return com.android.systemui.res.R.string
.notification_channel_summary_automatic_silenced;
case STATUS_PROMOTED:
- return com.android.systemui.R.string
+ return com.android.systemui.res.R.string
.notification_channel_summary_automatic_promoted;
case STATUS_DEMOTED:
- return com.android.systemui.R.string.notification_channel_summary_automatic_demoted;
+ return com.android.systemui.res.R.string.notification_channel_summary_automatic_demoted;
default:
- return com.android.systemui.R.string.notification_channel_summary_automatic;
+ return com.android.systemui.res.R.string.notification_channel_summary_automatic;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java
index 0c1891e1..f3067c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java
@@ -25,7 +25,7 @@
import android.view.ViewOutlineProvider;
import android.widget.LinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index b09b9f4..2ffbe4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -22,7 +22,7 @@
import android.widget.ImageView;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 11582d7..ad9e312 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -49,7 +49,7 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java
index 2ee2153..1f4f04c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java
@@ -20,7 +20,7 @@
import android.view.View;
import com.android.internal.widget.MessagingImageMessage;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.ViewTransformationHelper;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index a045698..167efc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -25,7 +25,7 @@
import android.widget.ImageView;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
index 43e45b1..552bfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
@@ -24,7 +24,7 @@
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class NotificationIconDozeHelper extends NotificationDozeHelper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index fb71210..eb1be67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -23,7 +23,7 @@
import android.widget.ImageView;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 8029f48..328a741 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,7 +3,7 @@
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.ViewRefactorFlag
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 90eb630..aa7a63e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -27,7 +27,7 @@
import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
index 1b43922..af4df32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
@@ -21,7 +21,7 @@
import android.animation.ValueAnimator
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.app.animation.Interpolators
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 93a34af..94cc7e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e206141..70ccc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -31,6 +31,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -38,6 +39,7 @@
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.icon.IconManager;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -151,6 +153,7 @@
component.getExpandableNotificationRowController();
rowController.init(entry);
entry.setRowController(rowController);
+ maybeSetBigPictureIconManager(row, component);
bindRow(entry, row);
updateRow(entry, row);
inflateContentViews(entry, params, row, callback);
@@ -165,6 +168,7 @@
return;
}
mLogger.logReleasingViews(entry);
+ cancelRunningJobs(entry.getRow());
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
@@ -172,6 +176,23 @@
mRowContentBindStage.requestRebind(entry, null);
}
+ private void maybeSetBigPictureIconManager(ExpandableNotificationRow row,
+ ExpandableNotificationRowComponent component) {
+ if (mFeatureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+ row.setBigPictureIconManager(component.getBigPictureIconManager());
+ }
+ }
+
+ private void cancelRunningJobs(ExpandableNotificationRow row) {
+ if (row == null) {
+ return;
+ }
+ BigPictureIconManager iconManager = row.getBigPictureIconManager();
+ if (iconManager != null) {
+ iconManager.cancelJobs();
+ }
+ }
+
/**
* Bind row to various controllers and managers. This is only called when the row is first
* created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
index 82c7aae..6b70a08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.provider
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
index 8de0381..70fabc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
@@ -19,7 +19,7 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.stack.MediaContainerView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 49990d9..c2b6f32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -21,7 +21,7 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.dagger.HeaderClickAction
import com.android.systemui.statusbar.notification.dagger.HeaderText
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
index 75801a8..ca43591 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
@@ -18,7 +18,7 @@
import android.annotation.StringRes
import android.provider.Settings
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 5664a2a..8ee0de6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -20,7 +20,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 98e167f..84678aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -27,7 +27,7 @@
import android.view.View
import android.widget.ImageView
import com.android.internal.statusbar.StatusBarIcon
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.InflationException
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
index 26dfe3e..a54687c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
@@ -30,7 +30,7 @@
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1b790fd..c61258b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -36,7 +36,7 @@
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -188,12 +188,6 @@
return super.onInterceptTouchEvent(ev);
}
- /**
- * Called by the TouchHandler when this view is tapped. This will be called for actual taps
- * only, i.e. taps that have been filtered by the FalsingManager.
- */
- public void onTap() {}
-
/** Sets the last action up time this view was touched. */
public void setLastActionUpTime(long eventTime) {
mLastActionUpTime = eventTime;
@@ -227,10 +221,6 @@
mBackgroundNormal.setState(getDrawableState());
}
- void setRippleAllowed(boolean allowed) {
- mBackgroundNormal.setPressedAllowed(allowed);
- }
-
private void updateOutlineAlpha() {
float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 028cd18..ded5ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -80,11 +80,7 @@
if (ev.getAction() == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click.
- boolean falseTap = mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
- if (!falseTap && v instanceof ActivatableNotificationView) {
- ((ActivatableNotificationView) v).onTap();
- }
- return falseTap;
+ return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
}
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
new file mode 100644
index 0000000..a5b32b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -0,0 +1,297 @@
+/*
+ * 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.statusbar.notification.row
+
+import android.annotation.WorkerThread
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Dumpable
+import android.util.Log
+import android.util.Size
+import com.android.internal.R
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.internal.widget.NotificationIconManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.PlaceHolder
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val TAG = "BigPicImageLoader"
+private const val DEBUG = false
+private const val FREE_IMAGE_DELAY_MS = 3000L
+
+/**
+ * A helper class for [com.android.internal.widget.BigPictureNotificationImageView] to lazy-load
+ * images from SysUI. It replaces the placeholder image with the fully loaded one, and vica versa.
+ *
+ * TODO(b/283082473) move the logs to a [com.android.systemui.log.LogBuffer]
+ */
+@SuppressWarnings("DumpableNotRegistered")
+class BigPictureIconManager
+@Inject
+constructor(
+ private val context: Context,
+ private val imageLoader: ImageLoader,
+ @Application private val scope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ @Background private val bgDispatcher: CoroutineDispatcher
+) : NotificationIconManager, Dumpable {
+
+ private var lastLoadingJob: Job? = null
+ private var drawableConsumer: NotificationDrawableConsumer? = null
+ private var displayedState: DrawableState = Empty(null)
+ private var viewShown = false
+
+ private var maxWidth = getMaxWidth()
+ private var maxHeight = getMaxHeight()
+
+ /**
+ * Called when the displayed state changes of the view.
+ *
+ * @param shown true if the view is shown, and the image needs to be displayed.
+ */
+ fun onViewShown(shown: Boolean) {
+ log("onViewShown:$shown")
+
+ if (this.viewShown != shown) {
+ this.viewShown = shown
+
+ val state = displayedState
+
+ this.lastLoadingJob?.cancel()
+ this.lastLoadingJob =
+ when {
+ skipLazyLoading(state.icon) -> null
+ state is Empty && shown -> state.icon?.let(::startLoadingJob)
+ state is PlaceHolder && shown -> startLoadingJob(state.icon)
+ state is FullImage && !shown ->
+ startFreeImageJob(state.icon, state.drawableSize)
+ else -> null
+ }
+ }
+ }
+
+ /**
+ * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+ */
+ fun updateMaxImageSizes() {
+ log("updateMaxImageSizes")
+ maxWidth = getMaxWidth()
+ maxHeight = getMaxHeight()
+ }
+
+ /** Cancels all currently running jobs. */
+ fun cancelJobs() {
+ lastLoadingJob?.cancel()
+ }
+
+ @WorkerThread
+ override fun updateIcon(drawableConsumer: NotificationDrawableConsumer, icon: Icon?): Runnable {
+ if (this.drawableConsumer != null && this.drawableConsumer != drawableConsumer) {
+ Log.wtf(TAG, "A consumer is already set for this iconManager.")
+ return Runnable {}
+ }
+
+ this.drawableConsumer = drawableConsumer
+ this.displayedState = Empty(icon)
+ this.lastLoadingJob?.cancel()
+
+ val drawable = loadImageOrPlaceHolderSync(icon)
+
+ log("icon updated")
+
+ return Runnable { drawableConsumer.setImageDrawable(drawable) }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>?) {
+ pw.println("BigPictureIconManager ${getDebugString()}")
+ }
+
+ @WorkerThread
+ private fun loadImageOrPlaceHolderSync(icon: Icon?): Drawable? {
+ icon ?: return null
+
+ if (viewShown || skipLazyLoading(icon)) {
+ return loadImageSync(icon)
+ }
+
+ return loadPlaceHolderSync(icon) ?: loadImageSync(icon)
+ }
+
+ @WorkerThread
+ private fun loadImageSync(icon: Icon): Drawable? {
+ return imageLoader.loadDrawableSync(icon, context, maxWidth, maxHeight)?.also { drawable ->
+ checkPlaceHolderSizeForDrawable(this.displayedState, drawable)
+ this.displayedState = FullImage(icon, drawable.intrinsicSize)
+ }
+ }
+
+ private fun checkPlaceHolderSizeForDrawable(
+ displayedState: DrawableState,
+ newDrawable: Drawable
+ ) {
+ if (displayedState is PlaceHolder) {
+ val (oldWidth, oldHeight) = displayedState.drawableSize
+ val (newWidth, newHeight) = newDrawable.intrinsicSize
+
+ if (oldWidth != newWidth || oldHeight != newHeight) {
+ Log.e(
+ TAG,
+ "Mismatch in dimensions, when replacing PlaceHolder " +
+ "$oldWidth X $oldHeight with Drawable $newWidth X $newHeight."
+ )
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun loadPlaceHolderSync(icon: Icon): Drawable? {
+ return imageLoader
+ .loadSizeSync(icon, context)
+ ?.resizeToMax(maxWidth, maxHeight) // match the dimensions of the fully loaded drawable
+ ?.let { size -> createPlaceHolder(size) }
+ ?.also { drawable -> this.displayedState = PlaceHolder(icon, drawable.intrinsicSize) }
+ }
+
+ private fun startLoadingJob(icon: Icon): Job =
+ scope.launch {
+ val drawable = withContext(bgDispatcher) { loadImageSync(icon) }
+ withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+ log("image loaded")
+ }
+
+ private fun startFreeImageJob(icon: Icon, drawableSize: Size): Job =
+ scope.launch {
+ delay(FREE_IMAGE_DELAY_MS)
+ val drawable = createPlaceHolder(drawableSize)
+ displayedState = PlaceHolder(icon, drawable.intrinsicSize)
+ withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+ log("placeholder loaded")
+ }
+
+ private fun createPlaceHolder(size: Size): Drawable {
+ return PlaceHolderDrawable(width = size.width, height = size.height)
+ }
+
+ private fun isLowRam(): Boolean {
+ return ActivityManager.isLowRamDeviceStatic()
+ }
+
+ private fun getMaxWidth() =
+ context.resources.getDimensionPixelSize(
+ if (isLowRam()) {
+ R.dimen.notification_big_picture_max_width_low_ram
+ } else {
+ R.dimen.notification_big_picture_max_width
+ }
+ )
+
+ private fun getMaxHeight() =
+ context.resources.getDimensionPixelSize(
+ if (isLowRam()) {
+ R.dimen.notification_big_picture_max_height_low_ram
+ } else {
+ R.dimen.notification_big_picture_max_height
+ }
+ )
+
+ /**
+ * We don't support lazy-loading or set placeholders for bitmap and data based icons, because
+ * they gonna stay in memory anyways.
+ */
+ private fun skipLazyLoading(icon: Icon?): Boolean =
+ when (icon?.type) {
+ Icon.TYPE_BITMAP,
+ Icon.TYPE_ADAPTIVE_BITMAP,
+ Icon.TYPE_DATA,
+ null -> true
+ else -> false
+ }
+
+ private fun log(msg: String) {
+ if (DEBUG) {
+ Log.d(TAG, "$msg state=${getDebugString()}")
+ }
+ }
+
+ private fun getDebugString() =
+ "{ state:$displayedState, hasConsumer:${drawableConsumer != null}, viewShown:$viewShown}"
+
+ private sealed class DrawableState(open val icon: Icon?) {
+ data class Empty(override val icon: Icon?) : DrawableState(icon)
+ data class PlaceHolder(override val icon: Icon, val drawableSize: Size) :
+ DrawableState(icon)
+ data class FullImage(override val icon: Icon, val drawableSize: Size) : DrawableState(icon)
+ }
+}
+
+/**
+ * @return an image size that conforms to the maxWidth / maxHeight parameters. It can be the same
+ * instance, if the provided size was already small enough.
+ */
+private fun Size.resizeToMax(maxWidth: Int, maxHeight: Int): Size {
+ if (width <= maxWidth && height <= maxHeight) {
+ return this
+ }
+
+ // Calculate the scale factor for both dimensions
+ val wScale =
+ if (maxWidth <= 0) {
+ 1.0f
+ } else {
+ maxWidth.toFloat() / width.toFloat()
+ }
+
+ val hScale =
+ if (maxHeight <= 0) {
+ 1.0f
+ } else {
+ maxHeight.toFloat() / height.toFloat()
+ }
+
+ // Scale down to the smaller scale factor
+ val scale = min(wScale, hScale)
+ if (scale < 1.0f) {
+ val targetWidth = (width * scale).toInt()
+ val targetHeight = (height * scale).toInt()
+
+ return Size(targetWidth, targetHeight)
+ }
+
+ return this
+}
+
+private val Drawable.intrinsicSize
+ get() = Size(/*width=*/ intrinsicWidth, /*height=*/ intrinsicHeight)
+
+private operator fun Size.component1() = width
+
+private operator fun Size.component2() = height
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
new file mode 100644
index 0000000..e226665
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.BigPictureNotificationImageView
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import javax.inject.Inject
+
+class BigPictureLayoutInflaterFactory @Inject constructor() : NotifRemoteViewsFactory {
+
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? {
+ // Currently the [BigPictureIconManager] only handles one view per notification.
+ // Exclude other layout types for now, to make sure that we set the same iconManager
+ // on only one [BigPictureNotificationImageView].
+ if (layoutType != FLAG_CONTENT_VIEW_EXPANDED) {
+ return null
+ }
+
+ return when (name) {
+ BigPictureNotificationImageView::class.java.name ->
+ BigPictureNotificationImageView(context, attrs).also { view ->
+ view.setIconManager(row.bigPictureIconManager)
+ }
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 0de3246..4114eb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -38,7 +38,7 @@
import android.view.WindowManager
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 9c4aa07..2cfd075 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -37,7 +37,7 @@
import android.widget.TextView
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.util.Assert
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index c02382d..fb8024c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -73,7 +73,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
@@ -376,6 +376,7 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private NotificationInlineImageResolver mImageResolver;
+ private BigPictureIconManager mBigPictureIconManager;
@Nullable
private OnExpansionChangedListener mExpansionChangedListener;
@Nullable
@@ -592,7 +593,6 @@
mPublicLayout.updateExpandButtons(true);
updateLimits();
updateShelfIconColor();
- updateRippleAllowed();
if (mUpdateSelfBackgroundOnUpdate) {
// Because this is triggered by UiMode change which we already propagated to children,
// we know that child rows will receive the same event, and will update their own
@@ -1355,6 +1355,9 @@
if (mImageResolver != null) {
mImageResolver.updateMaxImageSizes();
}
+ if (mBigPictureIconManager != null) {
+ mBigPictureIconManager.updateMaxImageSizes();
+ }
}
public void onUiModeChanged() {
@@ -1794,6 +1797,16 @@
return mImageResolver;
}
+ public BigPictureIconManager getBigPictureIconManager() {
+ return mBigPictureIconManager;
+ }
+
+ public void setBigPictureIconManager(
+ BigPictureIconManager bigPictureIconManager) {
+ mBigPictureIconManager = bigPictureIconManager;
+ }
+
+
/**
* Resets this view so it can be re-used for an updated notification.
*/
@@ -2556,31 +2569,8 @@
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
- updateRippleAllowed();
}
- private void updateRippleAllowed() {
- boolean allowed = isOnKeyguard()
- || mEntry.getSbn().getNotification().contentIntent == null;
- setRippleAllowed(allowed);
- }
-
- @Override
- public void onTap() {
- // This notification will expand and animates into the content activity, so we disable the
- // ripple. We will restore its value once the tap/click is actually performed.
- if (mEntry.getSbn().getNotification().contentIntent != null) {
- setRippleAllowed(false);
- }
- }
-
- @Override
- public boolean performClick() {
- // We force-disabled the ripple in onTap. When this method is called, the code drawing the
- // ripple will already have been called so we can restore its value now.
- updateRippleAllowed();
- return super.performClick();
- }
@Override
public int getHeightWithoutLockscreenConstraints() {
@@ -3687,6 +3677,9 @@
pw.println("no viewState!!!");
}
pw.println(getRoundableState().debugString());
+ if (mBigPictureIconManager != null) {
+ mBigPictureIconManager.dump(pw, args);
+ }
dumpBackgroundView(pw, args);
int transientViewCount = mChildrenContainer == null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index b950187..77f5717 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -48,7 +48,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index c8f13a6..2599231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,7 +28,7 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.statusbar.notification.RoundableState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index c4c116b..5aae488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -34,7 +34,7 @@
import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.RoundableState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 3f4fd50..42c80ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -43,7 +43,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 0989df6..26db5f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -34,7 +34,7 @@
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.DumpUtilsKt;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 99a24cb..43d99a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -27,7 +27,7 @@
import android.widget.TextView;
import com.android.internal.widget.ConversationLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 3e10f2a..ddd9bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -31,7 +31,7 @@
import android.widget.TextView;
import com.android.internal.widget.ConversationLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* A class managing hybrid groups that include {@link HybridNotificationView} and the notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index ce6dd89..892a635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -30,7 +30,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.keyguard.AlphaOptimizedLinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 647505c..b95e053 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -32,9 +32,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.util.ArrayUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -59,7 +58,6 @@
private int mExpandAnimationWidth = -1;
private int mExpandAnimationHeight = -1;
private int mDrawableAlpha = 255;
- private boolean mIsPressedAllowed;
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -153,9 +151,17 @@
public void setTint(int tintColor) {
if (tintColor != 0) {
- mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
+ ColorStateList stateList = new ColorStateList(new int[][]{
+ new int[]{com.android.internal.R.attr.state_pressed},
+ new int[]{com.android.internal.R.attr.state_hovered},
+ new int[]{}},
+
+ new int[]{0, 0, tintColor}
+ );
+ mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ mBackground.setTintList(stateList);
} else {
- mBackground.clearColorFilter();
+ mBackground.setTintList(null);
}
mTintColor = tintColor;
invalidate();
@@ -210,10 +216,6 @@
public void setState(int[] drawableState) {
if (mBackground != null && mBackground.isStateful()) {
- if (!mIsPressedAllowed) {
- drawableState = ArrayUtils.removeInt(drawableState,
- com.android.internal.R.attr.state_pressed);
- }
mBackground.setState(drawableState);
}
}
@@ -267,9 +269,12 @@
return;
}
if (mBackground instanceof LayerDrawable) {
- GradientDrawable gradientDrawable =
- (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
- gradientDrawable.setCornerRadii(mCornerRadii);
+ int numberOfLayers = ((LayerDrawable) mBackground).getNumberOfLayers();
+ for (int i = 0; i < numberOfLayers; i++) {
+ GradientDrawable gradientDrawable =
+ (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(i);
+ gradientDrawable.setCornerRadii(mCornerRadii);
+ }
}
}
@@ -295,10 +300,6 @@
invalidate();
}
- public void setPressedAllowed(boolean allowed) {
- mIsPressedAllowed = allowed;
- }
-
@Override
public void dump(PrintWriter pw, @NonNull String[] args) {
pw.println("mDontModifyCorners: " + mDontModifyCorners);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index f805183..f186e66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -41,7 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 065828b..a27a305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -45,7 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 7134f15..62b268b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -65,7 +65,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 047db20..b86d1d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -35,7 +35,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 44ead26..1dd3739 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -46,7 +46,7 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 99a7755..d8f31d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -60,7 +60,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 5a129fc..a317f95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -41,7 +41,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 867e08b..0239afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -60,12 +60,16 @@
@Named(NOTIF_REMOTEVIEWS_FACTORIES)
static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories(
FeatureFlags featureFlags,
- PrecomputedTextViewFactory precomputedTextViewFactory
+ PrecomputedTextViewFactory precomputedTextViewFactory,
+ BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory
) {
final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>();
if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) {
replacementFactories.add(precomputedTextViewFactory);
}
+ if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+ replacementFactories.add(bigPictureLayoutInflaterFactory);
+ }
return replacementFactories;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index d5d7f75..3443da1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -49,7 +49,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index ac97e77..06c3b79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -34,7 +34,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
new file mode 100644
index 0000000..40aa27f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
@@ -0,0 +1,33 @@
+package com.android.systemui.statusbar.notification.row
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+
+class PlaceHolderDrawable(private val width: Int, private val height: Int) : Drawable() {
+
+ companion object {
+ fun createFrom(other: Drawable): PlaceHolderDrawable {
+ return PlaceHolderDrawable(other.intrinsicWidth, other.intrinsicHeight)
+ }
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return width
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ return height
+ }
+
+ override fun draw(canvas: Canvas) {}
+ override fun setAlpha(alpha: Int) {}
+ override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+ @Suppress("DeprecatedCallableAddReplaceWith")
+ @Deprecated("Deprecated in android.graphics.drawable.Drawable")
+ override fun getOpacity(): Int {
+ return PixelFormat.TRANSPARENT
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index 96547db..0c4ffe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.util.AttributeSet
import android.view.View
-import android.widget.TextView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.ImageFloatingTextView
import com.android.internal.widget.MessagingLayout
@@ -36,8 +35,6 @@
attrs: AttributeSet
): View? {
return when (name) {
- TextView::class.java.name,
- TextView::class.java.simpleName -> PrecomputedTextView(context, attrs)
ImageFloatingTextView::class.java.name ->
PrecomputedImageFloatingTextView(context, attrs)
MessagingLayout::class.java.name ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 6feffe6..8e974c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -23,7 +23,7 @@
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 3588621..0a6a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -68,6 +69,12 @@
ExpandableNotificationRowController getExpandableNotificationRowController();
/**
+ * Creates a BigPictureIconManager.
+ */
+ @NotificationRowScope
+ BigPictureIconManager getBigPictureIconManager();
+
+ /**
* Dagger Module that extracts interesting properties from an ExpandableNotificationRow.
*/
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index 54af107..9a54de1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -82,11 +82,7 @@
}
if (ev.action == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click.
- val falseTap: Boolean = falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
- if (!falseTap && v is ActivatableNotificationView) {
- v.onTap()
- }
- return falseTap
+ return falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
}
return result
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 175ba15..acd6cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -28,6 +28,7 @@
import com.android.internal.R;
import com.android.internal.widget.BigPictureNotificationImageView;
import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -66,6 +67,17 @@
}
}
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+
+ BigPictureIconManager imageManager = mRow.getBigPictureIconManager();
+ if (imageManager != null) {
+ // TODO(b/283082473) call it a bit earlier for true, as soon as the row starts to expand
+ imageManager.onViewShown(visible);
+ }
+ }
+
/**
* Starts or stops the animations in any drawables contained in this BigPicture Notification.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index bb43b95..2d94694 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -20,7 +20,7 @@
import android.view.View
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.CallLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 10753f2..b4411f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -25,7 +25,7 @@
import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingImageMessage
import com.android.internal.widget.MessagingLinearLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index fdff12d..45c35d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -21,7 +21,7 @@
import android.view.View;
import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 87205e2..bd7f766 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -37,7 +37,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
index 4592fde..ba1deed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
@@ -26,7 +26,7 @@
import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingLayout;
import com.android.internal.widget.MessagingLinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 199692b..875a409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -39,7 +39,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.NotificationActionListLayout;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 38a368e..cb671447 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -26,7 +26,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index d73bbeb..33473a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -23,7 +23,7 @@
import android.view.View;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index a8d8a8e..5d46f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -22,7 +22,7 @@
import android.graphics.Path
import android.graphics.RectF
import android.util.AttributeSet
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 626f851..a929e4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -42,7 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.NotificationExpandButton;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
import com.android.systemui.statusbar.notification.FeedbackIcon;
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 e8521d1..28f0a0c 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
@@ -86,7 +86,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
@@ -569,6 +569,7 @@
private boolean mShouldUseSplitNotificationShade;
private boolean mHasFilteredOutSeenNotifications;
@Nullable private SplitShadeStateController mSplitShadeStateController = null;
+ private boolean mIsSmallLandscapeLockscreenEnabled = false;
/** Pass splitShadeStateController to view and update split shade */
public void passSplitShadeStateController(SplitShadeStateController splitShadeStateController) {
@@ -628,6 +629,8 @@
super(context, attrs, 0, 0);
Resources res = getResources();
FeatureFlags featureFlags = Dependency.get(FeatureFlags.class);
+ mIsSmallLandscapeLockscreenEnabled = featureFlags.isEnabled(
+ Flags.LOCKSCREEN_ENABLE_LANDSCAPE);
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
@@ -1054,6 +1057,17 @@
mOverflingDistance = configuration.getScaledOverflingDistance();
Resources res = context.getResources();
+ boolean useSmallLandscapeLockscreenResources = mIsSmallLandscapeLockscreenEnabled
+ && res.getBoolean(R.bool.is_small_screen_landscape);
+ // TODO (b/293252410) remove condition here when flag is launched
+ // Instead update the config_skinnyNotifsInLandscape to be false whenever
+ // is_small_screen_landscape is true. Then, only use the config_skinnyNotifsInLandscape.
+ if (useSmallLandscapeLockscreenResources) {
+ mSkinnyNotifsInLandscape = false;
+ } else {
+ mSkinnyNotifsInLandscape = res.getBoolean(
+ R.bool.config_skinnyNotifsInLandscape);
+ }
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mStackScrollAlgorithm.initView(context);
mAmbientState.reload(context);
@@ -1065,7 +1079,6 @@
mBottomPadding = res.getDimensionPixelSize(R.dimen.notification_panel_padding_bottom);
mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
- mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape);
mSidePaddings = mMinimumPaddings; // Updated in onMeasure by updateSidePadding()
mMinInteractionHeight = res.getDimensionPixelSize(
R.dimen.notification_min_interaction_height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 24104d2..30708b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -20,7 +20,7 @@
import android.util.Log
import android.view.View.GONE
import androidx.annotation.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.pipeline.MediaDataManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index baf09c7..722c28c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -28,7 +28,7 @@
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 90e10a7..f2d5394 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -27,7 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 2742a23..4e81d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -24,7 +24,7 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardSliceView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index f4605be..f097d06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -28,7 +28,7 @@
import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
import com.android.systemui.statusbar.notification.PropertyAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 51b6c75..57cea5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,7 +18,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.SplitShadeStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 688843d..b4f578f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import android.view.View
+import androidx.constraintlayout.core.widgets.Optimizer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -27,7 +28,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Container for the stack scroller, so that the bounds can be externally specified, such as from
@@ -45,6 +46,7 @@
private val baseConstraintSet = ConstraintSet()
init {
+ optimizationLevel = optimizationLevel or Optimizer.OPTIMIZATION_GRAPH
baseConstraintSet.apply {
create(R.id.nssl_guideline, VERTICAL)
setGuidelinePercent(R.id.nssl_guideline, 0.5f)
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 45ae4e0..dee3973 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
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.view.LayoutInflater
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationShelf
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index baeae79..07d3a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -30,7 +30,7 @@
import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter
import com.android.systemui.animation.DelegateLaunchAnimatorController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 5eafa9e..697d297 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -28,7 +28,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 4590712..f62a79f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -35,7 +35,7 @@
import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 3b9afa1..023efdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,7 +47,7 @@
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.logging.BiometricUnlockLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index cfa481e..3e1f09f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
-import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.window.RemoteTransition;
@@ -197,21 +196,6 @@
void onKeyguardViewManagerStatesUpdated();
- /**
- * Used to dispatch initial touch events before crossing the threshold to pull down the
- * notification shade. After that, since the launcher window is set to slippery, input
- * frameworks take care of routing the events to the notification shade.
- */
- void onInputFocusTransfer(boolean start, boolean cancel, float velocity);
-
- /**
- * Dispatches status bar motion event to the notification shade. This is different from
- * {@link #onInputFocusTransfer(boolean, boolean, float)} as it doesn't rely on setting the
- * launcher window slippery to allow the frameworks to route those events after passing the
- * initial threshold.
- */
- default void onStatusBarTrackpadEvent(MotionEvent event) {}
-
/** */
boolean getCommandQueuePanelsEnabled();
@@ -263,14 +247,10 @@
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
- boolean isOverviewEnabled();
-
void setBouncerShowing(boolean bouncerShowing);
boolean isScreenFullyOff();
- void showScreenPinningRequest(int taskId, boolean allowCancel);
-
@Nullable
Intent getEmergencyActionIntent();
@@ -301,8 +281,6 @@
boolean isBouncerShowingScrimmed();
- boolean isBouncerShowingOverDream();
-
void updateNotificationPanelTouchState();
int getRotation();
@@ -310,10 +288,6 @@
@VisibleForTesting
void setBarStateForTest(int state);
- void showTransientUnchecked();
-
- void clearTransient();
-
void acquireGestureWakeLock(long time);
boolean setAppearance(int appearance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 28bb581..485ab32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -36,7 +36,6 @@
import android.util.Slog;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -48,7 +47,7 @@
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.dagger.SysUISingleton;
@@ -59,6 +58,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.QuickSettingsController;
@@ -84,6 +84,7 @@
public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
private final CentralSurfaces mCentralSurfaces;
private final Context mContext;
+ private final ScreenPinningRequest mScreenPinningRequest;
private final com.android.systemui.shade.ShadeController mShadeController;
private final CommandQueue mCommandQueue;
private final ShadeViewController mShadeViewController;
@@ -126,6 +127,7 @@
QuickSettingsController quickSettingsController,
Context context,
@Main Resources resources,
+ ScreenPinningRequest screenPinningRequest,
ShadeController shadeController,
CommandQueue commandQueue,
ShadeViewController shadeViewController,
@@ -155,6 +157,7 @@
mCentralSurfaces = centralSurfaces;
mQsController = quickSettingsController;
mContext = context;
+ mScreenPinningRequest = screenPinningRequest;
mShadeController = shadeController;
mCommandQueue = commandQueue;
mShadeViewController = shadeViewController;
@@ -188,17 +191,6 @@
}
@Override
- public void abortTransient(int displayId, @InsetsType int types) {
- if (displayId != mDisplayId) {
- return;
- }
- if ((types & WindowInsets.Type.statusBars()) == 0) {
- return;
- }
- mCentralSurfaces.clearTransient();
- }
-
- @Override
public void addQsTile(ComponentName tile) {
mQSHost.addTile(tile);
}
@@ -483,17 +475,6 @@
}
@Override
- public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
- if (displayId != mDisplayId) {
- return;
- }
- if ((types & WindowInsets.Type.statusBars()) == 0) {
- return;
- }
- mCentralSurfaces.showTransientUnchecked();
- }
-
- @Override
public void toggleKeyboardShortcutsMenu(int deviceId) {
mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
}
@@ -516,7 +497,7 @@
return;
}
// Show screen pinning request, since this comes from an app, show 'no thanks', button.
- mCentralSurfaces.showScreenPinningRequest(taskId, true);
+ mScreenPinningRequest.showPrompt(taskId, true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index ff380db..3cb5e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -39,7 +39,6 @@
override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
override fun isLaunchingActivityOverLockscreen() = false
override fun onKeyguardViewManagerStatesUpdated() {}
- override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
override fun getCommandQueuePanelsEnabled() = false
override fun showWirelessChargingAnimation(batteryLevel: Int) {}
override fun checkBarModes() {}
@@ -68,10 +67,8 @@
cancelAction: Runnable?,
) {}
override fun getNavigationBarView(): NavigationBarView? = null
- override fun isOverviewEnabled() = false
override fun setBouncerShowing(bouncerShowing: Boolean) {}
override fun isScreenFullyOff() = false
- override fun showScreenPinningRequest(taskId: Int, allowCancel: Boolean) {}
override fun getEmergencyActionIntent(): Intent? = null
override fun isCameraAllowedByAdmin() = false
override fun isGoingToSleep() = false
@@ -84,12 +81,9 @@
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
- override fun isBouncerShowingOverDream() = false
override fun updateNotificationPanelTouchState() {}
override fun getRotation() = 0
override fun setBarStateForTest(state: Int) {}
- override fun showTransientUnchecked() {}
- override fun clearTransient() {}
override fun acquireGestureWakeLock(time: Long) {}
override fun setAppearance(appearance: Int) = false
override fun getBarMode() = 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 490c469..6f69ea81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -87,7 +87,6 @@
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
-import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowInsets;
@@ -122,7 +121,7 @@
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
@@ -168,7 +167,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
@@ -208,6 +206,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -239,6 +238,7 @@
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -388,7 +388,6 @@
*/
protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
- private boolean mBouncerShowingOverDream;
private final PhoneStatusBarPolicy mIconPolicy;
@@ -412,6 +411,7 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarInitializer mStatusBarInitializer;
private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarModeRepository mStatusBarModeRepository;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
@@ -498,8 +498,6 @@
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
- private boolean mTransientShown;
-
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
@@ -507,8 +505,6 @@
? new GestureRecorder("/sdcard/statusbar_gestures.dat")
: null;
- private final ScreenPinningRequest mScreenPinningRequest;
-
private final MetricsLogger mMetricsLogger;
// ensure quick settings is disabled until the current user makes it through the setup wizard
@@ -562,11 +558,10 @@
private final ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
private final BackActionInteractor mBackActionInteractor;
+ private final JavaAdapter mJavaAdapter;
private final Executor mUiBgExecutor;
protected boolean mDozing;
- private boolean mIsFullscreen;
-
boolean mCloseQsBeforeScreenOff;
private final NotificationMediaManager mMediaManager;
@@ -637,6 +632,7 @@
StatusBarInitializer statusBarInitializer,
StatusBarWindowController statusBarWindowController,
StatusBarWindowStateController statusBarWindowStateController,
+ StatusBarModeRepository statusBarModeRepository,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -655,6 +651,7 @@
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
+ JavaAdapter javaAdapter,
@UiBackground Executor uiBgExecutor,
ShadeSurface shadeSurface,
NotificationMediaManager notificationMediaManager,
@@ -692,7 +689,6 @@
DozeServiceHost dozeServiceHost,
BackActionInteractor backActionInteractor,
PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
@@ -745,6 +741,7 @@
mAutoHideController = autoHideController;
mStatusBarInitializer = statusBarInitializer;
mStatusBarWindowController = statusBarWindowController;
+ mStatusBarModeRepository = statusBarModeRepository;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPulseExpansionHandler = pulseExpansionHandler;
mWakeUpCoordinator = notificationWakeUpCoordinator;
@@ -764,6 +761,7 @@
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
mShadeLogger = shadeLogger;
+ mJavaAdapter = javaAdapter;
mUiBgExecutor = uiBgExecutor;
mShadeSurface = shadeSurface;
mMediaManager = notificationMediaManager;
@@ -799,7 +797,6 @@
mDozeParameters = dozeParameters;
mScrimController = scrimController;
mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
- mScreenPinningRequest = screenPinningRequest;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
mAuthRippleController = authRippleController;
@@ -940,7 +937,7 @@
setUpPresenter();
if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
- showTransientUnchecked();
+ mStatusBarModeRepository.showTransient();
}
mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
@@ -1190,6 +1187,11 @@
mWallpaperController.setRootView(getNotificationShadeWindowView());
mDemoModeController.addCallback(mDemoModeCallback);
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged);
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.isInFullscreenMode(),
+ this::onStatusBarFullscreenChanged);
mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
mCommandQueue.addCallback(mCommandQueueCallbacks);
@@ -1255,7 +1257,7 @@
@Override
public void hide() {
- clearTransient();
+ mStatusBarModeRepository.clearTransient();
}
});
@@ -1543,7 +1545,9 @@
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- mShadeController.onStatusBarTouch(event);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mShadeController.onStatusBarTouch(event);
+ }
return getNotificationShadeWindowView().onTouchEvent(event);
};
}
@@ -1687,27 +1691,6 @@
mHeadsUpManager.releaseAllImmediately();
}
- /**
- * Called when another window is about to transfer it's input focus.
- */
- @Override
- public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
- if (!mCommandQueue.panelsEnabled()) {
- return;
- }
-
- if (start) {
- mShadeSurface.startWaitingForExpandGesture();
- } else {
- mShadeSurface.stopWaitingForExpandGesture(cancel, velocity);
- }
- }
-
- @Override
- public void onStatusBarTrackpadEvent(MotionEvent event) {
- mShadeSurface.handleExternalTouch(event);
- }
-
private void onExpandedInvisible() {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarterLazy.get().isCollapsingToShowActivityOverLockscreen()) {
@@ -1722,25 +1705,19 @@
return mCommandQueue.panelsEnabled();
}
- @Override
- public void showTransientUnchecked() {
- if (!mTransientShown) {
- mTransientShown = true;
+ private void onTransientShownChanged(boolean transientShown) {
+ if (transientShown) {
mNoAnimationOnNextBarModeChange = true;
- maybeUpdateBarMode();
}
+ maybeUpdateBarMode();
}
- @Override
- public void clearTransient() {
- if (mTransientShown) {
- mTransientShown = false;
- maybeUpdateBarMode();
- }
+ private void onStatusBarFullscreenChanged(boolean isWindowShown) {
+ maybeUpdateBarMode();
}
private void maybeUpdateBarMode() {
- final int barMode = barMode(mTransientShown, mAppearance);
+ final int barMode = barMode(isTransientShown(), mAppearance);
if (updateBarMode(barMode)) {
mLightBarController.onStatusBarModeChanged(barMode);
updateBubblesVisibility();
@@ -1758,8 +1735,10 @@
}
private @TransitionMode int barMode(boolean isTransient, int appearance) {
+ boolean isFullscreen = mStatusBarModeRepository.isInFullscreenMode().getValue();
final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
+ if (mOngoingCallController.hasOngoingCall() && isFullscreen) {
+ // Force show the status bar if there's an ongoing call.
return MODE_SEMI_TRANSPARENT;
} else if (isTransient) {
return MODE_SEMI_TRANSPARENT;
@@ -2070,7 +2049,7 @@
}
mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
+ com.android.systemui.res.R.dimen.physical_power_button_center_screen_location_y));
}
private void logStateToEventlog() {
@@ -2180,10 +2159,16 @@
// * When phone is unlocked: we still don't want to execute hiding of the keyguard
// as the animation could prepare 'fake AOD' interface (without actually
// transitioning to keyguard state) and this might reset the view states
+ // Log for b/290627350
+ Log.d(TAG, "!shouldBeKeyguard mStatusBarStateController.isKeyguardRequested() "
+ + mStatusBarStateController.isKeyguardRequested() + " keyguardForDozing "
+ + keyguardForDozing + " wakeAndUnlocking " + wakeAndUnlocking
+ + " isWakingAndOccluded " + isWakingAndOccluded);
if (!mScreenOffAnimationController.isKeyguardHideDelayed()
// If we're animating occluded, there's an activity launching over the keyguard
// UI. Wait to hide it until after the animation concludes.
&& !mKeyguardViewMediator.isOccludeAnimationPlaying()) {
+ Log.d(TAG, "hideKeyguardImpl " + forceStateChange);
return hideKeyguardImpl(forceStateChange);
}
}
@@ -2460,10 +2445,7 @@
*/
@Override
public boolean shouldKeyguardHideImmediately() {
- final boolean isScrimmedBouncer =
- mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
- final boolean isBouncerOverDream = isBouncerShowingOverDream();
- return (isScrimmedBouncer || isBouncerOverDream);
+ return mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
}
private void showBouncerOrLockScreenIfKeyguard() {
@@ -2541,11 +2523,6 @@
return mNavigationBarController.getNavigationBarView(mDisplayId);
}
- @Override
- public boolean isOverviewEnabled() {
- return mNavigationBarController.isOverviewEnabled(mDisplayId);
- }
-
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -2815,11 +2792,6 @@
return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
}
- @Override
- public void showScreenPinningRequest(int taskId, boolean allowCancel) {
- mScreenPinningRequest.showPrompt(taskId, allowCancel);
- }
-
@Nullable
@Override
public Intent getEmergencyActionIntent() {
@@ -3160,15 +3132,10 @@
return isBouncerShowing() && mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming();
}
- @Override
- public boolean isBouncerShowingOverDream() {
- return mBouncerShowingOverDream;
- }
-
// End Extra BaseStatusBarMethods.
boolean isTransientShown() {
- return mTransientShown;
+ return mStatusBarModeRepository.isTransientShown().getValue();
}
private void updateLightRevealScrimVisibility() {
@@ -3251,8 +3218,6 @@
if (DEBUG) {
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
-
- mScreenPinningRequest.onConfigurationChanged();
}
@Override
@@ -3364,12 +3329,6 @@
updateReportRejectedTouchVisibility();
Trace.endSection();
}
-
- @Override
- public void onFullscreenStateChanged(boolean isFullscreen) {
- mIsFullscreen = isFullscreen;
- maybeUpdateBarMode();
- }
};
private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
@@ -3470,6 +3429,7 @@
@Override
public void setIsLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen;
+ mKeyguardViewMediator.launchingActivityOverLockscreen(mIsLaunchingActivityOverLockscreen);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 374543d..de9854a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -28,7 +28,7 @@
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index ae715b3..7730f7d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -37,7 +37,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
index efc2891..92b0f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
@@ -19,7 +19,7 @@
import android.util.AttributeSet;
import android.widget.ImageView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class ExpandableIndicator extends ImageView {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index a1f12b8..270c40e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,7 +26,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4f0cd80..a5ea142 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -30,7 +30,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 47ab316..25e634a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -24,7 +24,7 @@
import android.widget.FrameLayout
import androidx.annotation.StringRes
import com.android.keyguard.LockIconViewController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
@@ -62,6 +62,7 @@
private var keyguardIndicationArea: View? = null
private var binding: KeyguardBottomAreaViewBinder.Binding? = null
private var lockIconViewController: LockIconViewController? = null
+ private var isLockscreenLandscapeEnabled: Boolean = false
/** Initializes the view. */
@Deprecated("Deprecated as part of b/278057014")
@@ -115,15 +116,26 @@
}
}
+ fun setIsLockscreenLandscapeEnabled(isLockscreenLandscapeEnabled: Boolean) {
+ this.isLockscreenLandscapeEnabled = isLockscreenLandscapeEnabled
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
ambientIndicationArea = findViewById(R.id.ambient_indication_container)
+ keyguardIndicationArea = findViewById(R.id.keyguard_indication_area)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
binding?.onConfigurationChanged()
+ if (isLockscreenLandscapeEnabled) {
+ updateIndicationAreaBottomMargin()
+ }
+ }
+
+ private fun updateIndicationAreaBottomMargin() {
keyguardIndicationArea?.let {
val params = it.layoutParams as FrameLayout.LayoutParams
params.bottomMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
index 3942dae..0bad47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -16,11 +16,20 @@
package com.android.systemui.statusbar.phone
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.util.ViewController
import javax.inject.Inject
-class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) :
+class KeyguardBottomAreaViewController
+ @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) :
ViewController<KeyguardBottomAreaView> (view) {
+
+ init {
+ view.setIsLockscreenLandscapeEnabled(
+ featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE))
+ }
+
override fun onViewAttached() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 924aac4..430b0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,7 +22,7 @@
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index fc3c85a..fb5a530 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -26,7 +26,7 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 29a249f..c6690c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -32,7 +32,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.keyguard.KeyguardIndication;
/**
@@ -296,6 +296,6 @@
private int getYTranslationPixels() {
return mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.keyguard_indication_y_translation);
+ com.android.systemui.res.R.dimen.keyguard_indication_y_translation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 914e0c0..7efa705 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -43,7 +43,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index be336e5..fc9ecb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -42,9 +42,11 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
@@ -69,6 +71,8 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -76,8 +80,6 @@
import javax.inject.Inject;
-import kotlin.Unit;
-
/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
private static final String TAG = "KeyguardStatusBarViewController";
@@ -111,6 +113,7 @@
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
+ private final FeatureFlags mFeatureFlags;
private final UserManager mUserManager;
private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
private final SecureSettings mSecureSettings;
@@ -283,6 +286,7 @@
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+ FeatureFlags featureFlags,
UserManager userManager,
StatusBarUserChipViewModel userChipViewModel,
SecureSettings secureSettings,
@@ -308,6 +312,7 @@
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
+ mFeatureFlags = featureFlags;
mUserManager = userManager;
mStatusBarUserChipViewModel = userChipViewModel;
mSecureSettings = secureSettings;
@@ -367,7 +372,19 @@
mView.findViewById(R.id.statusIcons), StatusBarLocation.KEYGUARD);
mTintedIconManager.setBlockList(getBlockedIcons());
mStatusBarIconController.addIconGroup(mTintedIconManager);
+ } else {
+ // In the old implementation, the keyguard status bar view is never detached and
+ // re-attached, so only calling #addIconGroup when the IconManager is first created was
+ // safe and correct.
+ // In the new scene framework implementation, the keyguard status bar view *is* detached
+ // whenever the shade is opened on top of lockscreen, and then re-attached when the
+ // shade is closed. So, we need to re-add the IconManager each time we're re-attached to
+ // get icon updates.
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+ mStatusBarIconController.addIconGroup(mTintedIconManager);
+ }
}
+
mSystemIconsContainer = mView.findViewById(R.id.system_icons);
StatusOverlayHoverListener hoverListener = mStatusOverlayHoverListenerFactory
.createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index d22ed38..4bfce4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -34,7 +34,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 46a2457..572b7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -33,7 +33,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 365fbac..ec7767b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -24,7 +24,7 @@
import android.widget.Button;
import android.widget.FrameLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Container for image of the multi user switcher (tappable).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 3770c1d..7cbaf63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -39,7 +39,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 79151fd..54d81b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -43,7 +43,7 @@
import androidx.lifecycle.Observer;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index cc38405..ae3f923 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -22,7 +22,7 @@
import android.content.res.Resources;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public final class PhoneStatusBarTransitions extends BarTransitions {
private static final float ICON_ALPHA_WHEN_NOT_OPAQUE = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1966033..b53939e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -36,7 +36,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index fc5f915..e1096e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -24,7 +24,7 @@
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.scene.shared.flag.SceneContainerFlags
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 40432ee..5b55264 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -51,7 +51,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index ad18170..6a24174 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -26,7 +26,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.policy.SystemBarUtils
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.CallbackController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 4878d45..ffeb1a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -31,7 +31,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e337215..8d86d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -22,7 +22,6 @@
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -472,17 +471,11 @@
}
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ // Show the keyguard views whenever we've told WM that the lockscreen is visible.
mShadeViewController.postToView(() ->
collectFlow(
getViewRootImpl().getView(),
- combineFlows(
- mKeyguardTransitionInteractor.getFinishedKeyguardState(),
- mWmLockscreenVisibilityInteractor.get()
- .getUsingKeyguardGoingAwayAnimation(),
- (finishedState, animating) ->
- KeyguardInteractor.Companion.isKeyguardVisibleInState(
- finishedState)
- || animating),
+ mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
this::consumeShowStatusBarKeyguardView));
}
}
@@ -635,8 +628,12 @@
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
- mCentralSurfaces.hideKeyguard();
- mPrimaryBouncerInteractor.show(true);
+ if (!primaryBouncerIsShowing()) {
+ mCentralSurfaces.hideKeyguard();
+ mPrimaryBouncerInteractor.show(true);
+ } else {
+ Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
+ }
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 53662f4..fb7f9d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -31,7 +31,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 344e56c..30a445f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -23,7 +23,7 @@
import android.util.Log;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 5773612..40bd8d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -31,7 +31,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index d83664f..7910e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -33,7 +33,7 @@
import android.view.View;
import com.android.keyguard.AlphaOptimizedLinearLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
index 881741a..640ec28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
@@ -27,7 +27,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.DarkIconDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index c0269b8..829577b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -26,7 +26,6 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
import java.io.PrintWriter
import javax.inject.Inject
@@ -43,7 +42,6 @@
internal constructor(
private val centralSurfaces: CentralSurfaces,
private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
- private val statusBarStateController: SysuiStatusBarStateController,
private val lightBarController: LightBarController,
dumpManager: DumpManager,
) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
@@ -101,8 +99,6 @@
appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
centralSurfaces.updateBubblesVisibility()
- statusBarStateController.setSystemBarAttributes(
- appearance, behavior, requestedVisibleTypes, packageName)
}
private fun modifyAppearanceIfNeeded(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 69510ce..9d627af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -42,7 +42,7 @@
import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
index 52e0e8a..a033e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -28,7 +28,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.wm.shell.animation.Interpolators;
/**
@@ -83,7 +83,7 @@
public void animateOut() {
long fadeOutDuration = 167L; // From KeyguardIndicationTextView#getFadeOutDuration
int yTranslation = mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.keyguard_indication_y_translation);
+ com.android.systemui.res.R.dimen.keyguard_indication_y_translation);
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 1b93c16..c6d2eca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -25,7 +25,7 @@
import android.view.View;
import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* A view that displays a user image cropped to a circle with an optional frame.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 66f0f59..babd435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -38,7 +38,7 @@
import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt
index b845bad..48a9e0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt
@@ -37,7 +37,7 @@
): List<String> {
// Load the default blocklist from res
val blocklist = res.getStringArray(
- com.android.systemui.R.array.config_collapsed_statusbar_icon_blocklist).toList()
+ com.android.systemui.res.R.array.config_collapsed_statusbar_icon_blocklist).toList()
val vibrateIconSlot: String = res.getString(R.string.status_bar_volume)
val showVibrateIcon = settings.getIntForUser(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index 5903fa3..e73063b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -21,7 +21,7 @@
import androidx.core.animation.ValueAnimator
import android.content.res.Resources
import android.view.View
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_IN
import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index cd6ccb5..07c23d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,7 +19,7 @@
import android.view.View;
import android.view.ViewStub;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index b3af91d..4b1e7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -27,14 +27,16 @@
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -42,8 +44,9 @@
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.io.PrintWriter
-import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -52,19 +55,19 @@
*/
@SysUISingleton
class OngoingCallController @Inject constructor(
+ @Application private val scope: CoroutineScope,
private val context: Context,
private val notifCollection: CommonNotifCollection,
- private val ongoingCallFlags: OngoingCallFlags,
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
private val logger: OngoingCallLogger,
private val dumpManager: DumpManager,
- private val statusBarWindowController: Optional<StatusBarWindowController>,
- private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>,
- private val statusBarStateController: StatusBarStateController
-) : CallbackController<OngoingCallListener>, Dumpable {
+ private val statusBarWindowController: StatusBarWindowController,
+ private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
+ private val statusBarModeRepository: StatusBarModeRepository,
+) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
private var isFullscreen: Boolean = false
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
@@ -120,11 +123,15 @@
}
}
- fun init() {
+ override fun start() {
dumpManager.registerDumpable(this)
- if (ongoingCallFlags.isStatusBarChipEnabled()) {
- notifCollection.addCollectionListener(notifListener)
- statusBarStateController.addCallback(statusBarStateListener)
+ notifCollection.addCollectionListener(notifListener)
+ scope.launch {
+ statusBarModeRepository.isInFullscreenMode.collect {
+ isFullscreen = it
+ updateChipClickListener()
+ updateGestureListening()
+ }
}
}
@@ -138,7 +145,7 @@
this.chipView = chipView
val backgroundView: OngoingCallBackgroundContainer? =
chipView.findViewById(R.id.ongoing_call_chip_background)
- backgroundView?.maxHeightFetcher = { statusBarWindowController.get().statusBarHeight }
+ backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
if (hasOngoingCall()) {
updateChip()
}
@@ -197,9 +204,7 @@
uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
- statusBarWindowController.ifPresent {
- it.setOngoingProcessRequiresStatusBarVisible(true)
- }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
}
updateGestureListening()
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
@@ -217,23 +222,19 @@
private fun updateChipClickListener() {
if (callNotificationInfo == null) { return }
- if (isFullscreen && !ongoingCallFlags.isInImmersiveChipTapEnabled()) {
- chipView?.setOnClickListener(null)
- } else {
- val currentChipView = chipView
- val backgroundView =
- currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
- val intent = callNotificationInfo?.intent
- if (currentChipView != null && backgroundView != null && intent != null) {
- currentChipView.setOnClickListener {
- logger.logChipClicked()
- activityStarter.postStartActivityDismissingKeyguard(
- intent,
- ActivityLaunchAnimator.Controller.fromView(
- backgroundView,
- InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
- )
- }
+ val currentChipView = chipView
+ val backgroundView =
+ currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
+ val intent = callNotificationInfo?.intent
+ if (currentChipView != null && backgroundView != null && intent != null) {
+ currentChipView.setOnClickListener {
+ logger.logChipClicked()
+ activityStarter.postStartActivityDismissingKeyguard(
+ intent,
+ ActivityLaunchAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ )
}
}
}
@@ -247,10 +248,10 @@
if (callNotificationInfo == null ||
callNotificationInfo?.statusBarSwipedAway == true ||
!isFullscreen) {
- swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
} else {
- swipeStatusBarAwayGestureHandler.ifPresent {
- it.addOnGestureDetectedCallback(TAG) { _ -> onSwipeAwayGestureDetected() }
+ swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+ onSwipeAwayGestureDetected()
}
}
}
@@ -258,8 +259,8 @@
private fun removeChip() {
callNotificationInfo = null
tearDownChipView()
- statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
- swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
uidObserver.unregister()
}
@@ -283,20 +284,8 @@
private fun onSwipeAwayGestureDetected() {
if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
- statusBarWindowController.ifPresent {
- it.setOngoingProcessRequiresStatusBarVisible(false)
- }
- swipeStatusBarAwayGestureHandler.ifPresent {
- it.removeOnGestureDetectedCallback(TAG)
- }
- }
-
- private val statusBarStateListener = object : StatusBarStateController.StateListener {
- override fun onFullscreenStateChanged(isFullscreen: Boolean) {
- this@OngoingCallController.isFullscreen = isFullscreen
- updateChipClickListener()
- updateGestureListening()
- }
+ statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
private data class CallNotificationInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
deleted file mode 100644
index fcfcb8f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.ongoingcall
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import javax.inject.Inject
-
-@SysUISingleton
-class OngoingCallFlags @Inject constructor(private val featureFlags: FeatureFlags) {
-
- fun isStatusBarChipEnabled(): Boolean =
- featureFlags.isEnabled(Flags.ONGOING_CALL_STATUS_BAR_CHIP)
-
- fun isInImmersiveEnabled(): Boolean = isStatusBarChipEnabled()
- && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE)
-
- fun isInImmersiveChipTapEnabled(): Boolean = isInImmersiveEnabled()
- && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP)
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
index 1259477..387b970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
@@ -20,7 +20,7 @@
import android.util.AttributeSet
import android.widget.ImageView
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableLinearLayout
class StatusBarUserSwitcherContainer(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
index 3709e4c..b0b6b32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.ethernet.domain
import com.android.settingslib.AccessibilityContentDescriptions
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 67b04db..74a849a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -30,7 +30,7 @@
import com.android.internal.telephony.PhoneConstants
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 7ec8e12..f0470ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -28,7 +28,7 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settingslib.graph.SignalDrawable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
index f407127..fbd074d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
@@ -20,7 +20,7 @@
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index 68d02de..900a920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
index 0fe5329..9ea167f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.telephony.TelephonyManager
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index 7076f34..4227f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -31,7 +31,7 @@
import androidx.annotation.ArrayRes
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
index 327dd8d..1886590 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
@@ -20,6 +20,8 @@
import android.graphics.drawable.Drawable
import android.service.quicksettings.Tile
import com.android.settingslib.graph.SignalDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.plugins.qs.QSTile
@@ -31,17 +33,18 @@
val secondaryLabel: Text?
val iconId: Int?
val icon: QSTile.Icon?
+ val stateDescription: ContentDescription?
+ val contentDescription: ContentDescription?
- fun applyTo(state: QSTile.SignalState, context: Context) {
+ fun applyTo(state: QSTile.BooleanState, context: Context) {
if (secondaryLabel != null) {
state.secondaryLabel = secondaryLabel.loadText(context)
} else {
state.secondaryLabel = secondaryTitle
}
- // inout indicators are unused
- state.activityIn = false
- state.activityOut = false
+ state.stateDescription = stateDescription.loadContentDescription(context)
+ state.contentDescription = contentDescription.loadContentDescription(context)
// To support both SignalDrawable and other icons, give priority to icons over IDs
if (icon != null) {
@@ -63,6 +66,8 @@
override val secondaryLabel: Text? = null,
override val iconId: Int? = null,
override val icon: QSTile.Icon? = null,
+ override val stateDescription: ContentDescription? = null,
+ override val contentDescription: ContentDescription? = null,
) : InternetTileModel
data class Inactive(
@@ -70,6 +75,8 @@
override val secondaryLabel: Text? = null,
override val iconId: Int? = null,
override val icon: QSTile.Icon? = null,
+ override val stateDescription: ContentDescription? = null,
+ override val contentDescription: ContentDescription? = null,
) : InternetTileModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
index a1b96dd..fe69d81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
@@ -20,7 +20,7 @@
import android.graphics.Rect
import android.util.AttributeSet
import android.view.Gravity
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.BaseStatusBarFrameLayout
import com.android.systemui.statusbar.StatusBarIconView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
index b6f1677..a80ea90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
@@ -17,7 +17,8 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import android.content.Context
-import com.android.systemui.R
+import com.android.systemui.res.R
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -61,16 +62,21 @@
private val context: Context,
@Application scope: CoroutineScope,
) {
+ private val internetLabel: String = context.getString(R.string.quick_settings_internet_label)
+
// Three symmetrical Flows that can be switched upon based on the value of
// [DefaultConnectionModel]
private val wifiIconFlow: Flow<InternetTileModel> =
wifiInteractor.wifiNetwork.flatMapLatest {
val wifiIcon = WifiIcon.fromModel(it, context, showHotspotInfo = true)
if (it is WifiNetworkModel.Active && wifiIcon is WifiIcon.Visible) {
+ val secondary = removeDoubleQuotes(it.ssid)
flowOf(
InternetTileModel.Active(
- secondaryTitle = removeDoubleQuotes(it.ssid),
- icon = ResourceIcon.get(wifiIcon.icon.res)
+ secondaryTitle = secondary,
+ icon = ResourceIcon.get(wifiIcon.icon.res),
+ stateDescription = wifiIcon.contentDescription,
+ contentDescription = ContentDescription.Loaded("$internetLabel,$secondary"),
)
)
} else {
@@ -109,10 +115,13 @@
it.signalLevelIcon,
mobileDataContentName,
) { networkNameModel, signalIcon, dataContentDescription ->
+ val secondary =
+ mobileDataContentConcat(networkNameModel.name, dataContentDescription)
InternetTileModel.Active(
- secondaryTitle =
- mobileDataContentConcat(networkNameModel.name, dataContentDescription),
+ secondaryTitle = secondary,
icon = SignalIcon(signalIcon.toSignalDrawableState()),
+ stateDescription = ContentDescription.Loaded(secondary),
+ contentDescription = ContentDescription.Loaded(internetLabel),
)
}
}
@@ -148,10 +157,13 @@
if (it == null) {
notConnectedFlow
} else {
+ val secondary = it.contentDescription.toString()
flowOf(
InternetTileModel.Active(
- secondaryTitle = it.contentDescription.toString(),
- iconId = it.res
+ secondaryTitle = secondary,
+ iconId = it.res,
+ stateDescription = null,
+ contentDescription = ContentDescription.Loaded(secondary),
)
)
}
@@ -164,16 +176,23 @@
) { networksAvailable, isAirplaneMode ->
when {
isAirplaneMode -> {
+ val secondary = context.getString(R.string.status_bar_airplane)
InternetTileModel.Inactive(
- secondaryTitle = context.getString(R.string.status_bar_airplane),
- icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable)
+ secondaryTitle = secondary,
+ icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable),
+ stateDescription = null,
+ contentDescription = ContentDescription.Loaded(secondary),
)
}
networksAvailable -> {
+ val secondary =
+ context.getString(R.string.quick_settings_networks_available)
InternetTileModel.Inactive(
- secondaryTitle =
- context.getString(R.string.quick_settings_networks_available),
+ secondaryTitle = secondary,
iconId = R.drawable.ic_qs_no_internet_available,
+ stateDescription = null,
+ contentDescription =
+ ContentDescription.Loaded("$internetLabel,$secondary")
)
}
else -> {
@@ -206,6 +225,9 @@
InternetTileModel.Inactive(
secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
iconId = R.drawable.ic_qs_no_internet_unavailable,
+ stateDescription = null,
+ contentDescription =
+ ContentDescription.Resource(R.string.quick_settings_networks_unavailable),
)
private fun removeDoubleQuotes(string: String?): String? {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
index 8bea772..2da76d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
@@ -18,7 +18,7 @@
import android.content.Context
import com.android.systemui.Dumpable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index e593575..a9ac51d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -23,7 +23,7 @@
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.StatusBarIconView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index 668c5b3..efa092b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -22,7 +22,7 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
index f23e102..4cd3484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -20,7 +20,7 @@
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
index 27e40e0..a078dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
@@ -23,7 +23,7 @@
import android.content.Context
import android.content.Intent
import android.net.Uri
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 6186c43..13f76fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -25,7 +25,7 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.ToggleSlider;
import com.android.systemui.shade.NotificationShadeWindowView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index f7b601b..b06ebe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -35,7 +35,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.Utils;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index b7ae233..713283e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -47,7 +47,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index b5bd1d8..74e02d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -29,7 +29,7 @@
import android.widget.TextView;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import java.util.Date;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
index ffb743f..1224275 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -21,7 +21,7 @@
import android.content.SharedPreferences
import android.provider.Settings
import android.util.Log
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index d472c35..e59ec04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -34,7 +34,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
index feef029..f4a1975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -21,7 +21,7 @@
import android.util.Log;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index a4821e0..4b97197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -32,7 +32,7 @@
import androidx.annotation.NonNull;
import com.android.internal.util.ConcurrentUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 880e0d2..62e2381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -35,7 +35,7 @@
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.Expandable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.FalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 63dcad9..1c88289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -37,7 +37,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
index e1ec94f..012408e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
@@ -26,7 +26,7 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardConstants;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 2d04ffa..bb074ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -38,7 +38,7 @@
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
index 1d9d33d..5ed207cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -28,7 +28,7 @@
import android.graphics.drawable.Drawable;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Gradient background for the user switcher on Keyguard.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index e00365d..53fed3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -81,7 +81,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index 736b145..a50fd6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -30,7 +30,7 @@
import android.util.Log
import android.view.View
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION
import com.android.systemui.statusbar.NotificationRemoteInputManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
index e71c972..c302d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy
import android.content.res.Resources
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Fake SplitShadeStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 03656f0..4a4d4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -50,7 +50,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 52a6bca..7ac3e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -26,7 +26,7 @@
import android.util.Log;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.DeviceConfigProxy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index 1776e5b..616992e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -41,7 +41,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.Button
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.DevicePolicyManagerWrapper
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index fb6ba85..b46f52c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -32,7 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationUtils;
import java.text.BreakIterator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
index 9f61574..0d36b48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
@@ -27,7 +27,7 @@
import android.widget.LinearLayout;
import android.widget.TextClock;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Container for a clock which has two separate views for the clock itself and AM/PM indicator. This
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt
index ab4a8af..43905c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy
import android.content.res.Resources
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index a593d51..2ed9d15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -36,7 +36,7 @@
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
index cd1dcd5..2f171bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
@@ -20,7 +20,7 @@
import android.text.StaticLayout
import android.util.AttributeSet
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* View for showing a date that can toggle between two different formats depending on size.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index dabdcc5..39cdfa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -169,6 +169,7 @@
@Override
public void addCallback(@NonNull Callback callback) {
synchronized (mCallbacksLock) {
+ Log.d(TAG, "Added callback " + callback.getClass());
mCallbacks.add(callback);
}
}
@@ -176,6 +177,7 @@
@Override
public void removeCallback(@NonNull Callback callback) {
synchronized (mCallbacksLock) {
+ Log.d(TAG, "Removed callback " + callback.getClass());
mCallbacks.remove(callback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 927024f..a77c692 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -213,12 +213,11 @@
return networkController.getDataSaverController();
}
- /** Provides a log bufffer for BatteryControllerImpl */
+ /** Provides a log buffer for BatteryControllerImpl */
@Provides
@SysUISingleton
@BatteryControllerLog
- //TODO(b/300147438): reduce the size of this log buffer
static LogBuffer provideBatteryControllerLog(LogBufferFactory factory) {
- return factory.create(BatteryControllerLogger.TAG, 300);
+ return factory.create(BatteryControllerLogger.TAG, 30);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index f4cc0ed..e4e9554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -47,7 +47,7 @@
import android.view.WindowManager;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
index 874217a..1c7debc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
@@ -1,7 +1,7 @@
package com.android.systemui.statusbar.window
import android.view.LayoutInflater
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index c1ac800..fa9256f 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -36,7 +36,7 @@
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.DebugLogger.debugLog
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt b/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt
index b9c2487..ccd8e48 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt
@@ -26,7 +26,7 @@
import android.util.Log
import android.view.WindowManager
import com.android.internal.app.AlertActivity
-import com.android.systemui.R
+import com.android.systemui.res.R
import javax.inject.Inject
/** Dialog shown to the user to switch to managed profile for making a call using work SIM. */
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index f0aae0f..fc414b6 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -39,7 +39,7 @@
import com.android.app.animation.Interpolators
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index f24d526..4449f8d0 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -21,7 +21,7 @@
import android.view.View
import androidx.annotation.AttrRes
import com.android.internal.logging.InstanceId
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.temporarydisplay.TemporaryViewInfo
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeModule.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeModule.java
index 7fa90df..e78eba4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeModule.java
@@ -18,7 +18,7 @@
import android.content.res.Resources;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 29f16c7..d97cae2 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -170,9 +170,9 @@
}
final View toastView = mLayoutInflater.inflate(
- com.android.systemui.R.layout.text_toast, null);
- final TextView textView = toastView.findViewById(com.android.systemui.R.id.text);
- final ImageView iconView = toastView.findViewById(com.android.systemui.R.id.icon);
+ com.android.systemui.res.R.layout.text_toast, null);
+ final TextView textView = toastView.findViewById(com.android.systemui.res.R.id.text);
+ final ImageView iconView = toastView.findViewById(com.android.systemui.res.R.id.icon);
textView.setText(mText);
ApplicationInfo appInfo = null;
@@ -189,7 +189,7 @@
textView.setMaxLines(Integer.MAX_VALUE);
// no app icon
- toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
+ toastView.findViewById(com.android.systemui.res.R.id.icon).setVisibility(View.GONE);
} else {
Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
if (icon == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
index 8187956..8b5fd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
@@ -31,8 +31,8 @@
companion object {
// total duration shouldn't exceed NotificationManagerService's delay for "in" animation
fun toastIn(view: View): AnimatorSet? {
- val icon: View? = view.findViewById(com.android.systemui.R.id.icon)
- val text: View? = view.findViewById(com.android.systemui.R.id.text)
+ val icon: View? = view.findViewById(com.android.systemui.res.R.id.icon)
+ val text: View? = view.findViewById(com.android.systemui.res.R.id.text)
if (icon == null || text == null) {
return null
}
@@ -69,8 +69,8 @@
fun toastOut(view: View): AnimatorSet? {
// total duration shouldn't exceed NotificationManagerService's delay for "out" anim
- val icon: View? = view.findViewById(com.android.systemui.R.id.icon)
- val text: View? = view.findViewById(com.android.systemui.R.id.text)
+ val icon: View? = view.findViewById(com.android.systemui.res.R.id.icon)
+ val text: View? = view.findViewById(com.android.systemui.res.R.id.text)
if (icon == null || text == null) {
return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java b/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java
index 919f150..5ae4cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java
@@ -24,7 +24,7 @@
import android.view.View;
import android.widget.ImageView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class ClipboardView extends ImageView implements OnPrimaryClipChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 2464886..a43524a 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -29,7 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeAvailabilityTracker;
import com.android.systemui.demomode.DemoModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
index 4dbceac..771a8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
@@ -41,7 +41,7 @@
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
import com.android.systemui.statusbar.ScalingDrawableWrapper;
import com.android.systemui.statusbar.phone.ExpandableIndicator;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
index 87d2063..ec6f862 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
@@ -46,7 +46,7 @@
import androidx.preference.Preference.OnPreferenceChangeListener;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
index 7239c8a..32b1b26 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
@@ -18,7 +18,7 @@
import androidx.preference.PreferenceFragment;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class OtherPrefs extends PreferenceFragment {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index 4999515..7635a84 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -36,7 +36,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.PluginEnablerImpl;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginActionManager;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
index 80f9de6..ce1a2e9 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
@@ -27,7 +27,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
public class PowerNotificationControlsFragment extends Fragment {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
index 2475890..20ce230 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
@@ -29,7 +29,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.fragments.FragmentService;
import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/SelectablePreference.java b/packages/SystemUI/src/com/android/systemui/tuner/SelectablePreference.java
index 0be793e..386b7bb 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/SelectablePreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/SelectablePreference.java
@@ -27,7 +27,7 @@
public SelectablePreference(Context context) {
super(context);
- setWidgetLayoutResource(com.android.systemui.R.layout.preference_widget_radiobutton);
+ setWidgetLayoutResource(com.android.systemui.res.R.layout.preference_widget_radiobutton);
setSelectable(true);
mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32,
context.getResources().getDisplayMetrics());
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ShortcutPicker.java b/packages/SystemUI/src/com/android/systemui/tuner/ShortcutPicker.java
index 11e1f27..5b34052 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ShortcutPicker.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ShortcutPicker.java
@@ -30,7 +30,7 @@
import androidx.preference.PreferenceViewHolder;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.tuner.ShortcutParser.Shortcut;
import com.android.systemui.tuner.TunerService.Tunable;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index ecaf792..14d7281 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -30,7 +30,7 @@
import androidx.preference.PreferenceScreen;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.util.settings.GlobalSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 989462a..9cc526a 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -33,7 +33,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.shared.plugins.PluginPrefs;
public class TunerFragment extends PreferenceFragment {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index ccc0a79..8087a87 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -38,7 +38,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.systemui.DejankUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
index 71355bb..1c441b3 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerSwitch.java
@@ -9,7 +9,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.tuner.TunerService.Tunable;
public class TunerSwitch extends SwitchPreference implements Tunable {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index 3a7ac9c..c1eb5b6 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -30,7 +30,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java
index e06353b..7e9c5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java
@@ -22,7 +22,7 @@
import android.content.res.Resources;
import android.util.Log;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
index 534049b..7ede21a 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
@@ -29,7 +29,7 @@
import android.widget.TextView;
import android.widget.Toast;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Activity that alerts the user when contaminant is detected on USB port.
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 941cd77..e7e907a 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -38,7 +38,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
index 2871263..ea871be 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
@@ -34,7 +34,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java
index 55dec5f..44dee72 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java
@@ -29,7 +29,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
abstract class UsbDialogActivity extends AlertActivity
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index 02a7637..080a9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -38,7 +38,7 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.chooser.TargetInfo;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.Iterator;
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 08b0c64..562feb2e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -33,7 +33,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.users.CreateUserDialogController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ActivityStarter;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt
index 5ad9630..6df02e8 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt
@@ -22,7 +22,7 @@
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowInsets
import android.view.WindowInsetsController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
index ee84580..1fa4872 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
@@ -23,7 +23,7 @@
import android.widget.ListAdapter
import android.widget.ListPopupWindow
import android.widget.ListView
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* Popup menu for displaying items on the fullscreen user switcher.
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 8cfa555..f404549 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -23,7 +23,7 @@
import android.os.UserManager
import android.provider.Settings
import androidx.annotation.VisibleForTesting
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index a3e648d..e489499 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -37,7 +37,7 @@
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 03a7470..93573fa 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -23,7 +23,7 @@
import android.os.UserManager
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.shared.model.UserActionModel
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
index 1ac86ce..8957fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -20,7 +20,7 @@
import android.content.Context
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.user.data.source.UserRecord
/**
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
index 59f2cdb..c578702 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -34,7 +34,7 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Gefingerpoken
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.UserSwitcherPopupMenu
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
index e78807e..8fc1b25 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
@@ -25,7 +25,7 @@
import android.widget.ImageView
import androidx.core.content.res.ResourcesCompat
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.user.ui.viewmodel.UserViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
index fdf13be..b88f9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
@@ -82,7 +82,7 @@
context.getString(R.string.user_add_user_message_short) +
if (showEphemeralMessage) {
context.getString(
- com.android.systemui.R.string.user_add_user_message_guest_remove
+ com.android.systemui.res.R.string.user_add_user_message_guest_remove
)
} else {
""
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
index b8ae257..4058215 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
@@ -5,7 +5,7 @@
import android.provider.Settings
import android.view.LayoutInflater
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index aeed5fc..61952ba 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.user.ui.viewmodel
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
index 516efc0..947746c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
@@ -28,7 +28,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
index 8b90547..ef9340a3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.util.AttributeSet
-import com.android.systemui.R
+import com.android.systemui.res.R
class DelayableMarqueeTextView @JvmOverloads constructor(
context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
index 9b241a7..bbfdaa3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
@@ -1,7 +1,7 @@
package com.android.systemui.util
import android.content.res.Resources
-import com.android.systemui.R
+import com.android.systemui.res.R
object LargeScreenUtils {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 904a98b..8a6f79d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -24,7 +24,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import java.util.Arrays;
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index e0daa070..760fe6a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -23,7 +23,7 @@
import android.view.DisplayCutout;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt b/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
index 47a2d35..577d18e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
@@ -21,7 +21,7 @@
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
-import com.android.systemui.R
+import com.android.systemui.res.R
/**
* A special view that is designed to host a single "unique object". The unique object is
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 58cffa7..de392d3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -48,7 +48,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
index 96980b8..7934ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
@@ -24,7 +24,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.concurrency.DelayableExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
index c62c957..bcf34f8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
@@ -19,7 +19,7 @@
import android.content.res.Resources;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Named;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
index ae23ca6..ac41a50 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
@@ -26,7 +26,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/** Toggle button in Volume Dialog for controlling system captions state */
public class CaptionsToggleImageButton extends AlphaOptimizedImageButton {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
index fb5a71c..eb0bf46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 89bc757..d9593bd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -25,7 +25,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 9cc3cdb..ea4d31b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -64,7 +64,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.volume.MediaSessions;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index dcc0525..727d649 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -120,7 +120,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
index 96936e3..6050387 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
@@ -51,7 +51,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.MediaOutputConstants;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java
index 2371402..d7669d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelSlicesAdapter.java
@@ -33,7 +33,7 @@
import androidx.slice.SliceItem;
import androidx.slice.widget.SliceView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeToolTipView.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeToolTipView.java
index 1de5585..2143771 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeToolTipView.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeToolTipView.java
@@ -28,7 +28,7 @@
import androidx.core.content.ContextCompat;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.recents.TriangleShape;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 0b3521b..3451ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,7 +22,7 @@
import android.util.Log;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.tiles.DndTile;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index de9b5ee..0e6df6b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -30,7 +30,7 @@
import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
import android.util.Log;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index e5c55b0..e90d9e2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -27,7 +27,7 @@
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
private final int mUnselectedRadius;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 2491e2b..750b6f9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -40,7 +40,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
index b7c8b49..02a764c 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
@@ -38,7 +38,7 @@
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import java.util.Collections;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardView.java
index fc1adc3..7b50a65 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardView.java
@@ -25,7 +25,7 @@
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/** Customized card view of the wallet card carousel. */
public class WalletCardView extends CardView {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewHolder.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewHolder.java
index 3197976..c33a805 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewHolder.java
@@ -22,7 +22,7 @@
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* View holder for the quick access wallet card.
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 81d04d4..d21ccc9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -16,6 +16,8 @@
package com.android.systemui.wallet.ui;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
+
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -41,16 +43,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/** Controller for the wallet card carousel screen. */
public class WalletScreenController implements
@@ -126,22 +128,11 @@
return;
}
Log.i(TAG, "Successfully retrieved wallet cards.");
- List<WalletCard> walletCards = response.getWalletCards();
+ List<WalletCard> walletCards = getPaymentCards(response.getWalletCards());
- boolean allUnknown = true;
- for (WalletCard card : walletCards) {
- if (card.getCardType() != WalletCard.CARD_TYPE_UNKNOWN) {
- allUnknown = false;
- break;
- }
- }
-
- List<WalletCardViewInfo> paymentCardData = new ArrayList<>();
- for (WalletCard card : walletCards) {
- if (allUnknown || card.getCardType() == WalletCard.CARD_TYPE_PAYMENT) {
- paymentCardData.add(new QAWalletCardViewInfo(mContext, card));
- }
- }
+ List<WalletCardViewInfo> paymentCardData = walletCards.stream().map(
+ card -> new QAWalletCardViewInfo(mContext, card)
+ ).collect(Collectors.toList());
// Get on main thread for UI updates.
mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index cab47a3..f498520 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -40,7 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
new file mode 100644
index 0000000..077b80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+
+/**
+ * Filters wallet cards to only those of [WalletCard.CARD_TYPE_PAYMENT], or returns all cards if
+ * they are all of [WalletCard.CARD_TYPE_UNKNOWN] (maintaining pre-U behavior). Used by the wallet
+ * card carousel, quick settings tile, and lock screen.
+ */
+fun getPaymentCards(walletCards: List<WalletCard>): List<WalletCard> {
+ val atLeastOneKnownCardType = walletCards.any { it.cardType != WalletCard.CARD_TYPE_UNKNOWN }
+
+ return if (atLeastOneKnownCardType) {
+ walletCards.filter { it.cardType == WalletCard.CARD_TYPE_PAYMENT }
+ } else {
+ walletCards
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
index 1c7a9b5..f8ffab0 100644
--- a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
@@ -43,7 +43,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Alerts the user of an untrusted network when enabling wireless debugging.
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
index f9f14e0..972d3c0 100644
--- a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
@@ -30,7 +30,7 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
/**
* Alerts the user that wireless debugging cannot be enabled by a secondary user.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6fafcd5..897c4da 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -61,6 +61,7 @@
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.sysui.ShellInterface;
@@ -109,6 +110,7 @@
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<DesktopMode> mDesktopModeOptional;
+ private final Optional<RecentTasks> mRecentTasksOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
@@ -172,6 +174,7 @@
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<DesktopMode> desktopMode,
+ Optional<RecentTasks> recentTasks,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardStateController keyguardStateController,
@@ -195,6 +198,7 @@
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mDesktopModeOptional = desktopMode;
+ mRecentTasksOptional = recentTasks;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
@@ -220,6 +224,7 @@
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mDesktopModeOptional.ifPresent(this::initDesktopMode);
+ mRecentTasksOptional.ifPresent(this::initRecentTasks);
mNoteTaskInitializer.initialize();
}
@@ -351,6 +356,12 @@
}, mSysUiMainExecutor);
}
+ @VisibleForTesting
+ void initRecentTasks(RecentTasks recentTasks) {
+ recentTasks.addAnimationStateListener(mSysUiMainExecutor,
+ mCommandQueue::onRecentsAnimationStateChanged);
+ }
+
@Override
public boolean isDumpCritical() {
// Dump can't be critical because the shell has to dump on the main thread for
diff --git a/packages/SystemUI/tests/Android.bp b/packages/SystemUI/tests/Android.bp
index 3c418ed..ec0414e 100644
--- a/packages/SystemUI/tests/Android.bp
+++ b/packages/SystemUI/tests/Android.bp
@@ -20,6 +20,7 @@
android_test {
name: "SystemUITests",
+ use_resource_processor: true,
dxflags: ["--multi-dex"],
platform_apis: true,
diff --git a/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java
index 188dff2..205168e 100644
--- a/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java
+++ b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java
@@ -27,7 +27,7 @@
public class SysuiResourceLoadingTest extends SysuiRoboBase {
@Test
public void testResources() {
- assertThat(getContext().getString(com.android.systemui.R.string.app_label))
+ assertThat(getContext().getString(com.android.systemui.res.R.string.app_label))
.isEqualTo("System UI");
assertThat(getContext().getString(com.android.systemui.tests.R.string.test_content))
.isNotEmpty();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 9016220..9ce9bd6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -55,7 +55,7 @@
import android.text.TextUtils;
import com.android.keyguard.logging.CarrierTextManagerLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.LogBufferHelperKt;
import com.android.systemui.keyguard.WakefulnessLifecycle;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
index 6c5620d..94c34a5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
@@ -67,7 +67,7 @@
private fun createFaceWakeUpTriggersConfig(wakeUpTriggers: IntArray): FaceWakeUpTriggersConfig {
overrideResource(
- com.android.systemui.R.array.config_face_auth_wake_up_triggers,
+ com.android.systemui.res.R.array.config_face_auth_wake_up_triggers,
wakeUpTriggers
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index c894d91..42f65f6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -37,7 +37,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
index 00f88bf..8401e67 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java
@@ -27,7 +27,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 3d87196..00a2efe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -35,7 +35,7 @@
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -148,7 +148,7 @@
when(mView.getResources()).thenReturn(mResources);
when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
.thenReturn(100);
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin))
+ when(mResources.getDimensionPixelSize(com.android.systemui.customization.R.dimen.keyguard_large_clock_top_margin))
.thenReturn(-200);
when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
.thenReturn(INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 65c677e..e54b184 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -40,7 +40,7 @@
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.ClockFaceController;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index e1b608f..f943acd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -24,12 +24,13 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.whenever
import org.junit.Before
@@ -67,6 +68,7 @@
@Mock
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var postureController: DevicePostureController
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -89,6 +91,7 @@
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
val fakeFeatureFlags = FakeFeatureFlags()
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+ fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
keyguardPasswordViewController =
KeyguardPasswordViewController(
keyguardPasswordView,
@@ -104,6 +107,7 @@
mContext.resources,
falsingCollector,
keyguardViewController,
+ postureController,
fakeFeatureFlags
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 93048a5..e090093 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -94,9 +94,10 @@
.thenReturn(mKeyguardMessageAreaController)
fakeFeatureFlags = FakeFeatureFlags()
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
+ fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
mKeyguardPatternView =
View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
-
+ mKeyguardPatternView.setIsLockScreenLandscapeEnabled(false)
mKeyguardPatternViewController =
KeyguardPatternViewController(
mKeyguardPatternView,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 2b90e7c..8322b37 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -31,7 +31,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 33d4097..2f08804 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -123,7 +123,6 @@
private fun constructPinViewController(
mKeyguardPinView: KeyguardPINView
): KeyguardPinViewController {
- mKeyguardPinView.setIsLockScreenLandscapeEnabled(false)
return KeyguardPinViewController(
mKeyguardPinView,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 62906f3..f3a1b68 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -32,7 +32,7 @@
import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 7c1861e..f6649bd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,7 +36,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
@@ -60,6 +60,7 @@
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.UserSwitcherController
@@ -140,6 +141,7 @@
@Mock private lateinit var userInteractor: UserInteractor
@Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var postureController: DevicePostureController
@Captor
private lateinit var swipeListenerArgumentCaptor:
@@ -197,6 +199,7 @@
featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false)
featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
+ featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
keyguardPasswordViewController =
KeyguardPasswordViewController(
@@ -213,6 +216,7 @@
mock(),
null,
keyguardViewController,
+ postureController,
featureFlags
)
@@ -806,7 +810,8 @@
ObservableTransitionState.Transition(
SceneKey.Lockscreen,
SceneKey.Bouncer,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -818,7 +823,12 @@
// keyguard.
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Bouncer,
+ SceneKey.Gone,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -830,7 +840,12 @@
clearInvocations(viewMediatorCallback)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Gone,
+ SceneKey.Bouncer,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
@@ -843,7 +858,12 @@
underTest.onViewDetached()
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value =
- ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+ ObservableTransitionState.Transition(
+ SceneKey.Bouncer,
+ SceneKey.Gone,
+ flowOf(.5f),
+ false
+ )
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -856,7 +876,8 @@
ObservableTransitionState.Transition(
SceneKey.Gone,
SceneKey.Lockscreen,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -872,7 +893,8 @@
ObservableTransitionState.Transition(
SceneKey.Lockscreen,
SceneKey.Gone,
- flowOf(.5f)
+ flowOf(.5f),
+ false,
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 3e330d65..aad11d9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -56,7 +56,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingA11yDelegate;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 19bc818..b02b1f9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -35,7 +35,7 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4db5f35..d0bb5a9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 47ff3b9..59cd26c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 77302ce..6654a6c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -28,7 +28,7 @@
import androidx.slice.builders.ListBuilder;
import androidx.slice.widget.RowContent;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardSliceProvider;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index 210f3cb..86439e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -6,7 +6,7 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.children
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index bb03f28..3afca59 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -20,7 +20,7 @@
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 562d3a5..47be236 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,6 +29,7 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -38,11 +39,17 @@
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -139,6 +146,7 @@
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -172,7 +180,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Random;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -276,6 +283,8 @@
private TaskStackChangeListeners mTaskStackChangeListeners;
@Mock
private IActivityTaskManager mActivityTaskManager;
+ @Mock
+ private WakefulnessLifecycle mWakefulness;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -332,7 +341,7 @@
when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
mContext.getOrCreateTestableResources().addOverride(
- com.android.systemui.R.integer.config_face_auth_supported_posture,
+ com.android.systemui.res.R.integer.config_face_auth_supported_posture,
DEVICE_POSTURE_UNKNOWN);
mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
mContext.getResources(),
@@ -340,7 +349,7 @@
mDumpManager
);
- mContext.getOrCreateTestableResources().addOverride(com.android.systemui
+ mContext.getOrCreateTestableResources().addOverride(com.android.systemui.res
.R.array.config_fingerprint_listen_on_occluding_activity_packages,
new String[]{ PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY });
@@ -1363,7 +1372,7 @@
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
- mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0));
+ mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
@@ -2999,8 +3008,57 @@
// THEN face listening is stopped.
verify(faceCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
- eq(false), eq(BiometricSourceType.FACE)); // beverlyt
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+ @Test
+ public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
}
private void triggerDefaultDisplayStateChangeToOn() {
@@ -3307,6 +3365,7 @@
clearInvocations(mStatusBarStateController);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
setupBiometrics(mKeyguardUpdateMonitor);
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
}
@@ -3387,7 +3446,8 @@
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider),
- mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker);
+ mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
+ mWakefulness);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
index 08185af..c674053 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
@@ -18,7 +18,7 @@
import android.testing.AndroidTestingRunner
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 0e4b3c9..21a2822 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -39,7 +39,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
index 42e12df..835c9ff5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
@@ -20,7 +20,7 @@
import android.testing.TestableLooper
import android.view.LayoutInflater
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
index c04fd39..d431731 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
@@ -20,7 +20,7 @@
import android.testing.TestableLooper
import android.view.LayoutInflater
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
index d1573c3..532c59a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -32,6 +32,7 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.settingslib.R;
import com.android.settingslib.graph.BatteryMeterDrawableBase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index f81ef10..af88df7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -78,6 +78,7 @@
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.systemui.res.R;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.decor.CornerDecorProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 79dc057..8fd2bd6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -71,7 +71,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 09d0eeb..22ebd99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -46,7 +46,7 @@
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 39fe6fb..187f098 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -88,7 +88,7 @@
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.settings.FakeDisplayTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 91c4748..95e21cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -59,7 +59,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
index afd5f77..403385f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
@@ -28,7 +28,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
index 46c930f..43ebeee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -26,7 +26,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 42b610a..9b81947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -26,7 +26,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 1b0a10e..5764839 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -37,7 +37,7 @@
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
index 7b99314..83bee93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
@@ -24,7 +24,7 @@
import android.widget.Button
import android.widget.SeekBar
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index cc00436..59c7e76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -57,7 +57,8 @@
@Before
fun setup() {
- activityLaunchAnimator = ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator)
+ activityLaunchAnimator =
+ ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator, disableWmTimeout = true)
activityLaunchAnimator.callback = callback
activityLaunchAnimator.addListener(listener)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index f9830b1..0b0410a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -59,6 +59,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -108,6 +109,7 @@
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
+ private final FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
private String mExemptedRolePkgName;
@@ -139,6 +141,7 @@
mController = new AppOpsControllerImpl(
mContext,
mTestableLooper.getLooper(),
+ mBgExecutor,
mDumpManager,
mAudioManager,
mSensorPrivacyController,
@@ -150,6 +153,8 @@
@Test
public void testOnlyListenForFewOps() {
mController.setListening(true);
+ mBgExecutor.runAllReady();
+
verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
verify(mSensorPrivacyController, times(1)).addCallback(mController);
@@ -158,6 +163,7 @@
@Test
public void testStopListening() {
mController.setListening(false);
+ mBgExecutor.runAllReady();
verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
verify(mDispatcher, times(1)).unregisterReceiver(mController);
verify(mSensorPrivacyController, times(1)).removeCallback(mController);
@@ -169,6 +175,7 @@
.thenReturn(List.of());
mController.setListening(true);
+ mBgExecutor.runAllReady();
assertThat(mController.getActiveAppOps()).isEmpty();
}
@@ -186,6 +193,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the op
List<AppOpItem> list = mController.getActiveAppOps();
@@ -218,6 +226,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -265,6 +274,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -304,6 +314,7 @@
// WHEN we start listening
mController.setListening(true);
+ mBgExecutor.runAllReady();
// THEN the active list has the ops
List<AppOpItem> list = mController.getActiveAppOps();
@@ -341,6 +352,7 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
// THEN the callback is notified of the current active ops it cares about
@@ -366,11 +378,14 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mTestableLooper.processAllMessages();
+
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
}
@@ -383,6 +398,7 @@
public void addCallback_partialIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(
@@ -400,9 +416,12 @@
@Test
public void addCallback_notIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
+
verify(mCallback, never()).onActiveStateChanged(
anyInt(), anyInt(), anyString(), anyBoolean());
}
@@ -410,7 +429,10 @@
@Test
public void removeCallback_sameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -421,7 +443,10 @@
@Test
public void addCallback_notSameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -484,6 +509,8 @@
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
+
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
@@ -506,6 +533,7 @@
mController.setBGHandler(mMockHandler);
mController.setListening(true);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID,
TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
@@ -513,6 +541,7 @@
assertFalse(mController.getActiveAppOps().isEmpty());
mController.setListening(false);
+ mBgExecutor.runAllReady();
verify(mMockHandler).removeCallbacksAndMessages(null);
assertTrue(mController.getActiveAppOps().isEmpty());
@@ -583,6 +612,7 @@
TestHandler testHandler = new TestHandler(mTestableLooper.getLooper());
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.setBGHandler(testHandler);
mController.onOpActiveChanged(
@@ -616,6 +646,7 @@
@Test
public void testNotedNotRemovedAfterActive() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -645,6 +676,7 @@
@Test
public void testNotedAndActiveOnlyOneCall() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -660,6 +692,7 @@
@Test
public void testActiveAndNotedOnlyOneCall() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mBgExecutor.runAllReady();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
@@ -675,6 +708,7 @@
@Test
public void testPausedRecordingIsRetrievedOnCreation() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -688,6 +722,7 @@
@Test
public void testPausedRecordingFilteredOut() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -700,6 +735,7 @@
@Test
public void testPausedPhoneCallMicrophoneFilteredOut() {
mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -715,6 +751,7 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_CAMERA
}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
@@ -751,6 +788,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -779,6 +817,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -807,6 +846,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -835,6 +875,7 @@
// Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
// verify the micOp is the only active op returned.
mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -859,6 +900,7 @@
public void testCameraFilteredWhenCameraDisabled() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -892,6 +934,7 @@
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA},
mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OPSTR_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -922,6 +965,7 @@
private void verifyUnPausedSentActive(int micOpCode) {
mController.addCallback(new int[]{micOpCode}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID,
TEST_PACKAGE_NAME, true);
@@ -936,6 +980,7 @@
private void verifyAudioPausedSentInactive(int micOpCode) {
mController.addCallback(new int[]{micOpCode}, mCallback);
+ mBgExecutor.runAllReady();
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID_OTHER,
TEST_PACKAGE_NAME, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
index dd9683f..957443a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
index ec8be8e..323f15a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
index f0f4ca7..4ab7ab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -19,7 +19,7 @@
import android.testing.TestableLooper.RunWithLooper
import android.widget.ImageView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher
import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt
index 52bf350..215d635 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt
@@ -25,7 +25,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 969a011..f899e2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -37,7 +37,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index d0b3833..2bc0171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -906,14 +906,14 @@
when(mContextSpy.getResources()).thenReturn(mResources);
doReturn(500).when(mResources)
- .getDimensionPixelSize(eq(com.android.systemui.R.dimen
+ .getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_y));
mAuthController.onConfigurationChanged(null /* newConfig */);
final Point firstFpLocation = mAuthController.getFingerprintSensorLocation();
doReturn(1000).when(mResources)
- .getDimensionPixelSize(eq(com.android.systemui.R.dimen
+ .getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_y));
mAuthController.onConfigurationChanged(null /* newConfig */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
index b0d0063..3c10678 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
@@ -30,9 +30,12 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.BiometricStateListener;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -52,6 +55,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -65,11 +70,20 @@
KeyguardStateController mKeyguardStateController;
@Mock
NotificationManager mNotificationManager;
+ @Mock
+ Optional<FingerprintReEnrollNotification> mFingerprintReEnrollNotificationOptional;
+ @Mock
+ FingerprintReEnrollNotification mFingerprintReEnrollNotification;
+ @Mock
+ FingerprintManager mFingerprintManager;
+ @Mock
+ FaceManager mFaceManager;
private static final String TAG = "BiometricNotificationService";
private static final int FACE_NOTIFICATION_ID = 1;
private static final int FINGERPRINT_NOTIFICATION_ID = 2;
private static final long SHOW_NOTIFICATION_DELAY_MS = 5_000L; // 5 seconds
+ private static final int FINGERPRINT_ACQUIRED_RE_ENROLL = 0;
private final ArgumentCaptor<Notification> mNotificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
@@ -77,39 +91,61 @@
private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
private KeyguardStateController.Callback mKeyguardStateControllerCallback;
private BiometricNotificationService mBiometricNotificationService;
+ private BiometricStateListener mFaceStateListener;
+ private BiometricStateListener mFingerprintStateListener;
@Before
public void setUp() {
+ when(mFingerprintReEnrollNotificationOptional.orElse(any()))
+ .thenReturn(mFingerprintReEnrollNotification);
+ when(mFingerprintReEnrollNotification.isFingerprintReEnrollRequired(
+ FINGERPRINT_ACQUIRED_RE_ENROLL)).thenReturn(true);
+
mLooper = TestableLooper.get(this);
Handler handler = new Handler(mLooper.getLooper());
BiometricNotificationDialogFactory dialogFactory = new BiometricNotificationDialogFactory();
BiometricNotificationBroadcastReceiver broadcastReceiver =
new BiometricNotificationBroadcastReceiver(mContext, dialogFactory);
- mBiometricNotificationService = new BiometricNotificationService(mContext,
- mKeyguardUpdateMonitor, mKeyguardStateController, handler,
- mNotificationManager, broadcastReceiver);
+ mBiometricNotificationService =
+ new BiometricNotificationService(mContext,
+ mKeyguardUpdateMonitor, mKeyguardStateController, handler,
+ mNotificationManager,
+ broadcastReceiver,
+ mFingerprintReEnrollNotificationOptional,
+ mFingerprintManager,
+ mFaceManager);
mBiometricNotificationService.start();
ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
ArgumentCaptor<KeyguardStateController.Callback> stateControllerCallbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+ ArgumentCaptor<BiometricStateListener> faceStateListenerArgumentCaptor =
+ ArgumentCaptor.forClass(BiometricStateListener.class);
+ ArgumentCaptor<BiometricStateListener> fingerprintStateListenerArgumentCaptor =
+ ArgumentCaptor.forClass(BiometricStateListener.class);
verify(mKeyguardUpdateMonitor).registerCallback(
updateMonitorCallbackArgumentCaptor.capture());
verify(mKeyguardStateController).addCallback(
stateControllerCallbackArgumentCaptor.capture());
+ verify(mFaceManager).registerBiometricStateListener(
+ faceStateListenerArgumentCaptor.capture());
+ verify(mFingerprintManager).registerBiometricStateListener(
+ fingerprintStateListenerArgumentCaptor.capture());
+ mFaceStateListener = faceStateListenerArgumentCaptor.getValue();
+ mFingerprintStateListener = fingerprintStateListenerArgumentCaptor.getValue();
mKeyguardUpdateMonitorCallback = updateMonitorCallbackArgumentCaptor.getValue();
mKeyguardStateControllerCallback = stateControllerCallbackArgumentCaptor.getValue();
}
@Test
- public void testShowFingerprintReEnrollNotification() {
+ public void testShowFingerprintReEnrollNotification_onAcquiredReEnroll() {
when(mKeyguardStateController.isShowing()).thenReturn(false);
- mKeyguardUpdateMonitorCallback.onBiometricError(
- BiometricFingerprintConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ FINGERPRINT_ACQUIRED_RE_ENROLL,
"Testing Fingerprint Re-enrollment" /* errString */,
BiometricSourceType.FINGERPRINT
);
@@ -127,7 +163,7 @@
.isEqualTo(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG);
}
@Test
- public void testShowFaceReEnrollNotification() {
+ public void testShowFaceReEnrollNotification_onErrorReEnroll() {
when(mKeyguardStateController.isShowing()).thenReturn(false);
mKeyguardUpdateMonitorCallback.onBiometricError(
@@ -150,6 +186,54 @@
}
@Test
+ public void testCancelReEnrollmentNotification_onFaceEnrollmentStateChange() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ mKeyguardUpdateMonitorCallback.onBiometricError(
+ BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ "Testing Face Re-enrollment" /* errString */,
+ BiometricSourceType.FACE
+ );
+ mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+ mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS);
+ mLooper.processAllMessages();
+
+ verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID),
+ mNotificationArgumentCaptor.capture(), any());
+
+ mFaceStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */,
+ false /* hasEnrollments */);
+
+ verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID),
+ eq(UserHandle.CURRENT));
+ }
+
+ @Test
+ public void testCancelReEnrollmentNotification_onFingerprintEnrollmentStateChange() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ FINGERPRINT_ACQUIRED_RE_ENROLL,
+ "Testing Fingerprint Re-enrollment" /* errString */,
+ BiometricSourceType.FINGERPRINT
+ );
+ mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+ mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS);
+ mLooper.processAllMessages();
+
+ verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID),
+ mNotificationArgumentCaptor.capture(), any());
+
+ mFingerprintStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */,
+ false /* hasEnrollments */);
+
+ verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID),
+ eq(UserHandle.CURRENT));
+ }
+
+ @Test
public void testResetFaceUnlockReEnroll_onStart() {
when(mKeyguardStateController.isShowing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 17928a3..57cf834 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -52,7 +52,7 @@
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index c735419..21e614f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -37,7 +37,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index b6bc7af..ee3bd0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -74,7 +74,7 @@
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -1575,6 +1575,53 @@
anyBoolean());
}
+
+ @Test
+ public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible()
+ throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultMove =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+ 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_MOVE event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // GIVEN swipe down for QS
+ when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
+ when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
+
+ // WHEN ACTION_MOVE is received and touch is within sensor
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultMove);
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ mBiometricExecutor.runAllReady();
+ moveEvent.recycle();
+
+ // THEN the touch is still processed
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
+ anyBoolean());
+ }
+
@Test
public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
// GIVEN UDFPS overlay is showing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 1885f64..17f435b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -30,8 +30,6 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -99,7 +97,6 @@
context,
mKeyguardUpdateMonitor,
FakeTrustRepository(),
- FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
testScope.backgroundScope,
)
mAlternateBouncerInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 6d4588d..ebadfc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -25,7 +25,7 @@
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -67,7 +67,7 @@
@Before
fun setup() {
- context.setTheme(R.style.Theme_AppCompat)
+ context.setTheme(androidx.appcompat.R.style.Theme_AppCompat)
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index c9c46cb..c825d2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -82,6 +82,11 @@
rearDisplayDeviceStates
)
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_reverseDefaultRotation,
+ false
+ )
+
mContext = spy(mContext)
whenever(mContext.display).thenReturn(display)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
index 6fa1916..eee0ed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
@@ -36,7 +36,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
deleted file mode 100644
index 8eb274a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.factory
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.StringSubject
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class BouncerMessageFactoryTest : SysuiTestCase() {
- private lateinit var underTest: BouncerMessageFactory
-
- @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
-
- @Mock private lateinit var securityModel: KeyguardSecurityModel
-
- private lateinit var testScope: TestScope
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- testScope = TestScope()
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel)
- }
-
- @Test
- fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
- testScope.runTest {
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = false)
- .isEqualTo("Enter PIN")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = true)
- .isEqualTo("Unlock with PIN or fingerprint")
-
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = false)
- .isEqualTo("Enter password")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = true)
- .isEqualTo("Unlock with password or fingerprint")
-
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = false)
- .isEqualTo("Draw pattern")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = true)
- .isEqualTo("Unlock with pattern or fingerprint")
- }
-
- @Test
- fun bouncerMessages_overridesSecondaryMessageValue() =
- testScope.runTest {
- val bouncerMessageModel =
- bouncerMessageModel(
- PIN,
- true,
- PROMPT_REASON_DEFAULT,
- secondaryMessageOverride = "face acquisition message"
- )!!
- assertThat(context.resources.getString(bouncerMessageModel.message!!.messageResId!!))
- .isEqualTo("Unlock with PIN or fingerprint")
- assertThat(bouncerMessageModel.secondaryMessage!!.message!!)
- .isEqualTo("face acquisition message")
- }
-
- @Test
- fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
- testScope.runTest {
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = PIN,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong PIN. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = PIN,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
-
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Password,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong password. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Password,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
-
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Pattern,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong pattern. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Pattern,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
- }
-
- private fun primaryMessage(
- reason: Int,
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean
- ): StringSubject {
- return assertThat(
- context.resources.getString(
- bouncerMessageModel(mode, fpAuthAllowed, reason)!!.message!!.messageResId!!
- )
- )!!
- }
-
- private fun secondaryMessage(
- reason: Int,
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean
- ): StringSubject {
- return assertThat(
- context.resources.getString(
- bouncerMessageModel(mode, fpAuthAllowed, reason)!!.secondaryMessage!!.messageResId!!
- )
- )!!
- }
-
- private fun bouncerMessageModel(
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean,
- reason: Int,
- secondaryMessageOverride: String? = null,
- ): BouncerMessageModel? {
- whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed)
-
- return underTest.createFromPromptReason(
- reason,
- 0,
- secondaryMsgOverride = secondaryMessageOverride
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
deleted file mode 100644
index 562a8ef..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.repo
-
-import android.content.pm.UserInfo
-import android.hardware.biometrics.BiometricSourceType
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.R
-import com.android.systemui.R.string.keyguard_enter_pin
-import com.android.systemui.R.string.kg_prompt_after_dpm_lock
-import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin
-import com.android.systemui.R.string.kg_prompt_auth_timeout
-import com.android.systemui.R.string.kg_prompt_pin_auth_timeout
-import com.android.systemui.R.string.kg_prompt_reason_restart_pin
-import com.android.systemui.R.string.kg_prompt_unattended_update
-import com.android.systemui.R.string.kg_trust_agent_disabled
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
-import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.AuthenticationFlags
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-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
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class BouncerMessageRepositoryTest : SysuiTestCase() {
-
- @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var securityModel: KeyguardSecurityModel
- @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
- @Captor
- private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
-
- private lateinit var underTest: BouncerMessageRepository
- private lateinit var trustRepository: FakeTrustRepository
- private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private lateinit var userRepository: FakeUserRepository
- private lateinit var fingerprintRepository: FakeDeviceEntryFingerprintAuthRepository
- private lateinit var testScope: TestScope
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- trustRepository = FakeTrustRepository()
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- userRepository = FakeUserRepository()
- userRepository.setUserInfos(listOf(PRIMARY_USER))
- fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository()
- testScope = TestScope()
-
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
- whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
- underTest =
- BouncerMessageRepositoryImpl(
- trustRepository = trustRepository,
- biometricSettingsRepository = biometricSettingsRepository,
- updateMonitor = updateMonitor,
- bouncerMessageFactory =
- BouncerMessageFactory(biometricSettingsRepository, securityModel),
- userRepository = userRepository,
- fingerprintAuthRepository = fingerprintRepository,
- systemPropertiesHelper = systemPropertiesHelper
- )
- }
-
- @Test
- fun setCustomMessage_propagatesState() =
- testScope.runTest {
- underTest.setCustomMessage(message("not empty"))
-
- val customMessage = collectLastValue(underTest.customMessage)
-
- assertThat(customMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setFaceMessage_propagatesState() =
- testScope.runTest {
- underTest.setFaceAcquisitionMessage(message("not empty"))
-
- val faceAcquisitionMessage = collectLastValue(underTest.faceAcquisitionMessage)
-
- assertThat(faceAcquisitionMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setFpMessage_propagatesState() =
- testScope.runTest {
- underTest.setFingerprintAcquisitionMessage(message("not empty"))
-
- val fpAcquisitionMsg = collectLastValue(underTest.fingerprintAcquisitionMessage)
-
- assertThat(fpAcquisitionMsg()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setPrimaryAuthMessage_propagatesState() =
- testScope.runTest {
- underTest.setPrimaryAuthMessage(message("not empty"))
-
- val primaryAuthMessage = collectLastValue(underTest.primaryAuthMessage)
-
- assertThat(primaryAuthMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun biometricAuthMessage_propagatesBiometricAuthMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val biometricAuthMessage = collectLastValue(underTest.biometricAuthMessage)
- runCurrent()
-
- verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
-
- updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT)
-
- assertThat(biometricAuthMessage())
- .isEqualTo(message(R.string.kg_fp_not_recognized, R.string.kg_bio_try_again_or_pin))
-
- updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FACE)
-
- assertThat(biometricAuthMessage())
- .isEqualTo(
- message(R.string.bouncer_face_not_recognized, R.string.kg_bio_try_again_or_pin)
- )
-
- updateMonitorCallback.value.onBiometricAcquired(BiometricSourceType.FACE, 0)
-
- assertThat(biometricAuthMessage()).isNull()
- }
-
- @Test
- fun onFaceLockout_propagatesState() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val lockoutMessage = collectLastValue(underTest.biometricLockedOutMessage)
- runCurrent()
- verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
-
- whenever(updateMonitor.isFaceLockedOut).thenReturn(true)
- updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
-
- assertThat(lockoutMessage())
- .isEqualTo(message(keyguard_enter_pin, R.string.kg_face_locked_out))
-
- whenever(updateMonitor.isFaceLockedOut).thenReturn(false)
- updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
- assertThat(lockoutMessage()).isNull()
- }
-
- @Test
- fun onFingerprintLockout_propagatesState() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val lockedOutMessage = collectLastValue(underTest.biometricLockedOutMessage)
- runCurrent()
-
- fingerprintRepository.setLockedOut(true)
-
- assertThat(lockedOutMessage())
- .isEqualTo(message(keyguard_enter_pin, R.string.kg_fp_locked_out))
-
- fingerprintRepository.setLockedOut(false)
- assertThat(lockedOutMessage()).isNull()
- }
-
- @Test
- fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() =
- testScope.runTest {
- whenever(systemPropertiesHelper.get("sys.boot.reason.last"))
- .thenReturn("reboot,mainline_update")
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, R.string.kg_prompt_after_update_pin),
- )
- }
-
- @Test
- fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to null,
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to null,
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- )
- }
-
- @Test
- fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- trustRepository.setCurrentUserTrustManaged(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
- Pair(keyguard_enter_pin, kg_trust_agent_disabled),
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
- Pair(keyguard_enter_pin, kg_trust_agent_disabled),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- @Test
- fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- @Test
- fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
-
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- private fun TestScope.verifyMessagesForAuthFlag(
- vararg authFlagToExpectedMessages: Pair<Int, Pair<Int, Int>?>
- ) {
- val authFlagsMessage = collectLastValue(underTest.authFlagsMessage)
-
- authFlagToExpectedMessages.forEach { (flag, messagePair) ->
- biometricSettingsRepository.setAuthenticationFlags(
- AuthenticationFlags(PRIMARY_USER_ID, flag)
- )
-
- assertThat(authFlagsMessage())
- .isEqualTo(messagePair?.let { message(it.first, it.second) })
- }
- }
-
- private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel {
- return BouncerMessageModel(
- message = Message(messageResId = primaryResId, animate = false),
- secondaryMessage = Message(messageResId = secondaryResId, animate = false)
- )
- }
- private fun message(value: String): BouncerMessageModel {
- return BouncerMessageModel(message = Message(message = value))
- }
-
- companion object {
- private const val PRIMARY_USER_ID = 0
- private val PRIMARY_USER =
- UserInfo(
- /* id= */ PRIMARY_USER_ID,
- /* name= */ "primary user",
- /* flags= */ UserInfo.FLAG_PRIMARY
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 86e0c75..77d8102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
@@ -353,6 +353,34 @@
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
+ @Test
+ fun hide_whenOnBouncerScene_hidesBouncerAndGoesToLockscreenScene() =
+ testScope.runTest {
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerSceneKey = currentScene?.key
+ assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
+
+ underTest.hide("")
+
+ assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
+ fun hide_whenNotOnBouncerScene_doesNothing() =
+ testScope.runTest {
+ sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "")
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val notBouncerSceneKey = currentScene?.key
+ assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
+
+ underTest.hide("")
+
+ assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
+ }
+
private fun assertTryAgainMessage(
message: String?,
time: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 4089abe..cc4eca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -17,187 +17,216 @@
package com.android.systemui.bouncer.domain.interactor
import android.content.pm.UserInfo
+import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown
-import com.android.systemui.R.string.kg_unlock_with_pin_or_fp
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
+import com.android.systemui.res.R.string.kg_trust_agent_disabled
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class BouncerMessageInteractorTest : SysuiTestCase() {
+ private val countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
+ private val repository = BouncerMessageRepositoryImpl()
+ private val userRepository = FakeUserRepository()
+ private val fakeTrustRepository = FakeTrustRepository()
+ private val fakeFacePropertyRepository = FakeFacePropertyRepository()
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val fakeDeviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository()
+ private val fakeDeviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository()
+ private val biometricSettingsRepository: FakeBiometricSettingsRepository =
+ FakeBiometricSettingsRepository()
+ @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var securityModel: KeyguardSecurityModel
- @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
@Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
- private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback>
- private lateinit var underTest: BouncerMessageInteractor
- private lateinit var repository: FakeBouncerMessageRepository
- private lateinit var userRepository: FakeUserRepository
+ @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var testScope: TestScope
- private lateinit var bouncerMessage: FlowValue<BouncerMessageModel?>
+ private lateinit var underTest: BouncerMessageInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- repository = FakeBouncerMessageRepository()
- userRepository = FakeUserRepository()
userRepository.setUserInfos(listOf(PRIMARY_USER))
testScope = TestScope()
- countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
- biometricSettingsRepository = FakeBiometricSettingsRepository()
-
allowTestableLooperAsMainThread()
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ overrideResource(kg_trust_agent_disabled, "Trust agent is unavailable")
}
suspend fun TestScope.init() {
userRepository.setSelectedUserInfo(PRIMARY_USER)
- val featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+ val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ Mockito.mock(BouncerView::class.java),
+ Mockito.mock(Handler::class.java),
+ Mockito.mock(KeyguardStateController::class.java),
+ Mockito.mock(KeyguardSecurityModel::class.java),
+ Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
+ Mockito.mock(FalsingCollector::class.java),
+ Mockito.mock(DismissCallbackRegistry::class.java),
+ context,
+ keyguardUpdateMonitor,
+ fakeTrustRepository,
+ testScope.backgroundScope,
+ )
underTest =
BouncerMessageInteractor(
repository = repository,
- factory = BouncerMessageFactory(biometricSettingsRepository, securityModel),
userRepository = userRepository,
countDownTimerUtil = countDownTimerUtil,
- featureFlags = featureFlags
+ featureFlags = featureFlags,
+ updateMonitor = updateMonitor,
+ biometricSettingsRepository = biometricSettingsRepository,
+ applicationScope = this.backgroundScope,
+ trustRepository = fakeTrustRepository,
+ systemPropertiesHelper = systemPropertiesHelper,
+ primaryBouncerInteractor = primaryBouncerInteractor,
+ facePropertyRepository = fakeFacePropertyRepository,
+ deviceEntryFingerprintAuthRepository = fakeDeviceEntryFingerprintAuthRepository,
+ faceAuthRepository = fakeDeviceEntryFaceAuthRepository,
+ securityModel = securityModel
)
- bouncerMessage = collectLastValue(underTest.bouncerMessage)
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+ bouncerRepository.setPrimaryShow(true)
+ runCurrent()
}
@Test
- fun onIncorrectSecurityInput_setsTheBouncerModelInTheRepository() =
+ fun onIncorrectSecurityInput_providesTheAppropriateValueForBouncerMessage() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthIncorrectAttempt()
- assertThat(repository.primaryAuthMessage).isNotNull()
- assertThat(
- context.resources.getString(
- repository.primaryAuthMessage.value!!.message!!.messageResId!!
- )
- )
- .isEqualTo("Wrong PIN. Try again.")
+ assertThat(bouncerMessage).isNotNull()
+ assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.")
}
@Test
fun onUserStartsPrimaryAuthInput_clearsAllSetBouncerMessages() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
+ underTest.onPrimaryAuthIncorrectAttempt()
+ assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.")
underTest.onPrimaryBouncerUserInput()
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
}
@Test
- fun onBouncerBeingHidden_clearsAllSetBouncerMessages() =
+ fun setCustomMessage_propagateValue() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
-
- underTest.onBouncerBeingHidden()
-
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
- }
-
- @Test
- fun setCustomMessage_setsRepositoryValue() =
- testScope.runTest {
- init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setCustomMessage("not empty")
- val customMessage = repository.customMessage
- assertThat(customMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setCustomMessage(null)
- assertThat(customMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun setFaceMessage_setsRepositoryValue() =
+ fun setFaceMessage_propagateValue() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setFaceAcquisitionMessage("not empty")
- val faceAcquisitionMessage = repository.faceAcquisitionMessage
-
- assertThat(faceAcquisitionMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message)
- .isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setFaceAcquisitionMessage(null)
- assertThat(faceAcquisitionMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun setFingerprintMessage_setsRepositoryValue() =
+ fun setFingerprintMessage_propagateValue() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setFingerprintAcquisitionMessage("not empty")
- val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage
-
- assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message)
- .isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setFingerprintAcquisitionMessage(null)
- assertThat(fingerprintAcquisitionMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
fun onPrimaryAuthLockout_startsTimerForSpecifiedNumberOfSeconds() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthLockedOut(3)
@@ -206,7 +235,7 @@
countDownTimerCallback.value.onTick(2000L)
- val primaryMessage = repository.primaryAuthMessage.value!!.message!!
+ val primaryMessage = bouncerMessage!!.message!!
assertThat(primaryMessage.messageResId!!)
.isEqualTo(kg_too_many_failed_attempts_countdown)
assertThat(primaryMessage.formatterArgs).isEqualTo(mapOf(Pair("count", 2)))
@@ -216,10 +245,7 @@
fun onPrimaryAuthLockout_timerComplete_resetsRepositoryMessages() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthLockedOut(3)
@@ -228,59 +254,269 @@
countDownTimerCallback.value.onFinish()
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun bouncerMessage_hasPriorityOrderOfMessages() =
+ fun onFaceLockout_propagatesState() =
testScope.runTest {
init()
- repository.setBiometricAuthMessage(message("biometric message"))
- repository.setFaceAcquisitionMessage(message("face acquisition message"))
- repository.setFingerprintAcquisitionMessage(message("fingerprint acquisition message"))
- repository.setPrimaryAuthMessage(message("primary auth message"))
- repository.setAuthFlagsMessage(message("auth flags message"))
- repository.setBiometricLockedOutMessage(message("biometrics locked out"))
- repository.setCustomMessage(message("custom message"))
+ val lockoutMessage by collectLastValue(underTest.bouncerMessage)
- assertThat(bouncerMessage()).isEqualTo(message("primary auth message"))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+ runCurrent()
- repository.setPrimaryAuthMessage(null)
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(secondaryResMessage(lockoutMessage))
+ .isEqualTo("Can’t unlock with face. Too many attempts.")
- assertThat(bouncerMessage()).isEqualTo(message("biometric message"))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(false)
+ runCurrent()
- repository.setBiometricAuthMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("fingerprint acquisition message"))
-
- repository.setFingerprintAcquisitionMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("face acquisition message"))
-
- repository.setFaceAcquisitionMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("custom message"))
-
- repository.setCustomMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("auth flags message"))
-
- repository.setAuthFlagsMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("biometrics locked out"))
-
- repository.setBiometricLockedOutMessage(null)
-
- // sets the default message if everything else is null
- assertThat(bouncerMessage()!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockoutMessage?.secondaryMessage?.message).isNull()
}
- private fun message(value: String): BouncerMessageModel {
- return BouncerMessageModel(message = Message(message = value))
+ @Test
+ fun onFaceLockout_whenItIsClass3_propagatesState() =
+ testScope.runTest {
+ init()
+ val lockoutMessage by collectLastValue(underTest.bouncerMessage)
+ fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockoutMessage)).isEqualTo("Enter PIN")
+ assertThat(secondaryResMessage(lockoutMessage))
+ .isEqualTo("PIN is required after too many attempts")
+
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(false)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockoutMessage?.secondaryMessage?.message).isNull()
+ }
+
+ @Test
+ fun onFingerprintLockout_propagatesState() =
+ testScope.runTest {
+ init()
+ val lockedOutMessage by collectLastValue(underTest.bouncerMessage)
+
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockedOutMessage)).isEqualTo("Enter PIN")
+ assertThat(secondaryResMessage(lockedOutMessage))
+ .isEqualTo("PIN is required after too many attempts")
+
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockedOutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockedOutMessage?.secondaryMessage?.message).isNull()
+ }
+
+ @Test
+ fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() =
+ testScope.runTest {
+ init()
+ whenever(systemPropertiesHelper.get("sys.boot.reason.last"))
+ .thenReturn("reboot,mainline_update")
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "Device updated. Enter PIN to continue.")
+ )
+ }
+
+ @Test
+ fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
+ testScope.runTest {
+ init()
+ fakeTrustRepository.setTrustUsuallyManaged(false)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy")
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ fakeTrustRepository.setCurrentUserTrustManaged(true)
+ fakeTrustRepository.setTrustUsuallyManaged(true)
+
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ Pair("Enter PIN", "Trust agent is unavailable"),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ Pair("Enter PIN", "Trust agent is unavailable"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ fakeTrustRepository.setTrustUsuallyManaged(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ fakeTrustRepository.setCurrentUserTrustManaged(false)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to
+ Pair("Unlock with PIN or fingerprint", null)
+ )
+
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ Pair("Enter PIN", null),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ Pair("Enter PIN", null),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ private fun primaryResMessage(bouncerMessage: BouncerMessageModel?) =
+ resString(bouncerMessage?.message?.messageResId)
+
+ private fun secondaryResMessage(bouncerMessage: BouncerMessageModel?) =
+ resString(bouncerMessage?.secondaryMessage?.messageResId)
+
+ private fun resString(msgResId: Int?): String? =
+ msgResId?.let { context.resources.getString(it) }
+
+ private fun TestScope.verifyMessagesForAuthFlag(
+ vararg authFlagToExpectedMessages: Pair<Int, Pair<String, String?>>
+ ) {
+ val authFlagsMessage by collectLastValue(underTest.bouncerMessage)
+
+ authFlagToExpectedMessages.forEach { (flag, messagePair) ->
+ biometricSettingsRepository.setAuthenticationFlags(
+ AuthenticationFlags(PRIMARY_USER_ID, flag)
+ )
+ runCurrent()
+
+ assertThat(primaryResMessage(authFlagsMessage)).isEqualTo(messagePair.first)
+ if (messagePair.second == null) {
+ assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isEqualTo(0)
+ assertThat(authFlagsMessage?.secondaryMessage?.message).isNull()
+ } else {
+ assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isNotEqualTo(0)
+ assertThat(secondaryResMessage(authFlagsMessage)).isEqualTo(messagePair.second)
+ }
+ }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 420fdba..9a5b4585 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -25,7 +25,7 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.DejankUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -34,8 +34,6 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.ActivityStarter
@@ -76,7 +74,6 @@
private lateinit var underTest: PrimaryBouncerInteractor
private lateinit var resources: TestableResources
private lateinit var trustRepository: FakeTrustRepository
- private lateinit var featureFlags: FakeFeatureFlags
private lateinit var testScope: TestScope
@Before
@@ -89,7 +86,6 @@
testScope = TestScope()
mainHandler = FakeHandler(android.os.Looper.getMainLooper())
trustRepository = FakeTrustRepository()
- featureFlags = FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) }
underTest =
PrimaryBouncerInteractor(
repository,
@@ -103,7 +99,6 @@
context,
keyguardUpdateMonitor,
trustRepository,
- featureFlags,
testScope.backgroundScope,
)
whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index 665456d..cb0b74f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -27,8 +27,6 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -77,7 +75,6 @@
context,
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
- FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
TestScope().backgroundScope,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 7af8a04..9011c2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -22,6 +22,8 @@
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
@@ -39,6 +41,7 @@
utils.authenticationInteractor(
utils.authenticationRepository(),
)
+ private val sceneInteractor = utils.sceneInteractor()
private val underTest =
PinBouncerViewModel(
applicationContext = context,
@@ -46,7 +49,7 @@
interactor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
+ sceneInteractor = sceneInteractor,
),
isInputEnabled = MutableStateFlow(true),
)
@@ -75,4 +78,22 @@
underTest.onAuthenticateButtonClicked()
assertThat(animateFailure).isFalse()
}
+
+ @Test
+ fun onImeVisibilityChanged() =
+ testScope.runTest {
+ val desiredScene by collectLastValue(sceneInteractor.desiredScene)
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
+ assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+
+ underTest.onImeVisibilityChanged(false)
+ assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+
+ underTest.onImeVisibilityChanged(true)
+ assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+
+ underTest.onImeVisibilityChanged(false)
+ assertThat(desiredScene?.key).isEqualTo(SceneKey.Lockscreen)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index d4bba72..333bd21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -31,8 +31,6 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -85,7 +83,6 @@
context,
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
- FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
TestScope().backgroundScope,
)
underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 12090e5..3375184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
@@ -157,6 +157,29 @@
}
@Test
+ fun onAuthenticateKeyPressed_whenEmpty() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val message by collectLastValue(bouncerViewModel.message)
+ val password by collectLastValue(underTest.password)
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ utils.authenticationRepository.setUnlocked(false)
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+ // Enter nothing.
+
+ underTest.onAuthenticateKeyPressed()
+
+ assertThat(password).isEqualTo("")
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ }
+
+ @Test
fun onAuthenticateKeyPressed_correctAfterWrong() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 8ce738c..102cfe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index a684221..35238ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 5a5c058..11756d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -24,7 +24,7 @@
import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
index 673b5eb..db85522 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
@@ -39,7 +39,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
index 662c89c..fb07e6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
@@ -28,7 +28,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
index f0006e5..98f7f59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index d28f530..ddf788e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import com.android.systemui.coroutines.collectLastValue
@@ -44,6 +45,7 @@
private lateinit var testScope: TestScope
+ private lateinit var communalRepository: FakeCommunalRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var interactor: CommunalInteractor
@@ -52,12 +54,13 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
+ communalRepository = FakeCommunalRepository()
widgetRepository = FakeCommunalWidgetRepository()
- interactor = CommunalInteractor(widgetRepository)
+ interactor = CommunalInteractor(communalRepository, widgetRepository)
}
@Test
- fun testAppWidgetInfoFlow() =
+ fun appWidgetInfoFlow() =
testScope.runTest {
val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
runCurrent()
@@ -67,4 +70,22 @@
runCurrent()
assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
}
+
+ @Test
+ fun communalEnabled() =
+ testScope.runTest {
+ communalRepository.setIsCommunalEnabled(true)
+
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ assertThat(interactor.isCommunalEnabled).isTrue()
+ }
+
+ @Test
+ fun communalDisabled() =
+ testScope.runTest {
+ communalRepository.setIsCommunalEnabled(false)
+
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ assertThat(interactor.isCommunalEnabled).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
index 69d8d0b..a78f0b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
@@ -28,7 +28,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.ComplicationLayoutEngine.Margins;
import com.android.systemui.touch.TouchInsetManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
index bd4e8da..581e88b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
@@ -18,7 +18,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index bd3d09d..2a4524b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -11,7 +11,7 @@
import android.window.OnBackInvokedDispatcher
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.controls.CustomIconCache
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 70d93a1..88d36af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -13,7 +13,7 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.controls.ControlStatus
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 74d0d21..6361e94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -31,7 +31,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
index 756f267..4e8f866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -20,7 +20,7 @@
import android.content.DialogInterface
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index 7ac1953..4828ba3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -20,7 +20,7 @@
import android.content.SharedPreferences
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 2ae342a..101b8ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -30,7 +30,7 @@
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlInfo
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
index 1e8cd41..8eebcee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
@@ -19,7 +19,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.FakeSystemUIDialogController
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
index df6fa11..48e3962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
@@ -26,7 +26,7 @@
import android.widget.PopupWindow.OnDismissListener
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index a400ff9..5a4ad01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -33,7 +33,7 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsServiceInfo
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
index 588e34d..ac1f90c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
@@ -11,7 +11,7 @@
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlInfo
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
index 8bf17d7..a1cffc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -23,7 +23,7 @@
import android.view.Surface
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import org.junit.Assert
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
index 171b767..e4ddc37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -20,7 +20,7 @@
import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import org.junit.Assert
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
index 8f0b193..d1d4880 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -20,7 +20,7 @@
import android.util.Size
import android.view.DisplayCutout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import org.junit.Assert
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index 4feba7b..2bff7d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -23,7 +23,7 @@
import androidx.annotation.DrawableRes
import androidx.test.filters.SmallTest
import com.android.internal.R as InternalR
-import com.android.systemui.R as SystemUIR
+import com.android.systemui.res.R as SystemUIR
import com.android.systemui.SysuiTestCase
import com.android.systemui.tests.R
import org.junit.Assert.assertEquals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 3a0883b..511562f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
@@ -62,6 +63,7 @@
@Before
fun setup() {
setDisplays(emptyList())
+ setAllDisplaysIncludingDisabled()
displayRepository =
DisplayRepositoryImpl(
displayManager,
@@ -70,6 +72,7 @@
UnconfinedTestDispatcher()
)
verify(displayManager, never()).registerDisplayListener(any(), any())
+ verify(displayManager, never()).getDisplays(any())
}
@Test
@@ -351,6 +354,22 @@
}
@Test
+ fun initialState_onePendingDisplayOnBoot_notNull() =
+ testScope.runTest {
+ // 1 is not enabled, but just connected. It should be seen as pending
+ setAllDisplaysIncludingDisabled(0, 1)
+ setDisplays(0) // 0 is enabled.
+ verify(displayManager, never()).getDisplays(any())
+
+ val pendingDisplay by collectLastValue(displayRepository.pendingDisplay)
+
+ verify(displayManager).getDisplays(any())
+
+ assertThat(pendingDisplay).isNotNull()
+ assertThat(pendingDisplay!!.id).isEqualTo(1)
+ }
+
+ @Test
fun onPendingDisplay_internalDisplay_ignored() =
testScope.runTest {
val pendingDisplay by lastPendingDisplay()
@@ -365,7 +384,7 @@
testScope.runTest {
val pendingDisplay by lastPendingDisplay()
- sendOnDisplayConnected(1, Display.TYPE_EXTERNAL)
+ sendOnDisplayConnected(1, TYPE_EXTERNAL)
sendOnDisplayConnected(2, Display.TYPE_INTERNAL)
assertThat(pendingDisplay!!.id).isEqualTo(1)
@@ -416,7 +435,7 @@
whenever(displayManager.getDisplay(eq(id))).thenReturn(null)
}
- private fun sendOnDisplayConnected(id: Int, displayType: Int = Display.TYPE_EXTERNAL) {
+ private fun sendOnDisplayConnected(id: Int, displayType: Int = TYPE_EXTERNAL) {
val mockDisplay = display(id = id, type = displayType)
whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay)
connectedDisplayListener.value.onDisplayConnected(id)
@@ -424,15 +443,25 @@
private fun setDisplays(displays: List<Display>) {
whenever(displayManager.displays).thenReturn(displays.toTypedArray())
+ displays.forEach { display ->
+ whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+ }
+ }
+
+ private fun setAllDisplaysIncludingDisabled(vararg ids: Int) {
+ val displays = ids.map { display(type = TYPE_EXTERNAL, id = it) }.toTypedArray()
+ whenever(
+ displayManager.getDisplays(
+ eq(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ )
+ )
+ .thenReturn(displays)
+ displays.forEach { display ->
+ whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+ }
}
private fun setDisplays(vararg ids: Int) {
- setDisplays(ids.map { display(it) })
- }
-
- private fun display(id: Int): Display {
- return mock<Display>().also { mockDisplay ->
- whenever(mockDisplay.displayId).thenReturn(id)
- }
+ setDisplays(ids.map { display(type = TYPE_EXTERNAL, id = it) })
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
index 46f7582..dcc15ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
@@ -20,7 +20,7 @@
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
index ec9acdf..ea7467f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index af2dab5..9566e81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -46,7 +46,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.log.LogBuffer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
index 16d665c..f89a0b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.leak.RotationUtils;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index ea47859..5a97b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -28,7 +28,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.leak.RotationUtils;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
index 74e8cc2..a003e07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
index 8f66344..76c3349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -12,7 +12,7 @@
import android.util.Size
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import java.io.ByteArrayInputStream
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/FakeSliderEventProducer.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/FakeSliderEventProducer.kt
new file mode 100644
index 0000000..9deabc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/FakeSliderEventProducer.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Fake implementation of a slider event producer */
+class FakeSliderEventProducer : SliderEventProducer {
+
+ private val _currentEvent = MutableStateFlow(SliderEvent(SliderEventType.NOTHING, 0f))
+
+ fun sendEvent(event: SliderEvent) {
+ _currentEvent.value = event
+ }
+ override fun produceEvents(): Flow<SliderEvent> = _currentEvent.asStateFlow()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
new file mode 100644
index 0000000..add601c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -0,0 +1,547 @@
+/*
+ * 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.haptics.slider
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SeekableSliderTrackerTest : SysuiTestCase() {
+
+ @Mock private lateinit var sliderStateListener: SliderStateListener
+ private val sliderEventProducer = FakeSliderEventProducer()
+ private lateinit var mSeekableSliderTracker: SeekableSliderTracker
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun initializeSliderTracker_startsTracking() = runTest {
+ // GIVEN Initialized tracker
+ initTracker(testScheduler)
+
+ // THEN the tracker job is active
+ assertThat(mSeekableSliderTracker.isTracking).isTrue()
+ }
+
+ @Test
+ fun stopTracking_onAnyState_resetsToIdle() = runTest {
+ enumValues<SliderState>().forEach {
+ // GIVEN Initialized tracker
+ initTracker(testScheduler)
+
+ // GIVEN a state in the state machine
+ mSeekableSliderTracker.setState(it)
+
+ // WHEN the tracker stops tracking the state and listening to events
+ mSeekableSliderTracker.stopTracking()
+
+ // THEN The state is idle and the tracker is not active
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ assertThat(mSeekableSliderTracker.isTracking).isFalse()
+ }
+ }
+
+ // Tests on the IDLE state
+ @Test
+ fun initializeSliderTracker_isIdle() = runTest {
+ // GIVEN Initialized tracker
+ initTracker(testScheduler)
+
+ // THEN The state is idle and the listener is not called to play haptics
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun startsTrackingTouch_onIdle_entersWaitState() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a start of tracking touch event
+ val progress = 0f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // THEN the tracker moves to the wait state and the timer job begins
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+ verifyZeroInteractions(sliderStateListener)
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+ }
+
+ // Tests on the WAIT state
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun waitCompletes_onWait_movesToHandleAcquired() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT
+ val progress = 0f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // WHEN the wait time completes plus a small buffer time
+ advanceTimeBy(config.waitTimeMillis + 10L)
+
+ // THEN the tracker moves to the DRAG_HANDLE_ACQUIRED_BY_TOUCH state
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ verify(sliderStateListener).onHandleAcquiredByTouch()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun impreciseTouch_onWait_movesToHandleAcquired() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
+ // slider
+ var progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // GIVEN a progress event due to an imprecise touch with a progress below threshold
+ progress += (config.jumpThreshold - 0.01f)
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_ACQUIRED_BY_TOUCH state without the timer job
+ // being complete
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ verify(sliderStateListener).onHandleAcquiredByTouch()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun trackJump_onWait_movesToJumpTrackLocationSelected() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
+ // slider
+ var progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // GIVEN a progress event due to a touch on the slider track at threshold
+ progress += config.jumpThreshold
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the jump-track location selected state
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.JUMP_TRACK_LOCATION_SELECTED)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ verify(sliderStateListener).onProgressJump(anyFloat())
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun upperBookendSelection_onWait_movesToBookendSelected() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
+ // slider
+ var progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // GIVEN a progress event due to a touch on the slider upper bookend zone.
+ progress = (config.upperBookendThreshold + 0.01f)
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the jump-track location selected state
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.JUMP_BOOKEND_SELECTED)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ verify(sliderStateListener).onUpperBookend()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun lowerBookendSelection_onWait_movesToBookendSelected() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
+ // slider
+ var progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // GIVEN a progress event due to a touch on the slider lower bookend zone
+ progress = (config.lowerBookendThreshold - 0.01f)
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the JUMP_TRACK_LOCATION_SELECTED state
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.JUMP_BOOKEND_SELECTED)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ verify(sliderStateListener).onLowerBookend()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun stopTracking_onWait_whenWaitingJobIsActive_resetsToIdle() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
+ // slider
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, 0.5f))
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+
+ // GIVEN that the tracker stops tracking the state and listening to events
+ mSeekableSliderTracker.stopTracking()
+
+ // THEN the tracker moves to the IDLE state without the timer job being complete
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ assertThat(mSeekableSliderTracker.isWaiting).isFalse()
+ assertThat(mSeekableSliderTracker.isTracking).isFalse()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ // Tests on the JUMP_TRACK_LOCATION_SELECTED state
+
+ @Test
+ fun progressChangeByUser_onJumpTrackLocationSelected_movesToDragHandleDragging() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a JUMP_TRACK_LOCATION_SELECTED state
+ mSeekableSliderTracker.setState(SliderState.JUMP_TRACK_LOCATION_SELECTED)
+
+ // GIVEN a progress event due to dragging the handle
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 0.5f))
+
+ // THEN the tracker moves to the DRAG_HANDLE_DRAGGING state
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verify(sliderStateListener).onProgress(anyFloat())
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun touchRelease_onJumpTrackLocationSelected_movesToIdle() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a JUMP_TRACK_LOCATION_SELECTED state
+ mSeekableSliderTracker.setState(SliderState.JUMP_TRACK_LOCATION_SELECTED)
+
+ // GIVEN that the slider stopped tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5f))
+
+ // THEN the tracker executes on onHandleReleasedFromTouch before moving to the IDLE state
+ verify(sliderStateListener).onHandleReleasedFromTouch()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_onJumpBookendSelected_movesToDragHandleDragging() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a JUMP_BOOKEND_SELECTED state
+ mSeekableSliderTracker.setState(SliderState.JUMP_BOOKEND_SELECTED)
+
+ // GIVEN that the slider stopped tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 0.5f))
+
+ // THEN the tracker moves to the DRAG_HANDLE_DRAGGING state
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verify(sliderStateListener).onProgress(anyFloat())
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun touchRelease_onJumpBookendSelected_movesToIdle() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a JUMP_BOOKEND_SELECTED state
+ mSeekableSliderTracker.setState(SliderState.JUMP_BOOKEND_SELECTED)
+
+ // GIVEN that the slider stopped tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5f))
+
+ // THEN the tracker executes on onHandleReleasedFromTouch before moving to the IDLE state
+ verify(sliderStateListener).onHandleReleasedFromTouch()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ // Tests on the DRAG_HANDLE_ACQUIRED state
+
+ @Test
+ fun progressChangeByUser_onHandleAcquired_movesToDragHandleDragging() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a DRAG_HANDLE_ACQUIRED_BY_TOUCH state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+
+ // GIVEN a progress change by the user
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_DRAGGING state
+ verify(sliderStateListener).onProgress(progress)
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun touchRelease_onHandleAcquired_movesToIdle() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a DRAG_HANDLE_ACQUIRED_BY_TOUCH state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
+
+ // GIVEN that the handle stops tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5f))
+
+ // THEN the tracker executes on onHandleReleasedFromTouch before moving to the IDLE state
+ verify(sliderStateListener).onHandleReleasedFromTouch()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ // Tests on DRAG_HANDLE_DRAGGING
+
+ @Test
+ fun progressChangeByUser_onHandleDragging_progressOutsideOfBookends_doesNotChangeState() =
+ runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a DRAG_HANDLE_DRAGGING state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
+
+ // GIVEN a progress change by the user outside of bookend bounds
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker does not change state and executes the onProgress call
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verify(sliderStateListener).onProgress(progress)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_onHandleDragging_reachesLowerBookend_movesToHandleReachedBookend() =
+ runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_DRAGGING state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
+
+ // GIVEN a progress change by the user reaching the lower bookend
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_REACHED_BOOKEND state and executes the
+ // corresponding callback
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+ verify(sliderStateListener).onLowerBookend()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_onHandleDragging_reachesUpperBookend_movesToHandleReachedBookend() =
+ runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_DRAGGING state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
+
+ // GIVEN a progress change by the user reaching the upper bookend
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_REACHED_BOOKEND state and executes the
+ // corresponding callback
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+ verify(sliderStateListener).onUpperBookend()
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun touchRelease_onHandleDragging_movesToIdle() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a DRAG_HANDLE_DRAGGING state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
+
+ // GIVEN that the slider stops tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5f))
+
+ // THEN the tracker executes on onHandleReleasedFromTouch before moving to the IDLE state
+ verify(sliderStateListener).onHandleReleasedFromTouch()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ // Tests on the DRAG_HANDLE_REACHED_BOOKEND state
+
+ @Test
+ fun progressChangeByUser_outsideOfBookendRange_onLowerBookend_movesToDragHandleDragging() =
+ runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+
+ // GIVEN a progress event that falls outside of the lower bookend range
+ val progress = config.lowerBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_DRAGGING state and executes accordingly
+ verify(sliderStateListener).onProgress(progress)
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_insideOfBookendRange_onLowerBookend_doesNotChangeState() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+
+ // GIVEN a progress event that falls inside of the lower bookend range
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker stays in the current state and executes accordingly
+ verify(sliderStateListener).onLowerBookend()
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_outsideOfBookendRange_onUpperBookend_movesToDragHandleDragging() =
+ runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+
+ // GIVEN a progress event that falls outside of the upper bookend range
+ val progress = config.upperBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker moves to the DRAG_HANDLE_DRAGGING state and executes accordingly
+ verify(sliderStateListener).onProgress(progress)
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_DRAGGING)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun progressChangeByUser_insideOfBookendRange_onUpperBookend_doesNotChangeState() = runTest {
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+
+ // GIVEN a progress event that falls inside of the upper bookend range
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
+ )
+
+ // THEN the tracker stays in the current state and executes accordingly
+ verify(sliderStateListener).onUpperBookend()
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun touchRelease_onHandleReachedBookend_movesToIdle() = runTest {
+ initTracker(testScheduler)
+
+ // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
+ mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
+
+ // GIVEN that the handle stops tracking touch
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5f))
+
+ // THEN the tracker executes on onHandleReleasedFromTouch before moving to the IDLE state
+ verify(sliderStateListener).onHandleReleasedFromTouch()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyNoMoreInteractions(sliderStateListener)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun initTracker(
+ scheduler: TestCoroutineScheduler,
+ config: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
+ ) {
+ mSeekableSliderTracker =
+ SeekableSliderTracker(
+ sliderStateListener,
+ sliderEventProducer,
+ UnconfinedTestDispatcher(scheduler),
+ config
+ )
+ mSeekableSliderTracker.startTracking()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
new file mode 100644
index 0000000..0ee348e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -0,0 +1,247 @@
+/*
+ * 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.haptics.slider
+
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.view.VelocityTracker
+import android.view.animation.AccelerateInterpolator
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SliderHapticFeedbackProviderTest : SysuiTestCase() {
+
+ @Mock private lateinit var velocityTracker: VelocityTracker
+ @Mock private lateinit var vibratorHelper: VibratorHelper
+
+ private val config = SliderHapticFeedbackConfig()
+ private val clock = FakeSystemClock()
+
+ private val lowTickDuration = 12 // Mocked duration of a low tick
+ private val dragTextureThresholdMillis =
+ lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval
+ private val progressInterpolator = AccelerateInterpolator(config.progressInterpolatorFactor)
+ private val velocityInterpolator = AccelerateInterpolator(config.velocityInterpolatorFactor)
+ private lateinit var sliderHapticFeedbackProvider: SliderHapticFeedbackProvider
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(vibratorHelper.getPrimitiveDurations(any()))
+ .thenReturn(intArrayOf(lowTickDuration))
+ whenever(velocityTracker.xVelocity).thenReturn(config.maxVelocityToScale)
+ sliderHapticFeedbackProvider =
+ SliderHapticFeedbackProvider(vibratorHelper, velocityTracker, config, clock)
+ }
+
+ @Test
+ fun playHapticAtLowerBookend_playsClick() {
+ val vibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ sliderHapticFeedbackProvider.onLowerBookend()
+
+ verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtLowerBookend_twoTimes_playsClickOnlyOnce() {
+ val vibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ sliderHapticFeedbackProvider.onLowerBookend()
+ sliderHapticFeedbackProvider.onLowerBookend()
+
+ verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtUpperBookend_playsClick() {
+ val vibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ sliderHapticFeedbackProvider.onUpperBookend()
+
+ verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtUpperBookend_twoTimes_playsClickOnlyOnce() {
+ val vibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ sliderHapticFeedbackProvider.onUpperBookend()
+ sliderHapticFeedbackProvider.onUpperBookend()
+
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(vibration), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() {
+ // GIVEN max velocity and slider progress
+ val progress = 1f
+ val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
+ }
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN two calls to play occur immediately
+ sliderHapticFeedbackProvider.onProgress(progress)
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // THEN the correct composition only plays once
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
+ // GIVEN max velocity and slider progress
+ val progress = 1f
+ val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
+ }
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN two calls to play occur with the required threshold separation
+ sliderHapticFeedbackProvider.onProgress(progress)
+ clock.advanceTime(dragTextureThresholdMillis.toLong())
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // THEN the correct composition plays two times
+ verify(vibratorHelper, times(2))
+ .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() {
+ // GIVEN max velocity and slider progress
+ val progress = 1f
+ val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
+ }
+ val bookendVibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ // GIVEN a vibration at the lower bookend followed by a request to vibrate at progress
+ sliderHapticFeedbackProvider.onLowerBookend()
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // WHEN a vibration is to trigger again at the lower bookend
+ sliderHapticFeedbackProvider.onLowerBookend()
+
+ // THEN there are two bookend vibrations
+ verify(vibratorHelper, times(2))
+ .vibrate(eq(bookendVibration), any(VibrationAttributes::class.java))
+ }
+
+ @Test
+ fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() {
+ // GIVEN max velocity and slider progress
+ val progress = 1f
+ val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
+ }
+ val bookendVibration =
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ scaleAtBookends(config.maxVelocityToScale)
+ )
+ .compose()
+
+ // GIVEN a vibration at the upper bookend followed by a request to vibrate at progress
+ sliderHapticFeedbackProvider.onUpperBookend()
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // WHEN a vibration is to trigger again at the upper bookend
+ sliderHapticFeedbackProvider.onUpperBookend()
+
+ // THEN there are two bookend vibrations
+ verify(vibratorHelper, times(2))
+ .vibrate(eq(bookendVibration), any(VibrationAttributes::class.java))
+ }
+
+ private fun scaleAtBookends(velocity: Float): Float {
+ val range = config.upperBookendScale - config.lowerBookendScale
+ val interpolatedVelocity =
+ velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale)
+ return interpolatedVelocity * range + config.lowerBookendScale
+ }
+
+ private fun scaleAtProgressChange(velocity: Float, progress: Float): Float {
+ val range = config.progressBasedDragMaxScale - config.progressBasedDragMinScale
+ val interpolatedVelocity =
+ velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale)
+ val interpolatedProgress = progressInterpolator.getInterpolation(progress)
+ val bump = interpolatedVelocity * config.additionalVelocityMaxBump
+ return interpolatedProgress * range + config.progressBasedDragMinScale + bump
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index f62137c..6c990e45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -30,7 +30,7 @@
import android.view.SurfaceControlViewHost
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
@@ -172,7 +172,6 @@
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
- set(Flags.REVAMPED_WALLPAPER_UI, true)
set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
set(Flags.FACE_AUTH_REFACTOR, true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index f78d051..9d96ac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -186,10 +186,10 @@
private @Mock AuthController mAuthController;
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
private @Mock ShadeWindowLogger mShadeWindowLogger;
- private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
- mKeyguardUpdateMonitorCallbackCaptor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
+ private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
+ mKeyguardUpdateMonitorCallbackCaptor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -313,6 +313,26 @@
}
@Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
+ // GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
+ mViewMediator.onSystemReady();
+ mViewMediator.setKeyguardEnabled(true);
+ TestableLooper.get(this).processAllMessages();
+ captureKeyguardUpdateMonitorCallback();
+ when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true);
+ mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0);
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+
+ // WHEN keyguard is externally not enabled anymore
+ mViewMediator.setKeyguardEnabled(false);
+
+ // THEN keyguard is NOT dismissed; it continues to show
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
verify(mUpdateMonitor).dispatchKeyguardGoingAway(false);
@@ -1120,11 +1140,11 @@
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
}
- private void captureKeyguardUpdateMonitorCallback() {
- verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
- }
-
private void captureKeyguardStateControllerCallback() {
verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
}
+
+ private void captureKeyguardUpdateMonitorCallback() {
+ verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index d84a4f7..49168d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -25,7 +25,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.EnableZenModeDialog
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index b6dffff..c3e28ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -20,7 +20,7 @@
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 1815ea9..477f455 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -19,7 +19,7 @@
import android.app.Activity
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
@@ -176,7 +176,7 @@
!isFeatureEnabled ->
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice::class
.java
- hasServiceInfos && hasFavorites ->
+ hasServiceInfos && (hasFavorites || hasPanels) ->
KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java
else -> KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 2fd4947..ef56a98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
index 7941a23..bd4525b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
@@ -19,7 +19,7 @@
import android.content.Intent
import android.net.Uri
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index de3bb6f..4f071bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -22,7 +22,7 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index edaff44..bd0b71d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -22,7 +22,7 @@
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index d36e778..613c4ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -20,9 +20,10 @@
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -38,6 +39,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -92,6 +94,40 @@
}
@Test
+ fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ job.cancel()
+ }
+
+ @Test
+ fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
setUpState(isWalletFeatureAvailable = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -187,6 +223,7 @@
isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
hasSelectedCard: Boolean = true,
+ cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
) {
val walletClient: QuickAccessWalletClient = mock()
whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -202,7 +239,19 @@
if (isWalletQuerySuccessful) {
onWalletCardsRetrieved(
if (hasSelectedCard) {
- GetWalletCardsResponse(listOf(mock()), 0)
+ GetWalletCardsResponse(
+ listOf(
+ WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ ),
+ 0
+ )
} else {
GetWalletCardsResponse(emptyList(), 0)
}
@@ -216,5 +265,7 @@
companion object {
private val ICON: Drawable = mock()
+ private const val CARD_ID: String = "Id"
+ private const val CARD_DESCRIPTION: String = "Description"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index a6930d5..360fa56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -32,7 +32,7 @@
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index a76c885..5a95ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -25,6 +25,9 @@
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER
import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorProperties
import android.hardware.face.FaceSensorPropertiesInternal
@@ -36,9 +39,10 @@
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -101,7 +105,6 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
@@ -132,6 +135,7 @@
@Captor
private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
+ @Captor private lateinit var faceAuthenticateOptions: ArgumentCaptor<FaceAuthenticateOptions>
@Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
private lateinit var bypassStateChangedListener:
@@ -285,7 +289,7 @@
allPreconditionsToRunFaceAuthAreTrue()
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
@@ -318,12 +322,12 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
clearInvocations(uiEventLogger)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
verifyNoMoreInteractions(faceManager)
verifyNoMoreInteractions(uiEventLogger)
}
@@ -335,7 +339,7 @@
verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationError(
@@ -389,7 +393,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
var wasAuthCancelled = false
@@ -411,7 +415,7 @@
underTest = createDeviceEntryFaceAuthRepositoryImpl()
initCollectors()
- underTest.detect()
+ underTest.detect(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
faceDetectIsCalled()
detectionCallback.value.onFaceDetected(1, 1, true)
@@ -420,6 +424,8 @@
assertThat(status.sensorId).isEqualTo(1)
assertThat(status.userId).isEqualTo(1)
assertThat(status.isStrongBiometric).isEqualTo(true)
+ assertThat(faceAuthenticateOptions.value.authenticateReason)
+ .isEqualTo(AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED)
}
@Test
@@ -431,7 +437,7 @@
initCollectors()
clearInvocations(faceManager)
- underTest.detect()
+ underTest.detect(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
verify(faceManager, never())
.detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
@@ -443,7 +449,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
// Enter cancelling state
@@ -451,7 +457,7 @@
clearInvocations(faceManager)
// Auth is while cancelling.
- underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
// Auth is not started
verifyNoMoreInteractions(faceManager)
@@ -466,6 +472,8 @@
faceAuthenticateIsCalled()
uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ assertThat(faceAuthenticateOptions.value.authenticateReason)
+ .isEqualTo(AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
}
@Test
@@ -474,15 +482,17 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
clearInvocations(faceManager)
underTest.cancel()
advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
+ assertThat(faceAuthenticateOptions.value.authenticateReason)
+ .isEqualTo(AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER)
}
@Test
@@ -492,7 +502,7 @@
allPreconditionsToRunFaceAuthAreTrue()
val emittedValues by collectValues(underTest.authenticationStatus)
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
underTest.cancel()
advanceTimeBy(100)
underTest.cancel()
@@ -519,7 +529,7 @@
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
faceAuthenticateIsCalled()
authenticationCallback.value.onAuthenticationHelp(9, "help msg")
@@ -562,8 +572,26 @@
}
@Test
- fun authenticateDoesNotRunIfFaceAuthIsCurrentlyPaused() =
- testScope.runTest { testGatingCheckForFaceAuth { underTest.pauseFaceAuth() } }
+ fun authenticateDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ fakeUserRepository.setSelectedUserInfo(
+ primaryUser,
+ SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ fakeUserRepository.setSelectedUserInfo(
+ userInfo = primaryUser,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+ }
+ }
@Test
fun authenticateDoesNotRunIfKeyguardIsNotShowing() =
@@ -582,12 +610,6 @@
testScope.runTest { testGatingCheckForFaceAuth { underTest.setLockedOut(true) } }
@Test
- fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
- testScope.runTest {
- testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
- }
-
- @Test
fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
testScope.runTest {
testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
@@ -608,14 +630,6 @@
}
@Test
- fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() =
- testScope.runTest {
- testGatingCheckForFaceAuth {
- biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
- }
- }
-
- @Test
fun authenticateDoesNotRunWhenSecureCameraIsActive() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -672,7 +686,7 @@
// Flip one precondition to false.
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -693,7 +707,7 @@
trustRepository.setCurrentUserTrusted(true)
assertThat(canFaceAuthRun()).isFalse()
- underTest.authenticate(
+ underTest.requestAuthenticate(
FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
fallbackToDetection = true
)
@@ -884,10 +898,6 @@
}
@Test
- fun detectDoesNotRunWhenUserSwitchingInProgress() =
- testScope.runTest { testGatingCheckForDetect { underTest.pauseFaceAuth() } }
-
- @Test
fun detectDoesNotRunWhenKeyguardGoingAway() =
testScope.runTest {
testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
@@ -1075,6 +1085,28 @@
faceAuthenticateIsCalled()
}
+ @Test
+ fun queuedAuthOnlyRequestShouldNotBeProcessedIfOnlyDetectionCanBeRun() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ // This will prevent auth from running but not detection
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
+
+ runCurrent()
+ assertThat(canFaceAuthRun()).isFalse()
+
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
+ runCurrent()
+
+ faceDetectIsNotCalled()
+ faceAuthenticateIsNotCalled()
+
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+ faceAuthenticateIsCalled()
+ }
+
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
) {
@@ -1087,10 +1119,18 @@
// gating check doesn't allow face auth to run.
assertThat(underTest.canRunFaceAuth.value).isFalse()
+ // request face auth just before gating conditions become true, this ensures any race
+ // conditions won't prevent face auth from running
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
+ faceAuthenticateIsNotCalled()
+
// flip the gating check back on.
allPreconditionsToRunFaceAuthAreTrue()
+ assertThat(underTest.canRunFaceAuth.value).isTrue()
- triggerFaceAuth(false)
+ faceAuthenticateIsCalled()
+ assertThat(authRunning()).isTrue()
+ cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
// Flip gating check off
gatingCheckModifier()
@@ -1101,13 +1141,17 @@
clearInvocations(faceManager)
// Try auth again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+ runCurrent()
// Auth can't run again
faceAuthenticateIsNotCalled()
}
- private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+ private suspend fun TestScope.testGatingCheckForDetect(
+ gatingCheckModifier: suspend () -> Unit
+ ) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
@@ -1118,7 +1162,11 @@
assertThat(canFaceAuthRun()).isFalse()
// Trigger authenticate with detection fallback
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
+ runCurrent()
faceAuthenticateIsNotCalled()
faceDetectIsCalled()
@@ -1133,15 +1181,21 @@
clearInvocations(faceManager)
// Try to run detect again
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+ underTest.requestAuthenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
// Detect won't run because preconditions are not true anymore.
faceDetectIsNotCalled()
}
- private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+ private fun TestScope.triggerFaceAuth(fallbackToDetect: Boolean) {
assertThat(canFaceAuthRun()).isTrue()
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+ underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+
+ runCurrent()
+
faceAuthenticateIsCalled()
assertThat(authRunning()).isTrue()
cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
@@ -1150,7 +1204,6 @@
private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
verify(faceManager, atLeastOnce())
.addLockoutResetCallback(faceLockoutResetCallback.capture())
- underTest.resumeFaceAuth()
trustRepository.setCurrentUserTrusted(false)
keyguardRepository.setKeyguardGoingAway(false)
keyguardRepository.setWakefulnessModel(
@@ -1164,7 +1217,7 @@
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
biometricSettingsRepository.setIsUserInLockdown(false)
- fakeUserRepository.setSelectedUserInfo(primaryUser)
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
faceLockoutResetCallback.value.onLockoutReset(0)
bouncerRepository.setAlternateVisible(true)
keyguardRepository.setKeyguardShowing(true)
@@ -1187,27 +1240,33 @@
private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
- private fun faceDetectIsCalled() {
+ private fun TestScope.faceDetectIsCalled() {
+ runCurrent()
+
verify(faceManager)
.detectFace(
cancellationSignal.capture(),
detectionCallback.capture(),
- eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+ faceAuthenticateOptions.capture(),
)
}
- private fun faceAuthenticateIsCalled() {
+ private fun TestScope.faceAuthenticateIsCalled() {
+ runCurrent()
+
verify(faceManager)
.authenticate(
isNull(),
cancellationSignal.capture(),
authenticationCallback.capture(),
isNull(),
- eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+ faceAuthenticateOptions.capture(),
)
}
- private fun faceAuthenticateIsNotCalled() {
+ private fun TestScope.faceAuthenticateIsNotCalled() {
+ runCurrent()
+
verify(faceManager, never())
.authenticate(
isNull(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 6b5be58b..126b841 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -21,7 +21,7 @@
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 29d7500..7f784d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -260,4 +261,19 @@
listener.value.onIsActiveUnlockRunningChanged(true, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isTrue()
}
+
+ @Test
+ fun isTrustUsuallyManaged_providesTheValueForCurrentUser() =
+ testScope.runTest {
+ runCurrent()
+ val trustUsuallyManaged by collectLastValue(underTest.isCurrentUserTrustUsuallyManaged)
+ whenever(trustManager.isTrustUsuallyManaged(users[0].id)).thenReturn(true)
+ whenever(trustManager.isTrustUsuallyManaged(users[1].id)).thenReturn(false)
+
+ userRepository.setSelectedUserInfo(users[0])
+
+ assertThat(trustUsuallyManaged).isTrue()
+ userRepository.setSelectedUserInfo(users[1])
+ assertThat(trustUsuallyManaged).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index da70a9f..518b3cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -23,6 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceWakeUpTriggersConfig
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
@@ -42,17 +43,22 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -83,8 +89,10 @@
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fakeDeviceEntryFingerprintAuthRepository:
FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig
@Before
fun setup() {
@@ -108,6 +116,7 @@
fakeUserRepository = FakeUserRepository()
fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
facePropertyRepository = FakeFacePropertyRepository()
+ fakeKeyguardRepository = FakeKeyguardRepository()
underTest =
SystemUIKeyguardFaceAuthInteractor(
mContext,
@@ -126,7 +135,6 @@
context,
keyguardUpdateMonitor,
FakeTrustRepository(),
- FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
testScope.backgroundScope,
),
AlternateBouncerInteractor(
@@ -144,6 +152,8 @@
fakeDeviceEntryFingerprintAuthRepository,
fakeUserRepository,
facePropertyRepository,
+ fakeKeyguardRepository,
+ faceWakeUpTriggersConfig,
)
}
@@ -152,6 +162,18 @@
testScope.runTest {
underTest.start()
+ fakeKeyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ WakeSleepReason.LID,
+ WakeSleepReason.LID
+ )
+ )
+ whenever(
+ faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
+ )
+ .thenReturn(true)
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.OFF,
@@ -188,6 +210,18 @@
testScope.runTest {
underTest.start()
+ fakeKeyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ WakeSleepReason.LID,
+ WakeSleepReason.LID
+ )
+ )
+ whenever(
+ faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
+ )
+ .thenReturn(true)
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.AOD,
@@ -204,10 +238,51 @@
}
@Test
+ fun faceAuthIsNotRequestedWhenLockscreenBecomesVisibleDueToIgnoredWakeReasons() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeKeyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ WakeSleepReason.LIFT,
+ WakeSleepReason.POWER_BUTTON
+ )
+ )
+ whenever(
+ faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LIFT)
+ )
+ .thenReturn(false)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.DOZING,
+ KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
+ @Test
fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromDozingState() =
testScope.runTest {
underTest.start()
+ fakeKeyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ WakeSleepReason.LID,
+ WakeSleepReason.LID
+ )
+ )
+ whenever(
+ faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
+ )
+ .thenReturn(true)
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.DOZING,
@@ -224,23 +299,7 @@
}
@Test
- fun faceAuthIsPausedWhenUserSwitchingIsInProgress() =
- testScope.runTest {
- underTest.start()
-
- fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
- runCurrent()
- fakeUserRepository.setSelectedUserInfo(
- secondaryUser,
- SelectionStatus.SELECTION_IN_PROGRESS
- )
- runCurrent()
-
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
- }
-
- @Test
- fun faceAuthIsUnpausedWhenUserSwitchingIsInComplete() =
+ fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
testScope.runTest {
underTest.start()
@@ -251,7 +310,6 @@
SelectionStatus.SELECTION_IN_PROGRESS
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
bouncerRepository.setPrimaryShow(true)
// New user is not locked out.
@@ -262,7 +320,6 @@
)
runCurrent()
- assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
assertThat(faceAuthRepository.isLockedOut.value).isFalse()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 14cdf2f..90e217f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -217,6 +217,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0f),
+ isUserInputDriven = false,
)
runCurrent()
assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 0050d64..13025a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -21,7 +21,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -302,7 +302,6 @@
featureFlags =
FakeFeatureFlags().apply {
set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
- set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled)
},
broadcastDispatcher = fakeBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index e8542aad..8c13bb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,7 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d457605..f2636c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -39,11 +40,12 @@
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
@@ -56,6 +58,7 @@
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
@@ -66,6 +69,7 @@
* Class for testing user journeys through the interactors. They will all be activated during setup,
* to ensure the expected transitions are still triggered.
*/
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(JUnit4::class)
class KeyguardTransitionScenariosTest : SysuiTestCase() {
@@ -74,7 +78,7 @@
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var commandQueue: FakeCommandQueue
- private lateinit var shadeRepository: ShadeRepository
+ private lateinit var shadeRepository: FakeShadeRepository
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private lateinit var transitionInteractor: KeyguardTransitionInteractor
private lateinit var featureFlags: FakeFeatureFlags
@@ -1213,6 +1217,58 @@
coroutineContext.cancelChildren()
}
+ @Test
+ fun lockscreenToPrimaryBouncerDragging() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runTransition(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+ runCurrent()
+
+ // GIVEN the keyguard is showing locked
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ keyguardRepository.setKeyguardUnlocked(false)
+ runCurrent()
+ shadeRepository.setShadeModel(
+ ShadeModel(
+ expansionAmount = .9f,
+ isUserDragging = true,
+ )
+ )
+ runCurrent()
+
+ // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+ assertThat(info.animator).isNull() // dragging should be manually animated
+
+ // WHEN the user stops dragging and shade is back to expanded
+ clearInvocations(transitionRepository)
+ runTransition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+ shadeRepository.setShadeModel(
+ ShadeModel(
+ expansionAmount = 1f,
+ isUserDragging = false,
+ )
+ )
+ runCurrent()
+
+ // THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur
+ val info2 =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ assertThat(info2.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+ assertThat(info2.to).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info2.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
private fun startingToWake() =
WakefulnessModel(
WakefulnessState.STARTING_TO_WAKE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index b81a3d4..4736545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -100,11 +100,7 @@
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
configurationRepository = FakeConfigurationRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.FACE_AUTH_REFACTOR, false)
- set(Flags.DELAY_BOUNCER, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
trustRepository = FakeTrustRepository()
powerRepository = FakePowerRepository()
underTest =
@@ -138,7 +134,6 @@
context,
keyguardUpdateMonitor,
trustRepository,
- featureFlags,
testScope.backgroundScope,
),
AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 623c877..7a17435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -67,6 +67,7 @@
underTest.setLockscreenShown(true)
underTest.setAodVisible(true)
+ verify(activityTaskManagerService).setLockScreenShown(true, false)
verify(activityTaskManagerService).setLockScreenShown(true, true)
verifyNoMoreInteractions(activityTaskManagerService)
}
@@ -76,6 +77,7 @@
underTest.setLockscreenShown(true)
underTest.setAodVisible(true)
+ verify(activityTaskManagerService).setLockScreenShown(true, false)
verify(activityTaskManagerService).setLockScreenShown(true, true)
verifyNoMoreInteractions(activityTaskManagerService)
@@ -97,4 +99,12 @@
verifyNoMoreInteractions(activityTaskManagerService)
}
+
+ @Test
+ fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() {
+ underTest.setAodVisible(false)
+
+ verify(activityTaskManagerService).setLockScreenShown(true, false)
+ verifyNoMoreInteractions(activityTaskManagerService)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 681fce8..7f4755d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -23,8 +23,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -35,6 +33,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
@@ -60,12 +59,11 @@
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
+ @Mock private lateinit var defaultStatusBarViewSection: DefaultStatusBarSection
@Mock private lateinit var defaultNSSLSection: DefaultNotificationStackScrollLayoutSection
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
@Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection
- private val featureFlags = FakeFeatureFlags()
-
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -78,26 +76,17 @@
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultStatusBarViewSection,
defaultNSSLSection,
splitShadeGuidelines,
aodNotificationIconsSection,
- featureFlags,
)
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, false)
}
@Test
fun replaceViews() {
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(null, constraintLayout)
- underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
- }
-
- @Test
- fun replaceViews_lazyInflateFlagOn() {
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
- val constraintLayout = ConstraintLayout(context, null)
- underTest.replaceViews(null, constraintLayout)
underTest.sections.forEach { verify(it).addViews(constraintLayout) }
}
@@ -107,7 +96,6 @@
val someSection = mock(KeyguardSection::class.java)
whenever(prevBlueprint.sections)
.thenReturn(underTest.sections.minus(defaultLockIconSection).plus(someSection))
- featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(prevBlueprint, constraintLayout)
underTest.sections.minus(defaultLockIconSection).forEach {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 4e31af22..8b8c59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -21,7 +21,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
index 1c3b5e61..7495637 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 06bf7f0..32d28a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -22,7 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index b935e1d..1c6cc87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -22,7 +22,7 @@
import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index b30dc9c..1681cfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -42,14 +42,7 @@
repository = utils.authenticationRepository(),
)
- private val underTest =
- LockscreenSceneViewModel(
- authenticationInteractor = authenticationInteractor,
- longPress =
- KeyguardLongPressViewModel(
- interactor = mock(),
- ),
- )
+ private val underTest = createLockscreenSceneViewModel()
@Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -73,4 +66,34 @@
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
+
+ @Test
+ fun leftTransitionSceneKey_communalIsEnabled_communal() =
+ testScope.runTest {
+ utils.communalRepository.setIsCommunalEnabled(true)
+ val underTest = createLockscreenSceneViewModel()
+
+ assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+ }
+
+ @Test
+ fun leftTransitionSceneKey_communalIsDisabled_null() =
+ testScope.runTest {
+ utils.communalRepository.setIsCommunalEnabled(false)
+ val underTest = createLockscreenSceneViewModel()
+
+ assertThat(underTest.leftDestinationSceneKey).isNull()
+ }
+
+ private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
+ return LockscreenSceneViewModel(
+ applicationScope = testScope.backgroundScope,
+ authenticationInteractor = authenticationInteractor,
+ communalInteractor = utils.communalInteractor(),
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
+ ),
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index bd17de3..9ab9b3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -66,7 +66,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
testScope = TestScope()
shadeRepository = FakeShadeRepository()
featureFlags =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 0ad14d0..4f970d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -69,7 +69,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
testScope = TestScope()
configRepository = FakeConfigurationRepository()
keyguardRepository = FakeKeyguardRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/logcat/LogAccessDialogActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/logcat/LogAccessDialogActivityTest.java
index 1d7d5df..eed7ecd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/logcat/LogAccessDialogActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/logcat/LogAccessDialogActivityTest.java
@@ -31,7 +31,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.app.ILogAccessDialogCallback;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
index d6ceea1..043dae6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
@@ -24,7 +24,7 @@
import android.widget.SeekBar
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.SquigglyProgress
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
index 670f117..f7c20ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
@@ -20,7 +20,7 @@
import android.graphics.drawable.Icon
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 5939bb5..fb0a4be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -48,7 +48,7 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index 0c57e7b..85d3fba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -34,7 +34,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 3961a94..a4c2a08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -31,7 +31,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.TestScopeProvider
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 9a90a5c..d6e2e97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -61,7 +61,7 @@
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
@@ -1884,7 +1884,7 @@
@Test
fun bindRecommendation_listHasTooFewRecs_notDisplayed() {
player.attachRecommendation(recommendationViewHolder)
- val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val icon = Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
val data =
smartspaceData.copy(
recommendations =
@@ -1911,7 +1911,7 @@
@Test
fun bindRecommendation_listHasTooFewRecsWithIcons_notDisplayed() {
player.attachRecommendation(recommendationViewHolder)
- val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val icon = Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
val data =
smartspaceData.copy(
recommendations =
@@ -1955,7 +1955,7 @@
val subtitle1 = "Subtitle1"
val subtitle2 = "Subtitle2"
val subtitle3 = "Subtitle3"
- val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val icon = Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
val data =
smartspaceData.copy(
@@ -1998,7 +1998,7 @@
listOf(
SmartspaceAction.Builder("id1", "")
.setSubtitle("fake subtitle")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
+ .setIcon(Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata))
.setExtras(Bundle.EMPTY)
.build()
)
@@ -2013,7 +2013,7 @@
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
- val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val icon = Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
val data =
smartspaceData.copy(
recommendations =
@@ -2047,7 +2047,7 @@
useRealConstraintSets()
player.attachRecommendation(recommendationViewHolder)
- val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val icon = Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
val data =
smartspaceData.copy(
recommendations =
@@ -2086,7 +2086,7 @@
listOf(
SmartspaceAction.Builder("id1", "title1")
.setSubtitle("")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
+ .setIcon(Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "title2")
@@ -2096,7 +2096,7 @@
.build(),
SmartspaceAction.Builder("id3", "title3")
.setSubtitle("")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_3g_mobiledata))
+ .setIcon(Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_3g_mobiledata))
.setExtras(Bundle.EMPTY)
.build()
)
@@ -2119,7 +2119,7 @@
listOf(
SmartspaceAction.Builder("id1", "")
.setSubtitle("subtitle1")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_1x_mobiledata))
+ .setIcon(Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata))
.setExtras(Bundle.EMPTY)
.build(),
SmartspaceAction.Builder("id2", "")
@@ -2129,7 +2129,7 @@
.build(),
SmartspaceAction.Builder("id3", "")
.setSubtitle("subtitle3")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_3g_mobiledata))
+ .setIcon(Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_3g_mobiledata))
.setExtras(Bundle.EMPTY)
.build()
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 33ed01f..25faeef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -24,7 +24,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index ba97df9..1f99303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index e4f89a2..2f057a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -46,7 +46,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.google.common.collect.ImmutableList;
@@ -467,7 +467,7 @@
@Test
public void subStatusSupported_onBindViewHolder_bindDeviceRequirePremium_verifyView() {
String deviceStatus = (String) mContext.getText(
- R.string.media_output_status_require_premium);
+ com.android.settingslib.R.string.media_output_status_require_premium);
when(mMediaDevice2.hasSubtext()).thenReturn(true);
when(mMediaDevice2.getSubtext()).thenReturn(SUBTEXT_SUBSCRIPTION_REQUIRED);
when(mMediaDevice2.getSubtextString()).thenReturn(deviceStatus);
@@ -491,7 +491,7 @@
@Test
public void subStatusSupported_onBindViewHolder_bindDeviceWithAdPlaying_verifyView() {
String deviceStatus = (String) mContext.getText(
- R.string.media_output_status_try_after_ad);
+ com.android.settingslib.R.string.media_output_status_try_after_ad);
when(mMediaDevice2.hasSubtext()).thenReturn(true);
when(mMediaDevice2.getSubtext()).thenReturn(SUBTEXT_AD_ROUTING_DISALLOWED);
when(mMediaDevice2.getSubtextString()).thenReturn(deviceStatus);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index db00e09..9dfb5a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -47,7 +47,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index f25454c..e7400e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -50,7 +50,7 @@
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 1931580..ce999cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -71,7 +71,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index bfc8c83..379136b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -49,7 +49,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
index b09fc55..53e9dc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
@@ -27,7 +27,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.media.DeviceIconUtil
import com.android.settingslib.media.LocalMediaManager
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 6c3d6f5..5c6d913 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -20,7 +20,7 @@
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 2b66e7b..e2be4cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index c7a74da..83145bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -35,7 +35,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
index b396caf..d0c6d7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
@@ -21,7 +21,7 @@
import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 89405c1..c835146 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -74,11 +74,11 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-public class NavigationBarControllerTest extends SysuiTestCase {
+public class NavigationBarControllerImplTest extends SysuiTestCase {
private static final int SECONDARY_DISPLAY = 1;
- private NavigationBarController mNavigationBarController;
+ private NavigationBarControllerImpl mNavigationBarController;
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
private StaticMockitoSession mMockitoSession;
@@ -95,7 +95,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mNavigationBarController = spy(
- new NavigationBarController(mContext,
+ new NavigationBarControllerImpl(mContext,
mock(OverviewProxyService.class),
mock(NavigationModeController.class),
mock(SysUiState.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
index 7369c82..52d02b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
@@ -33,8 +33,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
-import com.android.systemui.navigationbar.NavigationBarInflaterView;
-import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.recents.OverviewProxyService;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
index baac9e0..06127a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
@@ -21,7 +21,7 @@
import android.os.UserHandle
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesService
import com.android.wm.shell.bubbles.Bubbles
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 1536c17..cf43b2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -45,7 +45,7 @@
import androidx.test.ext.truth.content.IntentSubject.assertThat
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
@@ -73,6 +73,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -705,12 +706,12 @@
fun updateNoteTaskAsUser_sameUser_shouldUpdateShortcuts() {
val user = UserHandle.CURRENT
val controller = spy(createNoteTaskController())
- doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+ doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
whenever(controller.getCurrentRunningUser()).thenReturn(user)
controller.updateNoteTaskAsUser(user)
- verify(controller).updateNoteTaskAsUserInternal(user)
+ verify(controller).launchUpdateNoteTaskAsUser(user)
verify(context, never()).startServiceAsUser(any(), any())
}
@@ -718,12 +719,12 @@
fun updateNoteTaskAsUser_differentUser_shouldUpdateShortcutsInUserProcess() {
val user = UserHandle.CURRENT
val controller = spy(createNoteTaskController(isEnabled = true))
- doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+ doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
whenever(controller.getCurrentRunningUser()).thenReturn(UserHandle.SYSTEM)
controller.updateNoteTaskAsUser(user)
- verify(controller, never()).updateNoteTaskAsUserInternal(any())
+ verify(controller, never()).launchUpdateNoteTaskAsUser(any())
val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) }
assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java)
}
@@ -733,7 +734,8 @@
@Test
fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
createNoteTaskController(isEnabled = true)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val actualComponent = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -768,7 +770,8 @@
.thenReturn(emptyList())
createNoteTaskController(isEnabled = true)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -787,7 +790,8 @@
@Test
fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() {
createNoteTaskController(isEnabled = false)
- .updateNoteTaskAsUserInternal(userTracker.userHandle)
+ .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+ testScope.runCurrent()
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 95bb3e0..7833007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -22,6 +22,8 @@
import android.testing.AndroidTestingRunner
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +32,6 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -43,7 +44,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -66,6 +66,7 @@
private val executor = FakeExecutor(FakeSystemClock())
private val userTracker = FakeUserTracker()
+ private val handlerCallbacks = mutableListOf<Runnable>()
@Before
fun setUp() {
@@ -74,19 +75,27 @@
}
private fun createUnderTest(
- isEnabled: Boolean,
- bubbles: Bubbles?,
+ isEnabled: Boolean,
+ bubbles: Bubbles?,
): NoteTaskInitializer =
- NoteTaskInitializer(
- controller = controller,
- commandQueue = commandQueue,
- optionalBubbles = Optional.ofNullable(bubbles),
- isEnabled = isEnabled,
- roleManager = roleManager,
- userTracker = userTracker,
- keyguardUpdateMonitor = keyguardMonitor,
- backgroundExecutor = executor,
- )
+ NoteTaskInitializer(
+ controller = controller,
+ commandQueue = commandQueue,
+ optionalBubbles = Optional.ofNullable(bubbles),
+ isEnabled = isEnabled,
+ roleManager = roleManager,
+ userTracker = userTracker,
+ keyguardUpdateMonitor = keyguardMonitor,
+ backgroundExecutor = executor,
+ )
+
+ private fun createKeyEvent(
+ action: Int,
+ code: Int,
+ downTime: Long = 0L,
+ eventTime: Long = 0L,
+ metaState: Int = 0
+ ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
@Test
fun initialize_withUserUnlocked() {
@@ -120,12 +129,12 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -136,18 +145,23 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@Test
fun initialize_handleSystemKey() {
- val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL)
+ val expectedKeyEvent =
+ createKeyEvent(
+ ACTION_DOWN,
+ KEYCODE_N,
+ metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
@@ -176,7 +190,7 @@
underTest.initialize()
val callback = withArgCaptor {
verify(roleManager)
- .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
+ .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
}
callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
@@ -203,4 +217,60 @@
verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
}
+
+ @Test
+ fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ verify(controller, never()).showNoteTask(any())
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+
+ verify(controller).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 99)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 150)
+ )
+
+ verify(controller, times(1)).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 1000)
+ )
+
+ verify(controller, never()).showNoteTask(any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index 58c762e..119ffd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -28,7 +28,7 @@
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.dx.mockito.inline.extended.ExtendedMockito
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index bd913ba..30d66dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -58,7 +58,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.appwidget.IAppWidgetService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.people.widget.PeopleTileKey;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index e6b960d..d0e8cce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -60,7 +60,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.widget.PeopleTileKey;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index e669d06..a63b221 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -101,7 +101,7 @@
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.PeopleBackupFollowUpJob;
import com.android.systemui.people.PeopleSpaceUtils;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index b9ee19b..2bdad2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -45,7 +45,7 @@
import android.testing.TestableResources;
import com.android.settingslib.fuelgauge.Estimate;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 1d687b1..b754145 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -22,7 +22,7 @@
import android.view.ViewGroup
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
index f4644a5..6c01ba5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
@@ -24,7 +24,7 @@
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
deleted file mode 100644
index 3e677c0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-
-import static org.junit.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashDrawable;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
-import org.junit.Test;
-
-@SmallTest
-public class AlphaControlledSignalTileViewTest extends SysuiTestCase {
-
- private AlphaControlledSignalTileView mTileView;
-
- @Test
- public void testTileView_createsAlphaControlledSlashImageView() {
- mTileView = new AlphaControlledSignalTileView(mContext);
-
- assertTrue(mTileView.createSlashImageView(mContext)
- instanceof AlphaControlledSlashImageView);
- }
-
- /// AlphaControlledSlashImageView tests
- @Test
- public void testSlashImageView_createsAlphaControlledSlashDrawable() {
- TestableSlashImageView iv = new TestableSlashImageView(mContext);
-
- iv.ensureSlashDrawable();
- assertTrue(iv.getSlashDrawable() instanceof AlphaControlledSlashDrawable);
- }
-
- /// AlphaControlledSlashDrawable tests
- @Test
- public void testSlashDrawable_doesNotSetTintList() {
- Drawable mockDrawable = mock(Drawable.class);
- AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
- ColorStateList list = ColorStateList.valueOf(0xffffff);
- drawable.setTintList(list);
- verify(mockDrawable, never()).setTintList(any());
- }
-
- @Test
- public void testSlashDrawable_setsFinalTintList() {
- Drawable mockDrawable = mock(Drawable.class);
- AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
- ColorStateList list = ColorStateList.valueOf(0xffffff);
- drawable.setFinalTintList(list);
- verify(mockDrawable, atLeastOnce()).setTintList(list);
- }
-
- // Expose getSlashDrawable
- private static class TestableSlashImageView extends AlphaControlledSlashImageView {
- TestableSlashImageView(Context c) {
- super(c);
- }
-
- private SlashDrawable getSlashDrawable() {
- return mSlash;
- }
-
- @Override
- protected void setSlash(SlashDrawable slash) {
- super.setSlash(slash);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
index bf237ab..8c5d99a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
@@ -5,7 +5,7 @@
import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.customize.QSCustomizer
import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index d9db60d..98e803f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -34,7 +34,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.retail.data.repository.FakeRetailModeRepository;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 8afe095..c4c233c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -48,7 +48,7 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 2ac220c..e2ac7bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -34,6 +34,7 @@
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.ContextThemeWrapper;
import androidx.test.filters.SmallTest;
@@ -41,12 +42,11 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -92,8 +92,6 @@
@Mock
QSTile mOtherTile;
@Mock
- QSTileView mQSTileView;
- @Mock
PagedTileLayout mPagedTileLayout;
@Mock
Resources mResources;
@@ -132,11 +130,12 @@
when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
when(mQSTile.getTileSpec()).thenReturn("dnd");
when(mQSHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
- when(mQSHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
when(mQSTileRevealControllerFactory.create(any(), any()))
.thenReturn(mQSTileRevealController);
when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
when(mQSPanel.getResources()).thenReturn(mResources);
+ when(mQSPanel.getContext()).thenReturn(
+ new ContextThemeWrapper(getContext(), R.style.Theme_SystemUI_QuickSettings));
when(mResources.getConfiguration()).thenReturn(mConfiguration);
doAnswer(invocation -> {
when(mQSPanel.isListening()).thenReturn(invocation.getArgument(0));
@@ -209,14 +208,15 @@
@Test
public void testDump() {
- String mockTileViewString = "Mock Tile View";
+ String mockTileViewString = "QSTileViewImpl[locInScreen=(0, 0), "
+ + "iconView=QSIconViewImpl[state=-1, tint=0], "
+ + "tileState=false]";
String mockTileString = "Mock Tile";
doAnswer(invocation -> {
PrintWriter pw = invocation.getArgument(0);
pw.println(mockTileString);
return null;
}).when(mQSTile).dump(any(PrintWriter.class), any(String[].class));
- when(mQSTileView.toString()).thenReturn(mockTileViewString);
StringWriter w = new StringWriter();
PrintWriter pw = new PrintWriter(w);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 8a530dd..5e4c954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -4,9 +4,10 @@
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableResources
+import android.view.ContextThemeWrapper
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.ui.MediaHost
@@ -71,6 +72,8 @@
whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
setShouldUseSplitShade(false)
whenever(qsPanel.resources).thenReturn(testableResources.resources)
+ whenever(qsPanel.context)
+ .thenReturn( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
whenever(qsPanel.setListening(anyBoolean())).then {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index fe6c9b3..cc48640 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -26,13 +26,12 @@
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.QSPanelControllerBase.TileRecord
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tileimpl.QSIconViewImpl
import com.android.systemui.qs.tileimpl.QSTileViewImpl
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -112,7 +111,7 @@
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
+ QSTileViewImpl(themedContext)
)
)
@@ -142,7 +141,7 @@
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
+ QSTileViewImpl(themedContext)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 2dc78a3..fbf3724 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -53,7 +53,7 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.animation.Expandable;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 64d3b82..b595e8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -48,7 +48,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.CollectionUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.nano.SystemUIProtoDump;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index f188b4e..2db79c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -18,15 +18,16 @@
import android.content.res.Configuration
import android.testing.AndroidTestingRunner
+import android.view.ContextThemeWrapper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
@@ -36,15 +37,14 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -58,7 +58,6 @@
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var tile: QSTile
@Mock private lateinit var tileLayout: TileLayout
- @Mock private lateinit var tileView: QSTileView
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
private val uiEventLogger = UiEventLoggerFake()
@@ -76,7 +75,8 @@
whenever(quickQSPanel.isAttachedToWindow).thenReturn(true)
whenever(quickQSPanel.dumpableTag).thenReturn("")
whenever(quickQSPanel.resources).thenReturn(mContext.resources)
- whenever(qsHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
+ whenever(quickQSPanel.context)
+ .thenReturn(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
controller =
TestQuickQSPanelController(
@@ -89,7 +89,8 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager)
+ dumpManager
+ )
controller.init()
}
@@ -169,7 +170,8 @@
uiEventLogger,
qsLogger,
dumpManager,
- ResourcesSplitShadeStateController()) {
+ ResourcesSplitShadeStateController()
+ ) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
deleted file mode 100644
index ea8c64a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.QSTile.SlashState;
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class SlashImageViewTest extends SysuiTestCase {
- private TestableSlashImageView mSlashView;
-
- @Test
- public void testSetNonNullSlashStateCreatesSlashDrawable() {
- SlashState mockState = mock(SlashState.class);
- Drawable mockDrawable = mock(Drawable.class);
- mSlashView = new TestableSlashImageView(mContext);
- assertTrue(mSlashView.getSlashDrawable() == null);
-
- mSlashView.setState(mockState, mockDrawable);
-
- assertTrue(mSlashView.getSlashDrawable() != null);
- }
-
- @Test
- public void testSetNullSlashStateRemovesSlashDrawable() {
- SlashState mockState = mock(SlashState.class);
- Drawable mockDrawable = mock(Drawable.class);
- mSlashView = new TestableSlashImageView(mContext);
- mSlashView.setState(mockState, mockDrawable);
-
- assertTrue(mSlashView.getSlashDrawable() != null);
-
- mSlashView.setState(null, mockDrawable);
-
- assertTrue(mSlashView.getSlashDrawable() == null);
- }
-
- @Test
- public void testSetNullDrawableRemovesSlashDrawable() {
- SlashState mockState = mock(SlashState.class);
- Drawable mockDrawable = mock(Drawable.class);
-
- mSlashView = new TestableSlashImageView(mContext);
- mSlashView.setImageDrawable(mockDrawable);
- mSlashView.setState(mockState, mockDrawable);
- mSlashView.setImageDrawable(null);
-
- assertTrue(mSlashView.getSlashDrawable() == null);
- }
-
- @Test
- public void testSetImageDrawableUsesDrawableLevel() {
- SlashImageView iv = new SlashImageView(mContext);
- Drawable mockDrawable = mock(Drawable.class);
- when(mockDrawable.getLevel()).thenReturn(2);
-
- iv.setImageDrawable(mockDrawable);
-
- // Make sure setting the drawable didn't reset its level to 0
- verify(mockDrawable).setLevel(eq(2));
- }
-
- // Expose getSlashDrawable
- private static class TestableSlashImageView extends SlashImageView {
- TestableSlashImageView(Context c) {
- super(c);
- }
-
- private SlashDrawable getSlashDrawable() {
- return mSlash;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index f55ef65..f809259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -39,10 +39,9 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import org.junit.Before;
@@ -78,7 +77,7 @@
private QSPanelControllerBase.TileRecord createTileRecord() {
return new QSPanelControllerBase.TileRecord(
mock(QSTile.class),
- spy(new QSTileViewImpl(mSpyContext, new QSIconViewImpl(mSpyContext))));
+ spy(new QSTileViewImpl(mSpyContext)));
}
@Test
@@ -272,7 +271,7 @@
private static class FakeTileView extends QSTileViewImpl {
FakeTileView(Context context) {
- super(context, new QSIconViewImpl(context), /* collapsed= */ false);
+ super(context, /* collapsed= */ false);
}
void changeState(QSTile.State state) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
index a5dead0..6cad985 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
@@ -31,7 +31,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 78a0258..bde3038 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,7 +34,6 @@
import static org.mockito.Mockito.when;
import android.Manifest;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -51,9 +50,8 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSHost;
import com.android.systemui.settings.UserTracker;
@@ -393,11 +391,6 @@
}
@Override
- public QSIconView createTileView(Context context) {
- return null;
- }
-
- @Override
public void click(@Nullable View view) {}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 244f627..126dd63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -34,7 +34,7 @@
import android.view.IWindowManager
import android.view.View
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.view.LaunchableFrameLayout
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
index ec8552b..b8e6403 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
@@ -23,7 +23,7 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index fb17fc0..365e8a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -23,7 +23,7 @@
import android.widget.ImageView
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTileView
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index d647d6a..4ada44c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index aef5faf..1c28e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -19,7 +19,7 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
index 6e579d4..15e401d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
index 8036cb4..5ce15fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
@@ -20,7 +20,7 @@
import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index 013c925..7e0e7d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
@@ -16,10 +16,8 @@
package com.android.systemui.qs.pipeline.domain.interactor
-import android.content.Context
import android.view.View
import com.android.internal.logging.InstanceId
-import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
class FakeQSTile(
@@ -58,10 +56,6 @@
callbacks.clear()
}
- override fun createTileView(context: Context?): QSIconView? {
- return null
- }
-
override fun click(view: View?) {}
override fun secondaryClick(view: View?) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 180fed9..81a9604 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -49,7 +49,6 @@
public class QSIconViewImplTest extends SysuiTestCase {
private QSIconViewImpl mIconView;
- private static int RES_ID = 1;
@Before
public void setup() {
@@ -79,7 +78,7 @@
@Test
public void testMutateIconDrawable() {
- SlashImageView iv = mock(SlashImageView.class);
+ ImageView iv = mock(ImageView.class);
Drawable originalDrawable = mock(Drawable.class);
Drawable otherDrawable = mock(Drawable.class);
State s = new State();
@@ -92,7 +91,7 @@
mIconView.updateIcon(iv, s, /* allowAnimations= */true);
- verify(iv).setState(any(), eq(otherDrawable));
+ verify(iv).setImageDrawable(eq(otherDrawable));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 3c66772..e0fff9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -27,9 +27,8 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -44,8 +43,6 @@
class QSTileViewImplTest : SysuiTestCase() {
@Mock
- private lateinit var iconView: QSIconView
- @Mock
private lateinit var customDrawable: Drawable
private lateinit var tileView: FakeTileView
@@ -57,7 +54,7 @@
MockitoAnnotations.initMocks(this)
context.ensureTestableResources()
- tileView = FakeTileView(context, iconView, false)
+ tileView = FakeTileView(context, false)
customDrawableView = tileView.requireViewById(R.id.customDrawable)
chevronView = tileView.requireViewById(R.id.chevron)
}
@@ -385,11 +382,9 @@
class FakeTileView(
context: Context,
- icon: QSIconView,
collapsed: Boolean
) : QSTileViewImpl(
ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
- icon,
collapsed
) {
fun changeState(state: QSTile.State) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
index b8d018f..f2400ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
@@ -18,7 +18,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.MediumTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index c60cecb..e2a3fac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingManagerFake
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index ff6814c..605dc14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -24,7 +24,7 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index e5c55d8..82ee99a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -11,9 +11,10 @@
import com.android.internal.logging.MetricsLogger
import com.android.settingslib.Utils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -22,6 +23,7 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -50,6 +52,8 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var bluetoothController: BluetoothController
@Mock private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var featureFlags: FeatureFlagsClassic
+ @Mock private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
private lateinit var testableLooper: TestableLooper
private lateinit var tile: FakeBluetoothTile
@@ -73,6 +77,8 @@
activityStarter,
qsLogger,
bluetoothController,
+ featureFlags,
+ bluetoothTileDialogViewModel
)
tile.initialize()
@@ -220,6 +226,8 @@
activityStarter: ActivityStarter,
qsLogger: QSLogger,
bluetoothController: BluetoothController,
+ featureFlags: FeatureFlagsClassic,
+ bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
) :
BluetoothTile(
qsHost,
@@ -232,6 +240,8 @@
activityStarter,
qsLogger,
bluetoothController,
+ featureFlags,
+ bluetoothTileDialogViewModel
) {
var restrictionChecked: String? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 70d82fd..c4cf342 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index 2e02bbe..c93ff4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -32,7 +32,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 176b33f..51e95be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 1346069..bce4c06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -27,7 +27,7 @@
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 77a4436..5dd297c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -29,7 +29,7 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index f231c6e..a0c1073 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -41,7 +41,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index 73aa699..c1a0928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -6,7 +6,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index 73f61d06..14dfdea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -31,7 +31,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
@@ -116,7 +116,7 @@
assertThat(mState.state).isNotEqualTo(Tile.STATE_UNAVAILABLE);
assertThat(String.valueOf(mState.secondaryLabel))
- .isNotEqualTo(mContext.getString(R.string.wifitrackerlib_admin_restricted_network));
+ .isNotEqualTo(mContext.getString(com.android.wifitrackerlib.R.string.wifitrackerlib_admin_restricted_network));
mockitoSession.finishMocking();
}
@@ -131,7 +131,7 @@
assertThat(mState.state).isEqualTo(Tile.STATE_UNAVAILABLE);
assertThat(String.valueOf(mState.secondaryLabel))
- .isEqualTo(mContext.getString(R.string.wifitrackerlib_admin_restricted_network));
+ .isEqualTo(mContext.getString(com.android.wifitrackerlib.R.string.wifitrackerlib_admin_restricted_network));
mockitoSession.finishMocking();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index b6cf459..83f8f18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 7957c6a..c1f1964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -29,7 +29,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 0bf0b38..62a50e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index ceff546..3511bb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index 763a7e5..118ad2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index 6c8f76b..a1d9e414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -23,7 +23,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dagger.NightDisplayListenerModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index 71aa7a8..7b3171d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index b00ae39..2d18f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -58,7 +58,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
@@ -395,7 +395,7 @@
PendingIntent.getActivity(mContext, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
WalletCard walletCard =
new WalletCard.Builder(
- CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
+ CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
GetWalletCardsResponse response =
new GetWalletCardsResponse(Collections.singletonList(walletCard), 0);
@@ -410,6 +410,22 @@
}
@Test
+ public void testQueryCards_cardDataPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_PAYMENT);
+
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_cardDataNonPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_NON_PAYMENT);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
setUpWalletCard(/* hasCard= */ false);
@@ -438,6 +454,29 @@
verifyZeroInteractions(mQuickAccessWalletClient);
}
+ private WalletCard createWalletCardWithType(Context context, int cardType) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID, cardType, CARD_IMAGE, CARD_DESCRIPTION,
+ pendingIntent).build();
+ }
+
+ private void setUpWalletCardWithType(boolean hasCard, int cardType) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(
+ createWalletCardWithType(mContext, cardType))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
private void setUpWalletCard(boolean hasCard) {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index d244594..c5721ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -32,7 +32,7 @@
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R.drawable;
+import com.android.systemui.res.R.drawable;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 41545fc..df6993d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -33,7 +33,7 @@
import android.testing.TestableResources;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index fff2b8f..5b3d45b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -35,7 +35,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 79147e7..47fc3ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -25,7 +25,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index 18e40f6..f9d69c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.UserIcons
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.qs.QSUserSwitcherEvent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
new file mode 100644
index 0000000..47b4244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
@@ -0,0 +1,41 @@
+package com.android.systemui.qs.tiles.base
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityStarted: ActivityStarter
+
+ lateinit var underTest: QSTileIntentUserActionHandler
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = QSTileIntentUserActionHandler(activityStarted)
+ }
+
+ @Test
+ fun testPassesIntentToStarter() {
+ val intent = Intent("test.ACTION")
+
+ underTest.handle(QSTileUserAction.Click(context, null), intent)
+
+ verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index df037c0..8004c6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -20,7 +20,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.wifitrackerlib.WifiEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 25684ce2..6dc7a06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -63,7 +63,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -983,7 +983,7 @@
spyController.mCarrierNetworkChangeMode = true;
String dds = spyController.getMobileNetworkSummary(SUB_ID);
- assertThat(dds).contains(mContext.getString(R.string.carrier_network_change_mode));
+ assertThat(dds).contains(mContext.getString(com.android.settingslib.R.string.carrier_network_change_mode));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 2c74d4f..039e58a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -33,7 +33,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
new file mode 100644
index 0000000..fc2b7a64
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractorTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BluetoothStateInteractorTest : SysuiTestCase() {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ private val testScope = TestScope()
+
+ private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
+
+ @Mock private lateinit var bluetoothAdapter: LocalBluetoothAdapter
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+
+ @Before
+ fun setUp() {
+ bluetoothStateInteractor =
+ BluetoothStateInteractor(localBluetoothManager, testScope.backgroundScope)
+ `when`(localBluetoothManager.bluetoothAdapter).thenReturn(bluetoothAdapter)
+ }
+
+ @Test
+ fun testGet_isBluetoothEnabled() {
+ testScope.runTest {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(true)
+
+ assertThat(bluetoothStateInteractor.isBluetoothEnabled).isTrue()
+ }
+ }
+
+ @Test
+ fun testGet_isBluetoothDisabled() {
+ testScope.runTest {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(false)
+
+ assertThat(bluetoothStateInteractor.isBluetoothEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun testSet_bluetoothEnabled() {
+ testScope.runTest {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(false)
+
+ bluetoothStateInteractor.isBluetoothEnabled = true
+ verify(bluetoothAdapter).enable()
+ }
+ }
+
+ @Test
+ fun testSet_bluetoothNoChange() {
+ testScope.runTest {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(false)
+
+ bluetoothStateInteractor.isBluetoothEnabled = false
+ verify(bluetoothAdapter, never()).enable()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt
new file mode 100644
index 0000000..da8f60a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogRepositoryTest.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BluetoothTileDialogRepositoryTest : SysuiTestCase() {
+
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+
+ @Mock private lateinit var bluetoothAdapter: BluetoothAdapter
+
+ @Mock private lateinit var cachedDeviceManager: CachedBluetoothDeviceManager
+
+ @Mock private lateinit var cachedDevicesCopy: Collection<CachedBluetoothDevice>
+
+ private lateinit var repository: BluetoothTileDialogRepository
+
+ @Before
+ fun setUp() {
+ `when`(localBluetoothManager.cachedDeviceManager).thenReturn(cachedDeviceManager)
+ `when`(cachedDeviceManager.cachedDevicesCopy).thenReturn(cachedDevicesCopy)
+
+ repository = BluetoothTileDialogRepository(localBluetoothManager, bluetoothAdapter)
+ }
+
+ @Test
+ fun testCachedDevices_bluetoothOff_emptyList() {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(false)
+
+ val result = repository.cachedDevices
+
+ assertThat(result).isEmpty()
+ }
+
+ @Test
+ fun testCachedDevices_bluetoothOn_returnDevice() {
+ `when`(bluetoothAdapter.isEnabled).thenReturn(true)
+
+ val result = repository.cachedDevices
+
+ assertThat(result).isEqualTo(cachedDevicesCopy)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
new file mode 100644
index 0000000..89fa55b3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.graphics.drawable.Drawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.DISABLED_ALPHA
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BluetoothTileDialogTest : SysuiTestCase() {
+ companion object {
+ const val DEVICE_NAME = "device"
+ const val DEVICE_CONNECTION_SUMMARY = "active"
+ const val ENABLED = true
+ }
+
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+
+ @Mock private lateinit var bluetoothTileDialogCallback: BluetoothTileDialogCallback
+
+ @Mock private lateinit var drawable: Drawable
+
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+
+ private lateinit var icon: Pair<Drawable, String>
+ private lateinit var bluetoothTileDialog: BluetoothTileDialog
+ private lateinit var deviceItem: DeviceItem
+
+ @Before
+ fun setUp() {
+ bluetoothTileDialog =
+ BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ icon = Pair(drawable, DEVICE_NAME)
+ deviceItem =
+ DeviceItem(
+ type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedBluetoothDevice,
+ deviceName = DEVICE_NAME,
+ connectionSummary = DEVICE_CONNECTION_SUMMARY,
+ iconWithDescription = icon,
+ background = null
+ )
+ `when`(cachedBluetoothDevice.isBusy).thenReturn(false)
+ }
+
+ @Test
+ fun testShowDialog_createRecyclerViewWithAdapter() {
+ bluetoothTileDialog.show()
+
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+
+ assertThat(bluetoothTileDialog.isShowing).isTrue()
+ assertThat(recyclerView).isNotNull()
+ assertThat(recyclerView?.visibility).isEqualTo(VISIBLE)
+ assertThat(recyclerView?.adapter).isNotNull()
+ assertThat(recyclerView?.layoutManager is LinearLayoutManager).isTrue()
+ }
+
+ @Test
+ fun testShowDialog_displayBluetoothDevice() {
+ bluetoothTileDialog =
+ BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ bluetoothTileDialog.show()
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = false
+ )
+
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+ assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
+ assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
+ assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ }
+
+ @Test
+ fun testDeviceItemViewHolder_cachedDeviceNotBusy() {
+ deviceItem.isEnabled = true
+ deviceItem.alpha = ENABLED_ALPHA
+
+ val view =
+ LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false)
+ val viewHolder =
+ BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ .Adapter(bluetoothTileDialogCallback)
+ .DeviceItemViewHolder(view)
+ viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback)
+ val container = view.requireViewById<View>(R.id.bluetooth_device)
+ val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
+
+ assertThat(container).isNotNull()
+ assertThat(container.isEnabled).isTrue()
+ assertThat(container.alpha).isEqualTo(ENABLED_ALPHA)
+ assertThat(deviceView.hasOnClickListeners()).isTrue()
+ }
+
+ @Test
+ fun testDeviceItemViewHolder_cachedDeviceBusy() {
+ deviceItem.isEnabled = false
+ deviceItem.alpha = DISABLED_ALPHA
+
+ val view =
+ LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false)
+ val viewHolder =
+ BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ .Adapter(bluetoothTileDialogCallback)
+ .DeviceItemViewHolder(view)
+ viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback)
+ val container = view.requireViewById<View>(R.id.bluetooth_device_row)
+ val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
+
+ assertThat(container).isNotNull()
+ assertThat(container.isEnabled).isFalse()
+ assertThat(container.alpha).isEqualTo(DISABLED_ALPHA)
+ assertThat(deviceView.hasOnClickListeners()).isTrue()
+ }
+
+ @Test
+ fun testOnDeviceUpdated_hideSeeAll_showPairNew() {
+ bluetoothTileDialog =
+ BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ bluetoothTileDialog.show()
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = true
+ )
+
+ val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
+ val pairNewLayout =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+
+ assertThat(seeAllLayout).isNotNull()
+ assertThat(seeAllLayout.visibility).isEqualTo(GONE)
+ assertThat(pairNewLayout).isNotNull()
+ assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(adapter.itemCount).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
new file mode 100644
index 0000000..7157cce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BluetoothTileDialogViewModelTest : SysuiTestCase() {
+
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ private val fakeSystemClock = FakeSystemClock()
+ private val backgroundExecutor = FakeExecutor(fakeSystemClock)
+
+ private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
+
+ @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
+
+ @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
+
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
+ @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+
+ @Mock private lateinit var deviceItem: DeviceItem
+
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+
+ private lateinit var scheduler: TestCoroutineScheduler
+ private lateinit var dispatcher: CoroutineDispatcher
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ scheduler = TestCoroutineScheduler()
+ dispatcher = UnconfinedTestDispatcher(scheduler)
+ testScope = TestScope(dispatcher)
+ bluetoothTileDialogViewModel =
+ BluetoothTileDialogViewModel(
+ deviceItemInteractor,
+ bluetoothStateInteractor,
+ dialogLaunchAnimator,
+ activityStarter,
+ uiEventLogger,
+ testScope.backgroundScope,
+ dispatcher,
+ )
+ `when`(deviceItemInteractor.deviceItemFlow).thenReturn(MutableStateFlow(null).asStateFlow())
+ `when`(bluetoothStateInteractor.updateBluetoothStateFlow)
+ .thenReturn(MutableStateFlow(null).asStateFlow())
+ `when`(deviceItemInteractor.updateDeviceItemsFlow)
+ .thenReturn(MutableStateFlow(Unit).asStateFlow())
+ `when`(bluetoothStateInteractor.isBluetoothEnabled).thenReturn(true)
+ }
+
+ @Test
+ fun testShowDialog_noAnimation() {
+ testScope.runTest {
+ bluetoothTileDialogViewModel.showDialog(context, null)
+
+ assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
+ verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), any())
+ assertThat(bluetoothTileDialogViewModel.dialog?.isShowing).isTrue()
+ verify(uiEventLogger).log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
+ }
+ }
+
+ @Test
+ fun testShowDialog_animated() {
+ testScope.runTest {
+ bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
+
+ assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
+ verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
+ }
+ }
+
+ @Test
+ fun testShowDialog_animated_callInBackgroundThread() {
+ testScope.runTest {
+ backgroundExecutor.execute {
+ bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
+
+ assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
+ verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
+ }
+ }
+ }
+
+ @Test
+ fun testShowDialog_fetchDeviceItem() {
+ testScope.runTest {
+ bluetoothTileDialogViewModel.showDialog(context, null)
+
+ assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
+ verify(deviceItemInteractor).deviceItemFlow
+ }
+ }
+
+ @Test
+ fun testShowDialog_withBluetoothStateValue() {
+ testScope.runTest {
+ bluetoothTileDialogViewModel.showDialog(context, null)
+
+ assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
+ verify(bluetoothStateInteractor).updateBluetoothStateFlow
+ }
+ }
+
+ @Test
+ fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
+ `when`(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+ bluetoothTileDialogViewModel.showDialog(context, null)
+
+ val clickedView = View(context)
+ bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView)
+
+ verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
+ verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
new file mode 100644
index 0000000..3451902
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DeviceItemFactoryTest : SysuiTestCase() {
+
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var cachedDevice: CachedBluetoothDevice
+
+ private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory()
+ private val connectedDeviceItemFactory = ConnectedDeviceItemFactory()
+ private val savedDeviceItemFactory = SavedDeviceItemFactory()
+
+ @Before
+ fun setup() {
+ `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+ `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+ }
+
+ @Test
+ fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() {
+ val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice)
+
+ assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ }
+
+ @Test
+ fun testConnectedDeviceItemFactory_createFromCachedDevice() {
+ val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice)
+
+ assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ }
+
+ @Test
+ fun testSavedDeviceItemFactory_createFromCachedDevice() {
+ val deviceItem = savedDeviceItemFactory.create(context, cachedDevice)
+
+ assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ assertThat(deviceItem.background).isNull()
+ }
+
+ private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) {
+ assertThat(deviceItem).isNotNull()
+ assertThat(deviceItem!!.type).isEqualTo(deviceItemType)
+ assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice)
+ assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME)
+ assertThat(deviceItem.connectionSummary).isEqualTo(CONNECTION_SUMMARY)
+ }
+
+ companion object {
+ const val DEVICE_NAME = "DeviceName"
+ const val CONNECTION_SUMMARY = "ConnectionSummary"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
new file mode 100644
index 0000000..07a95ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -0,0 +1,221 @@
+/*
+ * 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.qs.tiles.dialog.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.content.Context
+import android.media.AudioManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DeviceItemInteractorTest : SysuiTestCase() {
+
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var bluetoothTileDialogRepository: BluetoothTileDialogRepository
+
+ @Mock private lateinit var cachedDevice1: CachedBluetoothDevice
+
+ @Mock private lateinit var cachedDevice2: CachedBluetoothDevice
+
+ @Mock private lateinit var device1: BluetoothDevice
+
+ @Mock private lateinit var device2: BluetoothDevice
+
+ @Mock private lateinit var deviceItem1: DeviceItem
+
+ @Mock private lateinit var deviceItem2: DeviceItem
+
+ @Mock private lateinit var audioManager: AudioManager
+
+ @Mock private lateinit var adapter: BluetoothAdapter
+
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+
+ private lateinit var interactor: DeviceItemInteractor
+
+ private lateinit var dispatcher: CoroutineDispatcher
+
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ dispatcher = StandardTestDispatcher()
+ testScope = TestScope(dispatcher)
+ interactor =
+ DeviceItemInteractor(
+ bluetoothTileDialogRepository,
+ audioManager,
+ adapter,
+ localBluetoothManager,
+ uiEventLogger,
+ testScope.backgroundScope,
+ dispatcher
+ )
+
+ `when`(deviceItem1.cachedBluetoothDevice).thenReturn(cachedDevice1)
+ `when`(deviceItem2.cachedBluetoothDevice).thenReturn(cachedDevice2)
+ `when`(cachedDevice1.device).thenReturn(device1)
+ `when`(cachedDevice2.device).thenReturn(device2)
+ `when`(bluetoothTileDialogRepository.cachedDevices)
+ .thenReturn(listOf(cachedDevice1, cachedDevice2))
+ }
+
+ @Test
+ fun testUpdateDeviceItems_noCachedDevice_returnEmpty() {
+ testScope.runTest {
+ `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(emptyList())
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(createFactory({ true }, deviceItem1))
+ )
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).isEmpty()
+ }
+ }
+
+ @Test
+ fun testUpdateDeviceItems_hasCachedDevice_filterNotMatch_returnEmpty() {
+ testScope.runTest {
+ `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1))
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(createFactory({ false }, deviceItem1))
+ )
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).isEmpty()
+ }
+ }
+
+ @Test
+ fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnDeviceItem() {
+ testScope.runTest {
+ `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1))
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(createFactory({ true }, deviceItem1))
+ )
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).hasSize(1)
+ assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem1)
+ }
+ }
+
+ @Test
+ fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnMultipleDeviceItem() {
+ testScope.runTest {
+ `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2))
+ )
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).hasSize(2)
+ assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem2)
+ assertThat(interactor.deviceItemFlow.value!![1]).isEqualTo(deviceItem2)
+ }
+ }
+
+ @Test
+ fun testUpdateDeviceItems_sortByDisplayPriority() {
+ testScope.runTest {
+ `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(
+ createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1),
+ createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2)
+ )
+ )
+ interactor.setDisplayPriorityForTesting(
+ listOf(
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
+ )
+ )
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1))
+ }
+ }
+
+ @Test
+ fun testUpdateDeviceItems_sameType_sortByRecentlyConnected() {
+ testScope.runTest {
+ `when`(adapter.mostRecentlyConnectedDevices).thenReturn(listOf(device2, device1))
+ interactor.setDeviceItemFactoryListForTesting(
+ listOf(
+ createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1),
+ createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2)
+ )
+ )
+ interactor.setDisplayPriorityForTesting(
+ listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ )
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+
+ interactor.updateDeviceItems(mContext)
+
+ assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1))
+ }
+ }
+
+ private fun createFactory(
+ isFilterMatchFunc: (CachedBluetoothDevice) -> Boolean,
+ deviceItem: DeviceItem
+ ): DeviceItemFactory {
+ return object : DeviceItemFactory() {
+ override fun isFilterMatched(
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager?
+ ) = isFilterMatchFunc(cachedDevice)
+
+ override fun create(context: Context, cachedDevice: CachedBluetoothDevice) = deviceItem
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
new file mode 100644
index 0000000..643866e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.MediumTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// TODO(b/299909368): Add more tests
+@MediumTest
+@RoboPilotTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
+
+ private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
+ private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun testDoesntListenStateUntilCreated() =
+ testScope.runTest {
+ assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+ underTest.onLifecycle(QSTileLifecycle.ALIVE)
+ underTest.onUserIdChanged(1)
+
+ assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
+ assertThat(fakeQSTileDataInteractor.dataRequests.first())
+ .isEqualTo(QSTileDataRequest(1, StateUpdateTrigger.InitialRequest))
+ }
+
+ private fun createViewModel(
+ scope: TestScope,
+ config: QSTileConfig = TEST_QS_TILE_CONFIG,
+ ): QSTileViewModel =
+ object :
+ BaseQSTileViewModel<Any>(
+ config,
+ fakeQSTileUserActionInteractor,
+ fakeQSTileDataInteractor,
+ object : QSTileDataToStateMapper<Any> {
+ override fun map(config: QSTileConfig, data: Any): QSTileState {
+ return QSTileState(config.tileIcon, config.tileLabel)
+ }
+ },
+ testCoroutineDispatcher,
+ tileScope = scope.backgroundScope,
+ ) {}
+
+ private companion object {
+ val TEST_QS_TILE_CONFIG =
+ QSTileConfig(
+ TileSpec.create("default"),
+ Icon.createWithContentUri(""),
+ "",
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index 55813f6..c108a80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -28,7 +28,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.FakeExecutor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index e353a53..6d358db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -48,7 +48,6 @@
import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_WAKING
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -94,8 +93,8 @@
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var shellInterface: ShellInterface
@Mock private lateinit var navBarController: NavigationBarController
- @Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var screenPinningRequest: ScreenPinningRequest
@Mock private lateinit var navModeController: NavigationModeController
@Mock private lateinit var statusBarWinController: NotificationShadeWindowController
@Mock private lateinit var userTracker: UserTracker
@@ -134,8 +133,8 @@
commandQueue,
shellInterface,
{ navBarController },
- { Optional.of(centralSurfaces) },
{ shadeViewController },
+ screenPinningRequest,
navModeController,
statusBarWinController,
sysUiState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 141fcbb..6b918c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -22,7 +22,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.model.WakefulnessState
@@ -48,7 +47,9 @@
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -99,6 +100,8 @@
sceneInteractor = sceneInteractor,
)
+ private val communalInteractor = utils.communalInteractor()
+
private val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
@@ -123,7 +126,9 @@
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
+ applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
+ communalInteractor = communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -155,6 +160,8 @@
repository = keyguardRepository,
)
+ private var bouncerSceneJob: Job? = null
+
@Before
fun setUp() {
shadeHeaderViewModel =
@@ -284,7 +291,7 @@
@Test
fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.None)
+ setAuthMethod(DomainLayerAuthenticationMethodModel.None)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -295,7 +302,7 @@
@Test
fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.Swipe)
+ setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -360,6 +367,23 @@
assertCurrentScene(SceneKey.Lockscreen)
}
+ @Test
+ fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
+ testScope.runTest {
+ setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ val upDestinationSceneKey by
+ collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
+ assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ emulateUserDrivenTransition(
+ to = upDestinationSceneKey,
+ )
+
+ dismissIme()
+
+ assertCurrentScene(SceneKey.Lockscreen)
+ emulateUiSceneTransition()
+ }
+
/**
* Asserts that the current scene in the view-model matches what's expected.
*
@@ -392,7 +416,9 @@
// Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
// lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
// is not an observable that can trigger a new evaluation.
- authenticationRepository.setLockscreenEnabled(authMethod !is AuthenticationMethodModel.None)
+ authenticationRepository.setLockscreenEnabled(
+ authMethod !is DomainLayerAuthenticationMethodModel.None
+ )
authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer())
if (!authMethod.isSecure) {
// When the auth method is not secure, the device is never considered locked.
@@ -432,6 +458,7 @@
fromScene = getCurrentSceneInUi(),
toScene = to.key,
progress = progressFlow,
+ isUserInputDriven = false,
)
runCurrent()
@@ -450,6 +477,19 @@
assertWithMessage("Visibility mismatch after scene transition from $from to ${to.key}!")
.that(sceneContainerViewModel.isVisible.value)
.isEqualTo(expectedVisible)
+
+ bouncerSceneJob =
+ if (to.key == SceneKey.Bouncer) {
+ testScope.backgroundScope.launch {
+ bouncerViewModel.authMethod.collect {
+ // Do nothing. Need this to turn this otherwise cold flow, hot.
+ }
+ }
+ } else {
+ bouncerSceneJob?.cancel()
+ null
+ }
+ runCurrent()
}
/**
@@ -568,4 +608,16 @@
lockDevice()
}
}
+
+ /** Emulates the dismissal of the IME (soft keyboard). */
+ private fun TestScope.dismissIme(
+ showImeBeforeDismissing: Boolean = true,
+ ) {
+ if (showImeBeforeDismissing) {
+ bouncerViewModel.authMethod.value?.onImeVisibilityChanged(true)
+ }
+
+ bouncerViewModel.authMethod.value?.onImeVisibilityChanged(false)
+ runCurrent()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 181f8a7..ff28d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -119,6 +119,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ed716a9..afc0e69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -86,6 +86,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -123,6 +124,7 @@
fromScene = underTest.desiredScene.value.key,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
assertThat(transitionTo).isEqualTo(SceneKey.Shade)
@@ -158,7 +160,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -176,7 +179,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
underTest.setTransitionState(transitionState)
@@ -192,7 +196,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -219,7 +224,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
- progress = flowOf(0.5f)
+ progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(transitioning).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 145629a..16fdf8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -109,6 +109,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -121,6 +122,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Gone,
progress = flowOf(0.5f),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 17ee3a1..f6195aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -78,8 +78,8 @@
@JvmStatic
fun testCases() = buildList {
repeat(4) { combination ->
- val isComposeAvailable = combination and 0b100 != 0
- val areAllFlagsSet = combination and 0b001 != 0
+ val isComposeAvailable = combination and 0b10 != 0
+ val areAllFlagsSet = combination and 0b01 != 0
val expectedEnabled = isComposeAvailable && areAllFlagsSet
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
deleted file mode 100644
index 03d9444..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-/*
- * Copyright (C) 2022 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.screenrecord
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserContextProvider
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class ScreenRecordDialogTest : SysuiTestCase() {
-
- @Mock
- private lateinit var starter: ActivityStarter
- @Mock
- private lateinit var controller: RecordingController
- @Mock
- private lateinit var userContextProvider: UserContextProvider
- @Mock
- private lateinit var flags: FeatureFlags
- @Mock
- private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
- @Mock
- private lateinit var onStartRecordingClicked: Runnable
-
- private lateinit var dialog: ScreenRecordDialog
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- dialog = ScreenRecordDialog(
- context, controller, starter, userContextProvider, flags, dialogLaunchAnimator,
- onStartRecordingClicked
- )
- }
-
- @After
- fun teardown() {
- if (::dialog.isInitialized) {
- dialog.dismiss()
- }
- }
-
- @Test
- fun testShowDialog_partialScreenSharingDisabled_appButtonIsNotVisible() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
-
- dialog.show()
-
- val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
- assertThat(visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testShowDialog_partialScreenSharingEnabled_appButtonIsVisible() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-
- dialog.show()
-
- val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
- assertThat(visibility).isEqualTo(View.VISIBLE)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index ad6909d..d470d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -22,9 +22,8 @@
import android.view.View
import android.widget.Spinner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
@@ -36,8 +35,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -48,7 +47,6 @@
@Mock private lateinit var controller: RecordingController
@Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var flags: FeatureFlags
- @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
@Mock private lateinit var onStartRecordingClicked: Runnable
private lateinit var dialog: ScreenRecordPermissionDialog
@@ -63,7 +61,6 @@
UserHandle.of(0),
controller,
starter,
- dialogLaunchAnimator,
userContextProvider,
onStartRecordingClicked
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
index ca4486b..36639d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
@@ -22,7 +22,7 @@
import android.net.Uri
import androidx.test.ext.truth.content.IntentSubject.assertThat as assertThatIntent
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index d672056..d4e8d37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -61,19 +61,19 @@
workProfileData = WorkProfileMessageController.WorkProfileFirstRunData(appName, icon)
val guideline = Guideline(mContext)
- guideline.id = com.android.systemui.R.id.guideline
+ guideline.id = com.android.systemui.res.R.id.guideline
screenshotView.addView(guideline)
container = FrameLayout(mContext)
- container.id = com.android.systemui.R.id.screenshot_message_container
+ container.id = com.android.systemui.res.R.id.screenshot_message_container
screenshotView.addView(container)
workProfileFirstRunView = FrameLayout(mContext)
- workProfileFirstRunView.id = com.android.systemui.R.id.work_profile_first_run
+ workProfileFirstRunView.id = com.android.systemui.res.R.id.work_profile_first_run
container.addView(workProfileFirstRunView)
detectionNoticeView = FrameLayout(mContext)
- detectionNoticeView.id = com.android.systemui.R.id.screenshot_detection_notice
+ detectionNoticeView.id = com.android.systemui.res.R.id.screenshot_detection_notice
container.addView(detectionNoticeView)
messageContainer.setView(screenshotView)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index cfdf66e..a105c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -180,7 +180,7 @@
}
@Test
- fun executeScreenshots_doesNotReportFinishedIfOneFinishesOtherFails() =
+ fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri -> }
@@ -207,7 +207,7 @@
}
@Test
- fun executeScreenshots_doesNotReportFinishedAfterOneFails() =
+ fun executeScreenshots_allDisplaysFail_reportsFail() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri -> }
@@ -224,11 +224,12 @@
capturer0.value.reportError()
verify(callback, never()).onFinish()
- verify(callback).reportError()
+ verify(callback, never()).reportError()
- capturer1.value.onFinish()
+ capturer1.value.reportError()
verify(callback, never()).onFinish()
+ verify(callback).reportError()
screenshotExecutor.onDestroy()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index 31f7771..10bd6af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -38,7 +38,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.FakeSharedPreferences;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index ab91d9f..0f33aaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -47,7 +47,7 @@
import androidx.test.runner.intercepting.SingleActivityFactory;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ImageExporter;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
index 674ce9c..3d55527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
@@ -57,7 +57,7 @@
import com.android.internal.infra.ServiceConnector;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IAppClipsService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index beb981d..1bb2ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -11,10 +11,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +63,9 @@
@Mock private lateinit var callback: UserTracker.Callback
@Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
+ private val fakeFeatures = FakeFeatureFlagsClassic()
+ private val testDispatcher = StandardTestDispatcher()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -68,12 +74,21 @@
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
-
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
- fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
+ fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() = runTest {
+ tracker =
+ UserTrackerImpl(
+ context,
+ { fakeFeatures },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ this,
+ testDispatcher,
+ handler
+ )
tracker.initialize(0)
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index aa98f08..52b25f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -26,16 +26,25 @@
import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
-import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
+import com.google.common.truth.TruthJUnit.assume
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
@@ -43,28 +52,52 @@
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(Parameterized::class)
class UserTrackerImplTest : SysuiTestCase() {
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
+ }
+
@Mock
private lateinit var context: Context
+
@Mock
private lateinit var userManager: UserManager
+
@Mock
private lateinit var iActivityManager: IActivityManager
+
@Mock
private lateinit var userSwitchingReply: IRemoteCallback
+
@Mock(stubOnly = true)
private lateinit var dumpManager: DumpManager
+
@Mock(stubOnly = true)
private lateinit var handler: Handler
+ @Parameterized.Parameter
+ @JvmField
+ var isBackgroundUserTrackerEnabled: Boolean = false
+
+ private val testScope = TestScope()
+ private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
private val executor = Executor(Runnable::run)
+ private val featureFlags = FakeFeatureFlagsClassic()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -84,54 +117,65 @@
listOf(info)
}
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
+ featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
+ tracker =
+ UserTrackerImpl(
+ context,
+ { featureFlags },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ testScope.backgroundScope,
+ testDispatcher,
+ handler,
+ )
}
@Test
- fun testNotInitialized() {
+ fun testNotInitialized() = testScope.runTest {
assertThat(tracker.initialized).isFalse()
}
@Test(expected = IllegalStateException::class)
- fun testGetUserIdBeforeInitThrowsException() {
+ fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
tracker.userId
}
@Test(expected = IllegalStateException::class)
- fun testGetUserHandleBeforeInitThrowsException() {
+ fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
tracker.userHandle
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContextBeforeInitThrowsException() {
+ fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
tracker.userContext
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContentResolverBeforeInitThrowsException() {
+ fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
tracker.userContentResolver
}
@Test(expected = IllegalStateException::class)
- fun testGetUserProfilesBeforeInitThrowsException() {
+ fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
tracker.userProfiles
}
@Test
- fun testInitialize() {
+ fun testInitialize() = testScope.runTest {
tracker.initialize(0)
assertThat(tracker.initialized).isTrue()
}
@Test
- fun testReceiverRegisteredOnInitialize() {
+ fun testReceiverRegisteredOnInitialize() = testScope.runTest {
tracker.initialize(0)
val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
- verify(context).registerReceiverForAllUsers(
- eq(tracker), capture(captor), isNull(), eq(handler))
+ verify(context)
+ .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
assertThat(countActions()).isEqualTo(6)
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
@@ -144,7 +188,7 @@
}
@Test
- fun testInitialValuesSet() {
+ fun testInitialValuesSet() = testScope.runTest {
val testID = 4
tracker.initialize(testID)
@@ -161,7 +205,7 @@
}
@Test
- fun testUserSwitch() {
+ fun testUserSwitch() = testScope.runTest {
tracker.initialize(0)
val newID = 5
@@ -169,6 +213,7 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -184,7 +229,7 @@
}
@Test
- fun testManagedProfileAvailable() {
+ fun testManagedProfileAvailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -209,7 +254,8 @@
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileUnavailable() {
+ @Test
+ fun testManagedProfileUnavailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -234,7 +280,8 @@
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileStartedAndRemoved() {
+ @Test
+ fun testManagedProfileStartedAndRemoved() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -271,7 +318,7 @@
}
@Test
- fun testCallbackNotCalledOnAdd() {
+ fun testCallbackNotCalledOnAdd() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
@@ -282,7 +329,7 @@
}
@Test
- fun testCallbackCalledOnUserChanging() {
+ fun testCallbackCalledOnUserChanging() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -293,15 +340,49 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
- verify(userSwitchingReply).sendResult(any())
+ runCurrent()
+ verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
}
@Test
- fun testCallbackCalledOnUserChanged() {
+ fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
+ // Skip this test for CountDownLatch variation. The problem is that there would be a
+ // deadlock if the callbacks processing runs on the same thread as the callback (which
+ // is blocked by the latch). Before the change it works because the callbacks are
+ // processed on a binder thread which is always distinct.
+ // This is the issue that this feature addresses.
+ assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+ tracker.initialize(0)
+ val callback = TestCallback()
+ val callbackExecutor = FakeExecutor(FakeSystemClock())
+ tracker.addCallback(callback, callbackExecutor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
+ verify(userSwitchingReply, never()).sendResult(any())
+
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ verify(userSwitchingReply).sendResult(any())
+ }
+
+ @Test
+ fun testCallbackCalledOnUserChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -312,6 +393,7 @@
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitchComplete(newID)
+ runCurrent()
assertThat(callback.calledOnUserChanged).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -321,7 +403,7 @@
}
@Test
- fun testCallbackCalledOnUserInfoChanged() {
+ fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -331,18 +413,18 @@
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
}
val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intent)
@@ -352,7 +434,7 @@
}
@Test
- fun testCallbackRemoved() {
+ fun testCallbackRemoved() = testScope.runTest {
tracker.initialize(0)
val newID = 5
val profileID = newID + 10
@@ -364,6 +446,7 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index ed1397f..152be65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -25,7 +25,7 @@
import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index d75405f..707a297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -40,7 +40,6 @@
import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
import org.mockito.Mockito.notNull
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -61,6 +60,8 @@
private lateinit var motionEvent: MotionEvent
@Mock
private lateinit var listener: ToggleSlider.Listener
+ @Mock
+ private lateinit var mBrightnessSliderHapticPlugin: BrightnessSliderHapticPlugin
@Captor
private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
@@ -79,7 +80,12 @@
whenever(motionEvent.copy()).thenReturn(motionEvent)
mController =
- BrightnessSliderController(brightnessSliderView, mFalsingManager, uiEventLogger)
+ BrightnessSliderController(
+ brightnessSliderView,
+ mFalsingManager,
+ uiEventLogger,
+ mBrightnessSliderHapticPlugin,
+ )
mController.init()
mController.setOnChangedListener(listener)
}
@@ -94,6 +100,7 @@
mController.onViewAttached()
verify(brightnessSliderView).setOnSeekBarChangeListener(notNull())
+ verify(mBrightnessSliderHapticPlugin).start()
}
@Test
@@ -103,6 +110,7 @@
verify(brightnessSliderView).setOnSeekBarChangeListener(isNull())
verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
+ verify(mBrightnessSliderHapticPlugin).stop()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt
new file mode 100644
index 0000000..51629b5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.settings.brightness
+
+import android.view.VelocityTracker
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.slider.SeekableSliderEventProducer
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BrightnessSliderHapticPluginImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var velocityTracker: VelocityTracker
+ @Mock private lateinit var mainDispatcher: CoroutineDispatcher
+
+ private val systemClock = FakeSystemClock()
+ private val sliderEventProducer = SeekableSliderEventProducer()
+
+ private lateinit var plugin: BrightnessSliderHapticPluginImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(vibratorHelper.getPrimitiveDurations(anyInt())).thenReturn(intArrayOf(0))
+ }
+
+ @Test
+ fun start_beginsTrackingSlider() = runTest {
+ createPlugin(UnconfinedTestDispatcher(testScheduler))
+ plugin.start()
+
+ assertThat(plugin.isTracking).isTrue()
+ }
+
+ @Test
+ fun stop_stopsTrackingSlider() = runTest {
+ createPlugin(UnconfinedTestDispatcher(testScheduler))
+ // GIVEN that the plugin started the tracking component
+ plugin.start()
+
+ // WHEN called to stop
+ plugin.stop()
+
+ // THEN the tracking component stops
+ assertThat(plugin.isTracking).isFalse()
+ }
+
+ @Test
+ fun start_afterStop_startsTheTrackingAgain() = runTest {
+ createPlugin(UnconfinedTestDispatcher(testScheduler))
+ // GIVEN that the plugin started the tracking component
+ plugin.start()
+
+ // WHEN the plugin is restarted
+ plugin.stop()
+ plugin.start()
+
+ // THEN the tracking begins again
+ assertThat(plugin.isTracking).isTrue()
+ }
+
+ private fun createPlugin(dispatcher: CoroutineDispatcher) {
+ plugin =
+ BrightnessSliderHapticPluginImpl(
+ vibratorHelper,
+ systemClock,
+ dispatcher,
+ velocityTracker,
+ sliderEventProducer,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index 0a1eca6..2b3588a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -22,7 +22,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
index 38a666e..bff408a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
@@ -20,7 +20,7 @@
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 9130bc2..31c8a3d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -79,7 +79,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 638c266..37ec762 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.shade;
-import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -58,7 +57,8 @@
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.DejankUtils;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.shared.model.WakeSleepReason;
import com.android.systemui.keyguard.shared.model.WakefulnessModel;
import com.android.systemui.keyguard.shared.model.WakefulnessState;
@@ -197,6 +197,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 20,
/* indicationPadding= */ 0,
@@ -209,6 +210,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 30,
@@ -221,6 +223,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 0,
@@ -233,6 +236,7 @@
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
+ enableSplitShade(/* enabled= */ false);
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 10,
/* indicationPadding= */ 8,
@@ -244,6 +248,19 @@
}
@Test
+ public void getVerticalSpaceForLockscreenShelf_splitShade() {
+ enableSplitShade(/* enabled= */ true);
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 10,
+ /* indicationPadding= */ 8,
+ /* ambientPadding= */ 0);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
+ }
+
+ @Test
public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() {
mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
mNotificationPanelViewController.getMaxPanelHeight() / 2);
@@ -817,6 +834,42 @@
}
@Test
+ public void switchesToBigClockInSplitShadeOn_landFlagOn_ForceSmallClock() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ false);
+ mNotificationPanelViewController.setDozing(false, false);
+ when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ clearInvocations(mKeyguardStatusViewController);
+
+ enableSplitShade(/* enabled= */ true);
+ mNotificationPanelViewController.updateResources();
+
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ false);
+ }
+
+ @Test
+ public void switchesToBigClockInSplitShadeOn_landFlagOff_DontForceSmallClock() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ false);
+ mNotificationPanelViewController.setDozing(false, false);
+ when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ clearInvocations(mKeyguardStatusViewController);
+
+ enableSplitShade(/* enabled= */ true);
+ mNotificationPanelViewController.updateResources();
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
+ }
+
+ @Test
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
@@ -908,8 +961,10 @@
@Test
public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
// to make sure shade is in expanded state
- mNotificationPanelViewController.startWaitingForExpandGesture();
+ mNotificationPanelViewController.startInputFocusTransfer();
// switch to split shade from portrait (default state)
enableSplitShade(/* enabled= */ true);
@@ -1047,7 +1102,18 @@
mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
+ }
+ @Test
+ public void nsslFlagEnabled_allowOnlyExternalTouches() {
+ when(mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)).thenReturn(true);
+
+ // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
+ mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
+ verify(mQsController, never()).disallowTouches();
+
+ mNotificationPanelViewController.handleExternalInterceptTouch(mDownMotionEvent);
+ verify(mQsController).disallowTouches();
}
@Test
@@ -1171,18 +1237,50 @@
}
@Test
- public void shadeExpanded_whenWaitingForExpandGesture() {
- mNotificationPanelViewController.startWaitingForExpandGesture();
+ public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
+ when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
}
@Test
- public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
- when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
+ public void shadeExpanded_whenInputFocusTransferStarted() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
+ mNotificationPanelViewController.startInputFocusTransfer();
+
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
}
@Test
+ public void shadeNotExpanded_whenInputFocusTransferStartedButPanelsDisabled() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(false);
+
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void cancelInputFocusTransfer_shadeCollapsed() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ mNotificationPanelViewController.cancelInputFocusTransfer();
+
+ assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void finishInputFocusTransfer_shadeFlingingOpen() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ mNotificationPanelViewController.startInputFocusTransfer();
+
+ mNotificationPanelViewController.finishInputFocusTransfer(/* velocity= */ 0f);
+
+ assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
+ }
+
+ @Test
public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() {
mFakeKeyguardRepository.setWakefulnessModel(
new WakefulnessModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index b0125f8..aead53e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.CollectionUtils
import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index dfd782b..2d00e8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -46,7 +46,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 39fe498..9651072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.KeyEvent
@@ -24,30 +25,44 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -61,6 +76,7 @@
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -124,6 +140,7 @@
private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock lateinit var dragDownHelper: DragDownHelper
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
@Mock lateinit var keyEventInteractor: KeyEventInteractor
@@ -137,6 +154,8 @@
private lateinit var testScope: TestScope
+ private lateinit var featureFlags: FakeFeatureFlags
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -150,12 +169,13 @@
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>())
- val featureFlags = FakeFeatureFlags()
+ featureFlags = FakeFeatureFlags()
featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+ featureFlags.set(Flags.MIGRATE_NSSL, false)
testScope = TestScope()
fakeClock = FakeSystemClock()
@@ -196,16 +216,41 @@
featureFlags,
fakeClock,
BouncerMessageInteractor(
- FakeBouncerMessageRepository(),
- mock(BouncerMessageFactory::class.java),
- FakeUserRepository(),
- CountDownTimerUtil(),
- featureFlags
+ repository = BouncerMessageRepositoryImpl(),
+ userRepository = FakeUserRepository(),
+ countDownTimerUtil = mock(CountDownTimerUtil::class.java),
+ featureFlags = featureFlags,
+ updateMonitor = mock(KeyguardUpdateMonitor::class.java),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ applicationScope = testScope.backgroundScope,
+ trustRepository = FakeTrustRepository(),
+ systemPropertiesHelper = mock(SystemPropertiesHelper::class.java),
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ FakeKeyguardBouncerRepository(),
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ context,
+ mock(KeyguardUpdateMonitor::class.java),
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ ),
+ facePropertyRepository = FakeFacePropertyRepository(),
+ deviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository(),
+ faceAuthRepository = FakeDeviceEntryFaceAuthRepository(),
+ securityModel = mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
keyEventInteractor,
)
underTest.setupExpandedStatusBar()
+ underTest.setDragDownHelper(dragDownHelper)
interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
@@ -347,9 +392,8 @@
testScope.runTest {
// GIVEN touch dispatcher in a state that returns true
underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(
- true
- )
+ whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ .thenReturn(true)
assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
// WHEN launch animation is running for 2 seconds
@@ -381,6 +425,32 @@
}
@Test
+ fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
+ // GIVEN not dozing
+ whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
+ // AND alternate bouncer doesn't want the touch
+ whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+ .thenReturn(false)
+ // AND the lock icon doesn't want the touch
+ whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+ // AND the notification panel can accept touches
+ whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
+ whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
+ whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
+
+ // AND the drag down helper doesn't want the touch (to pull the shade down)
+ whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+
+ featureFlags.set(Flags.MIGRATE_NSSL, true)
+
+ // WHEN asked if should intercept touch
+ interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+
+ // Verify that NPVC gets a chance to use the touch
+ verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+ }
+
+ @Test
fun testGetKeyguardMessageArea() =
testScope.runTest {
underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 3031658..0023020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shade
+import android.os.Handler
import android.os.SystemClock
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -23,28 +24,41 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -60,6 +74,7 @@
import com.android.systemui.statusbar.phone.DozeScrimController
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -164,6 +179,7 @@
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+ featureFlags.set(Flags.MIGRATE_NSSL, false)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
@@ -202,11 +218,35 @@
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(
- FakeBouncerMessageRepository(),
- Mockito.mock(BouncerMessageFactory::class.java),
- FakeUserRepository(),
- CountDownTimerUtil(),
- featureFlags
+ repository = BouncerMessageRepositoryImpl(),
+ userRepository = FakeUserRepository(),
+ countDownTimerUtil = Mockito.mock(CountDownTimerUtil::class.java),
+ featureFlags = featureFlags,
+ updateMonitor = Mockito.mock(KeyguardUpdateMonitor::class.java),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ applicationScope = testScope.backgroundScope,
+ trustRepository = FakeTrustRepository(),
+ systemPropertiesHelper = Mockito.mock(SystemPropertiesHelper::class.java),
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ FakeKeyguardBouncerRepository(),
+ Mockito.mock(BouncerView::class.java),
+ Mockito.mock(Handler::class.java),
+ Mockito.mock(KeyguardStateController::class.java),
+ Mockito.mock(KeyguardSecurityModel::class.java),
+ Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
+ Mockito.mock(FalsingCollector::class.java),
+ Mockito.mock(DismissCallbackRegistry::class.java),
+ context,
+ Mockito.mock(KeyguardUpdateMonitor::class.java),
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ ),
+ facePropertyRepository = FakeFacePropertyRepository(),
+ deviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository(),
+ faceAuthRepository = FakeDeviceEntryFaceAuthRepository(),
+ securityModel = Mockito.mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
Mockito.mock(KeyEventInteractor::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 36cf1d9..e70dbc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -101,7 +101,11 @@
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, false) }
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.MIGRATE_NSSL, false)
+ set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false)
+ }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 090bee2..ac8c924 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -100,7 +100,11 @@
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, true) }
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.MIGRATE_NSSL, true)
+ set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true)
+ }
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
index 8bb8f62..f7d2497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -22,7 +22,7 @@
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSFragment
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index d421aca..2cd740d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -6,7 +6,7 @@
import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 849127e..fb0d4db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -34,7 +34,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 6d288e1..5ca45f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -41,7 +41,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QS;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 7d5f68e..56061f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -35,7 +35,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
index dae9c97..a657edf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
@@ -31,7 +31,7 @@
import androidx.test.filters.SmallTest;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 9275ccb..d018cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -402,6 +402,7 @@
assertThat(actual).isEqualTo(0.8f)
}
+ @Test
fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
@@ -410,27 +411,31 @@
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
overrideResource(R.bool.config_use_split_notification_shade, true)
configurationRepository.onAnyConfigurationChange()
- runCurrent()
shadeRepository.setQsExpansion(.5f)
shadeRepository.setLegacyShadeExpansion(.7f)
+ runCurrent()
// THEN legacy shade expansion is passed through
assertThat(actual).isEqualTo(.7f)
}
+ @Test
fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
// WHEN split shade is not enabled and QS is expanded
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
shadeRepository.setQsExpansion(.5f)
shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
// THEN shade expansion is zero
assertThat(actual).isEqualTo(0f)
}
+ @Test
fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
@@ -471,7 +476,7 @@
@Test
fun expanding_shadeDraggedDown_expandingTrue() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade and QS collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -489,7 +494,7 @@
@Test
fun expanding_qsDraggedDown_expandingTrue() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade and QS collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -507,7 +512,7 @@
@Test
fun expanding_shadeDraggedUpAndDown() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// WHEN shade starts collapsed then partially expanded
shadeRepository.setLegacyShadeExpansion(0f)
@@ -532,7 +537,7 @@
// THEN anyExpanding is still true
assertThat(actual).isTrue()
- // WHEN shade fully shadeExpanded
+ // WHEN shade fully expanded
shadeRepository.setLegacyShadeExpansion(1f)
runCurrent()
@@ -550,7 +555,7 @@
@Test
fun expanding_shadeDraggedDownThenUp_expandingFalse() =
testScope.runTest() {
- val actual by collectLastValue(underTest.anyExpanding)
+ val actual by collectLastValue(underTest.isAnyExpanding)
// GIVEN shade starts collapsed
shadeRepository.setLegacyShadeExpansion(0f)
@@ -618,6 +623,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -654,6 +660,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -689,6 +696,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -708,4 +716,415 @@
// THEN expansion is still 0
assertThat(expansionAmount).isEqualTo(0f)
}
+
+ @Test
+ fun userInteractingWithShade_shadeDraggedUpAndDown() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed but tracking is not stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged halfway and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(.6f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade completes expansion stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeExpanded() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged down halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully expanded and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadePartiallyExpanded() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade collapsed and not tracking input
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade partially expanded
+ shadeRepository.setLegacyShadeExpansion(.4f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN tracking is stopped
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade goes back to collapsed
+ shadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeCollapsed() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithShade)
+ // GIVEN shade expanded and not tracking input
+ shadeRepository.setLegacyShadeExpansion(1f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN shade tracking starts
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade dragged up halfway
+ shadeRepository.setLegacyShadeExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN shade fully collapsed and tracking is stopped
+ shadeRepository.setLegacyShadeExpansion(0f)
+ shadeRepository.setLegacyShadeTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun userInteractingWithQs_qsDraggedUpAndDown() =
+ testScope.runTest() {
+ val actual by collectLastValue(underTest.isUserInteractingWithQs)
+ // GIVEN qs collapsed and not tracking input
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+
+ // WHEN qs tracking starts
+ shadeRepository.setLegacyQsTracking(true)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged down halfway
+ shadeRepository.setQsExpansion(.5f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully expanded but tracking is not stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs fully collapsed but tracking is not stopped
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs dragged halfway and tracking is stopped
+ shadeRepository.setQsExpansion(.6f)
+ shadeRepository.setLegacyQsTracking(false)
+ runCurrent()
+
+ // THEN user is interacting
+ assertThat(actual).isTrue()
+
+ // WHEN qs completes expansion stopped
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+
+ // THEN user is not interacting
+ assertThat(actual).isFalse()
+ }
+ @Test
+ fun userInteracting_idle() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is idle
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_programmatic() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isUserInputDriven = false,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_userInputDriven() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_programmatic() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isUserInputDriven = false,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_userInputDriven() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toAndFromDifferentScenes() =
+ testScope.runTest() {
+ // GIVEN an interacting flow based on transitions to and from a scene
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+ val interacting by collectLastValue(interactingFlow)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.QuickSettings,
+ progress = progress,
+ isUserInputDriven = true,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ assertThat(interacting).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 19d59fd..7463e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -169,6 +169,15 @@
}
@Test
+ fun updateLegacyExpandedOrAwaitingInputTransfer() =
+ testScope.runTest {
+ assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(false)
+
+ underTest.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(true)
+ }
+
+ @Test
fun updateUdfpsTransitionToFullShadeProgress() =
testScope.runTest {
assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
index 36f82c2..a844bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
@@ -2,7 +2,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index a09e844..0925858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -83,7 +83,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
@@ -100,7 +101,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
@@ -117,7 +119,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
- progress = MutableStateFlow(0.5f)
+ progress = MutableStateFlow(0.5f),
+ isUserInputDriven = false,
)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
index f5a24ff..eb418fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
@@ -22,7 +22,7 @@
import android.testing.AndroidTestingRunner
import android.util.AttributeSet
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.shared.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.shadow.DoubleShadowTextClock
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
index e92368d..7b0cd19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -20,7 +20,7 @@
import android.view.LayoutInflater
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
-import com.android.systemui.R
+import com.android.systemui.customization.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.TextAnimator
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 3f3faf7..bd3dae4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -24,7 +24,7 @@
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.customization.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 88d853e..8f06e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -181,29 +181,30 @@
String ACTION = "testAction";
}
- public void assertInstances(Integer allocated, Integer created) {
- // Run the garbage collector to finalize and deallocate outstanding
- // instances. Since the GC doesn't always appear to want to run
- // completely when we ask, we ask it 10 times in a short loop.
- for (int i = 0; i < 10; i++) {
+ private void assertInstances(int allocated, int created) {
+ // If there are more than the expected number of allocated instances, then we run the
+ // garbage collector to finalize and deallocate any outstanding non-referenced instances.
+ // Since the GC doesn't always appear to want to run completely when we ask, we do this up
+ // to 10 times before failing the test.
+ for (int i = 0; mCounter.getAllocatedInstances() > allocated && i < 10; i++) {
System.runFinalization();
System.gc();
}
- mCounter.assertInstances(allocated, created);
+ assertEquals(allocated, mCounter.getAllocatedInstances());
+ assertEquals(created, mCounter.getCreatedInstances());
}
public static class RefCounter {
public final AtomicInteger mAllocatedInstances = new AtomicInteger();
public final AtomicInteger mCreatedInstances = new AtomicInteger();
- public void assertInstances(Integer allocated, Integer created) {
- if (allocated != null) {
- assertEquals(allocated.intValue(), mAllocatedInstances.get());
- }
- if (created != null) {
- assertEquals(created.intValue(), mCreatedInstances.get());
- }
+ public int getAllocatedInstances() {
+ return mAllocatedInstances.get();
+ }
+
+ public int getCreatedInstances() {
+ return mCreatedInstances.get();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 280897d..b98dc00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -39,7 +39,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index d44846e..63e46d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -56,7 +56,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 1240f26..2bcad1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -73,7 +73,7 @@
import com.android.keyguard.TrustGrantFlags;
import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 673d63f..0b4de34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -18,7 +18,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 2c9dfcc..6e990a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -6,7 +6,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index a258f67..68bc72b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -23,7 +23,7 @@
import android.view.View
import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index c75aa81..544860e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -36,7 +36,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 8d016e3..1592c30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -52,7 +52,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
index aab4bc3..b905825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
@@ -75,6 +75,18 @@
}
@Test
+ fun testVibrate5() {
+ vibratorHelper.vibrate(
+ mock(VibrationEffect::class.java),
+ mock(VibrationAttributes::class.java)
+ )
+ verifyAsync().vibrate(
+ any(VibrationEffect::class.java),
+ any(VibrationAttributes::class.java)
+ )
+ }
+
+ @Test
fun testPerformHapticFeedback() {
val constant = HapticFeedbackConstants.CONFIRM
vibratorHelper.performHapticFeedback(view, constant)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 2d29c80..44e3bb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -29,7 +29,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
-import com.android.systemui.tests.R;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
@@ -179,7 +179,7 @@
@Test
public void testSignalCallback_setIsAirplaneMode() {
IconState state =
- new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description");
+ new IconState(true, com.android.settingslib.R.drawable.stat_sys_airplane_mode, "Test Description");
mHandler.setIsAirplaneMode(state);
waitForCallbacks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 35b9814..375ca063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -41,7 +41,7 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
new file mode 100644
index 0000000..2c8900c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.statusbar.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeStatusBarModeRepository : StatusBarModeRepository {
+ override val isTransientShown = MutableStateFlow(false)
+ override val isInFullscreenMode = MutableStateFlow(false)
+
+ override fun showTransient() {
+ isTransientShown.value = true
+ }
+ override fun clearTransient() {
+ isTransientShown.value = false
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
new file mode 100644
index 0000000..27c1ba3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.statusbar.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class StatusBarModeRepositoryImplTest : SysuiTestCase() {
+ private val testScope = TestScope()
+ private val commandQueue = mock<CommandQueue>()
+
+ private val underTest =
+ StatusBarModeRepositoryImpl(
+ testScope.backgroundScope,
+ DISPLAY_ID,
+ commandQueue,
+ )
+ .apply { this.start() }
+
+ private val commandQueueCallback: CommandQueue.Callbacks
+ get() {
+ val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ return callbackCaptor.value
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_wrongDisplayId_notUpdated() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID + 1,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_notStatusBarType_notUpdated() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.navigationBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_true() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueShow_statusBarAndOtherTypes_true() {
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars().or(WindowInsets.Type.navigationBars()),
+ /* isGestureOnSystemBar= */ false,
+ )
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_wrongDisplayId_notUpdated() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ // GIVEN the wrong display ID
+ commandQueueCallback.abortTransient(DISPLAY_ID + 1, WindowInsets.Type.statusBars())
+
+ // THEN the old value remains
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_notStatusBarType_notUpdated() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ // GIVEN the wrong type
+ commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.navigationBars())
+
+ // THEN the old value remains
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.statusBars())
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_commandQueueAbort_statusBarAndOtherTypes_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ commandQueueCallback.abortTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars().or(WindowInsets.Type.captionBar()),
+ )
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isTransientShown_showTransient_true() {
+ underTest.showTransient()
+
+ assertThat(underTest.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun isTransientShown_clearTransient_false() {
+ // Start as true
+ commandQueueCallback.showTransient(
+ DISPLAY_ID,
+ WindowInsets.Type.statusBars(),
+ /* isGestureOnSystemBar= */ false,
+ )
+ assertThat(underTest.isTransientShown.value).isTrue()
+
+ underTest.clearTransient()
+
+ assertThat(underTest.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun isInFullscreenMode_visibleTypesHasStatusBar_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isInFullscreenMode_visibleTypesDoesNotHaveStatusBar_true() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun isInFullscreenMode_wrongDisplayId_notUpdated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isInFullscreenMode)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ )
+ assertThat(latest).isTrue()
+
+ onSystemBarAttributesChanged(
+ displayId = DISPLAY_ID + 1,
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ private fun onSystemBarAttributesChanged(
+ displayId: Int = DISPLAY_ID,
+ @WindowInsetsController.Appearance appearance: Int = APPEARANCE_OPAQUE_STATUS_BARS,
+ appearanceRegions: Array<AppearanceRegion> = emptyArray(),
+ navbarColorManagedByIme: Boolean = false,
+ @WindowInsetsController.Behavior behavior: Int = WindowInsetsController.BEHAVIOR_DEFAULT,
+ @WindowInsets.Type.InsetsType
+ requestedVisibleTypes: Int = WindowInsets.Type.defaultVisible(),
+ packageName: String = "package name",
+ letterboxDetails: Array<LetterboxDetails> = emptyArray(),
+ ) {
+ commandQueueCallback.onSystemBarAttributesChanged(
+ displayId,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibleTypes,
+ packageName,
+ letterboxDetails,
+ )
+ }
+
+ private companion object {
+ const val DISPLAY_ID = 5
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
index 88ddc2d..31e1fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -22,7 +22,7 @@
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import android.content.res.Configuration
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index 524ead2..a59ba07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -32,7 +32,7 @@
import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index 470d340..d86f8bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -16,7 +16,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.policy.HeadsUpUtil
-import com.android.systemui.tests.R
+import com.android.systemui.res.R
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.test.TestScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index f0abf2f..c664c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -33,7 +33,7 @@
import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 90f6201..ccc9dc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -50,7 +50,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
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 daa45db..f05436f 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
@@ -61,7 +61,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
index 3f61af0..ccef1d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
@@ -22,7 +22,7 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.FakeShadowView
import com.android.systemui.statusbar.notification.NotificationUtils
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
new file mode 100644
index 0000000..d52cbce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
@@ -0,0 +1,440 @@
+/*
+ * 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.statusbar.notification.row
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.systemui.res.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+
+private const val FREE_IMAGE_DELAY_MS = 4000L
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BigPictureIconManagerTest : SysuiTestCase() {
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val testableResources = context.orCreateTestableResources
+ private val imageLoader: ImageLoader = ImageLoader(context, testDispatcher)
+ private var mockConsumer: NotificationDrawableConsumer = mock()
+ private val drawableCaptor = argumentCaptor<Drawable>()
+
+ private lateinit var iconManager: BigPictureIconManager
+
+ private val expectedDrawable by lazy {
+ context.resources.getDrawable(R.drawable.dessert_zombiegingerbread, context.theme)
+ }
+ private val supportedIcon by lazy {
+ Icon.createWithContentUri(
+ Uri.parse(
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ )
+ )
+ }
+ private val unsupportedIcon by lazy {
+ Icon.createWithBitmap(
+ BitmapFactory.decodeResource(context.resources, R.drawable.dessert_donutburger)
+ )
+ }
+ private val invalidIcon by lazy { Icon.createWithContentUri(Uri.parse("this.is/broken")) }
+
+ @Before
+ fun setUp() {
+ iconManager =
+ BigPictureIconManager(
+ context,
+ imageLoader,
+ scope = testScope,
+ mainDispatcher = testDispatcher,
+ bgDispatcher = testDispatcher
+ )
+ }
+
+ @Test
+ fun onIconUpdated_supportedType_placeholderLoaded() =
+ testScope.runTest {
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with a placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_unsupportedType_fullImageLoaded() =
+ testScope.runTest {
+ // WHEN update with an unsupported icon
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+ // THEN consumer is updated with the full image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_withNull_drawableIsNull() =
+ testScope.runTest {
+ // WHEN update with null
+ iconManager.updateIcon(mockConsumer, null).run()
+
+ // THEN consumer is updated with null
+ verify(mockConsumer).setImageDrawable(null)
+ }
+
+ @Test
+ fun onIconUpdated_invalidIcon_drawableIsNull() =
+ testScope.runTest {
+ // WHEN update with an invalid icon
+ iconManager.updateIcon(mockConsumer, invalidIcon).run()
+
+ // THEN consumer is updated with null
+ verify(mockConsumer).setImageDrawable(null)
+ }
+
+ @Test
+ fun onIconUpdated_consumerAlreadySet_nothingHappens() =
+ testScope.runTest {
+ // GIVEN a consumer is set
+ val otherConsumer: NotificationDrawableConsumer = mock()
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN a new consumer is set
+ iconManager.updateIcon(otherConsumer, unsupportedIcon).run()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer, otherConsumer)
+ }
+
+ @Test
+ fun onIconUpdated_iconAlreadySet_loadsNewIcon() =
+ testScope.runTest {
+ // GIVEN an icon is set
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN a new icon is set
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+ // THEN consumer is updated with the new image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_iconAlreadySetForTheSameIcon_loadsIconAgain() =
+ testScope.runTest {
+ // GIVEN an icon is set
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ // AND the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+ // WHEN the icon is set again
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the new image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_iconAlreadySetForUnsupportedIcon_loadsNewIcon() =
+ testScope.runTest {
+ // GIVEN an unsupported icon is set
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ // AND the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN a new icon is set
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the new image
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onIconUpdated_supportedTypeButTooWide_resizedPlaceholderLoaded() =
+ testScope.runTest {
+ // GIVEN the max width is smaller than our image
+ testableResources.addOverride(
+ com.android.internal.R.dimen.notification_big_picture_max_width,
+ 20
+ )
+ iconManager.updateMaxImageSizes()
+
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the resized placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+ }
+
+ @Test
+ fun onIconUpdated_supportedTypeButTooHigh_resizedPlaceholderLoaded() =
+ testScope.runTest {
+ // GIVEN the max height is smaller than our image
+ testableResources.addOverride(
+ com.android.internal.R.dimen.notification_big_picture_max_height,
+ 20
+ )
+ iconManager.updateMaxImageSizes()
+
+ // WHEN update with a supported icon
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+ // THEN consumer is updated with the resized placeholder
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+ }
+
+ @Test
+ fun onViewShown_placeholderShowing_fullImageLoaded() =
+ testScope.runTest {
+ // GIVEN placeholder is showing
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN full image is set
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsFullImage(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onViewHidden_fullImageShowing_placeHolderSet() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the view goes off the screen
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN placeholder is set
+ verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+ assertIsPlaceHolder(drawableCaptor.value)
+ assertSize(drawableCaptor.value)
+ }
+
+ @Test
+ fun onViewShownToggled_viewShown_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the onViewShown is toggled
+ iconManager.onViewShown(false)
+ runCurrent()
+ iconManager.onViewShown(true)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewShown_unsupportedIconLoaded_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing for an unsupported icon
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewShown_nullSetForIcon_nothingHappens() =
+ testScope.runTest {
+ // GIVEN null is set for the icon
+ iconManager.updateIcon(mockConsumer, null).run()
+ reset(mockConsumer)
+
+ // WHEN the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewHidden_unsupportedIconLoadedAndViewIsShown_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing for an unsupported icon
+ iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+ // AND the view is shown
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the view goes off the screen
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewHidden_placeholderShowing_nothingHappens() =
+ testScope.runTest {
+ // GIVEN placeholder image is showing
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ reset(mockConsumer)
+
+ // WHEN the view is hidden
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewShown_alreadyShowing_nothingHappens() =
+ testScope.runTest {
+ // GIVEN full image is showing and the view is shown
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN view shown called again
+ iconManager.onViewShown(true)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun onViewHidden_alreadyHidden_nothingHappens() =
+ testScope.runTest {
+ // GIVEN placeholder image is showing and the view is hidden
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(false)
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+ reset(mockConsumer)
+
+ // WHEN the view is hidden again
+ iconManager.onViewShown(false)
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN nothing happens
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ @Test
+ fun cancelJobs_freeImageJobRunning_jobCancelled() =
+ testScope.runTest {
+ // GIVEN full image is showing
+ iconManager.updateIcon(mockConsumer, supportedIcon).run()
+ iconManager.onViewShown(true)
+ runCurrent()
+ reset(mockConsumer)
+ // AND the view has just gone off the screen
+ iconManager.onViewShown(false)
+
+ // WHEN cancelJobs is called
+ iconManager.cancelJobs()
+ // AND we wait a bit
+ advanceTimeBy(FREE_IMAGE_DELAY_MS)
+ runCurrent()
+
+ // THEN no more updates are happening
+ verifyZeroInteractions(mockConsumer)
+ }
+
+ private fun assertIsPlaceHolder(drawable: Drawable) {
+ assertThat(drawable).isInstanceOf(PlaceHolderDrawable::class.java)
+ }
+
+ private fun assertIsFullImage(drawable: Drawable) {
+ assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+ }
+
+ private fun assertSize(
+ drawable: Drawable,
+ expectedWidth: Int = expectedDrawable.intrinsicWidth,
+ expectedHeight: Int = expectedDrawable.intrinsicHeight
+ ) {
+ assertThat(drawable.intrinsicWidth).isEqualTo(expectedWidth)
+ assertThat(drawable.intrinsicHeight).isEqualTo(expectedHeight)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index cf1d2ca..c9b77c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -54,7 +54,7 @@
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 90cb734..b120c47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -31,7 +31,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index e52cb57..b0996ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -115,7 +115,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mBuilder = new Notification.Builder(mContext).setSmallIcon(
- R.drawable.ic_person)
+ com.android.systemui.res.R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text")
.setStyle(new Notification.BigTextStyle().bigText("big text"));
@@ -191,7 +191,7 @@
public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
mRow.getPrivateLayout().removeAllViews();
mRow.getEntry().getSbn().getNotification().contentView
- = new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
+ = new RemoteViews(mContext.getPackageName(), com.android.systemui.res.R.layout.status_bar);
inflateAndWait(true /* expectingException */, mNotificationInflater, FLAG_CONTENT_VIEW_ALL,
mRow);
assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 596e9a2..1763d9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -74,7 +74,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shade.ShadeController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
index fdfb4f4..7f9471e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
@@ -22,7 +22,7 @@
import android.view.LayoutInflater
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import org.junit.After
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 8dd0488..f0b4dd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -66,7 +66,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index f8f7af0..0a15f0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -23,7 +23,7 @@
import android.testing.UiThreadTest;
import android.util.KeyValueListParser;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 6386940..ac680e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -88,7 +88,7 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
-import com.android.systemui.tests.R;
+import com.android.systemui.res.R;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
@@ -495,7 +495,7 @@
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
- R.layout.custom_view_dark))
+ com.android.systemui.tests.R.layout.custom_view_dark))
.build();
Notification.Builder notificationBuilder = new Notification.Builder(mContext, "channelId")
.setSmallIcon(R.drawable.ic_person)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 12c8fd5..e42ce27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -52,7 +52,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
index 0909ff2..f4e236e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
@@ -5,7 +5,7 @@
import android.view.LayoutInflater
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.R
+import com.android.systemui.res.R
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 1dc0ab0..957cb88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -6,7 +6,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.flags.FakeFeatureFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index b3f5ea2..5606216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -180,7 +180,7 @@
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4307066..3a820e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -65,7 +65,7 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -154,6 +154,7 @@
assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+ mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
// Register the feature flags we use
// TODO: Ideally we wouldn't need to set these unless a test actually reads them,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index bc12bb0..2b3f9d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -21,7 +21,7 @@
import android.testing.AndroidTestingRunner
import android.view.View.VISIBLE
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index d9c3c3c..a52466d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -5,7 +5,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
index 7fc399b..198f278 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index bfc0910..e254dd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index e4a2236..416694b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -50,7 +50,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 3ad3c15..c71c0c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.QuickSettingsController;
@@ -79,6 +80,7 @@
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private CentralSurfaces mCentralSurfaces;
+ @Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private ShadeController mShadeController;
@Mock private CommandQueue mCommandQueue;
@Mock private QuickSettingsController mQuickSettingsController;
@@ -116,6 +118,7 @@
mQuickSettingsController,
mContext,
mContext.getResources(),
+ mScreenPinningRequest,
mShadeController,
mCommandQueue,
mShadeViewController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index bd3fb9f..5c3dde5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -82,9 +82,10 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -116,7 +117,6 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -146,6 +146,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -178,6 +179,7 @@
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
@@ -285,7 +287,6 @@
@Mock private PluginManager mPluginManager;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- @Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
@@ -454,6 +455,7 @@
emptySet()),
mStatusBarWindowController,
mStatusBarWindowStateController,
+ new FakeStatusBarModeRepository(),
mKeyguardUpdateMonitor,
mStatusBarSignalPolicy,
mPulseExpansionHandler,
@@ -472,6 +474,7 @@
new DisplayMetrics(),
mMetricsLogger,
mShadeLogger,
+ new JavaAdapter(TestScopeProvider.getTestScope()),
mUiBgExecutor,
mNotificationPanelViewController,
mNotificationMediaManager,
@@ -508,7 +511,6 @@
mDozeServiceHost,
mBackActionInteractor,
mPowerManager,
- mScreenPinningRequest,
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index bb20d18..1bc522d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -32,7 +32,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.AlertingNotificationManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 4aac841..ab441e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -20,7 +20,7 @@
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 5d2b59b..03f5f00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.util.BurnInHelperKt;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index d100c687..2b1ce0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -30,6 +30,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,9 +50,11 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
@@ -79,6 +83,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@Mock
private CarrierTextController mCarrierTextController;
@Mock
@@ -131,6 +136,7 @@
@Before
public void setup() throws Exception {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
mShadeViewStateProvider = new TestShadeViewStateProvider();
MockitoAnnotations.initMocks(this);
@@ -166,6 +172,7 @@
mBiometricUnlockController,
mStatusBarStateController,
mStatusBarContentInsetsProvider,
+ mFeatureFlags,
mUserManager,
mStatusBarUserChipViewModel,
mSecureSettings,
@@ -228,6 +235,30 @@
}
@Test
+ public void onViewReAttached_flagOff_iconManagerNotReRegistered() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
+ mController.onViewAttached();
+ mController.onViewDetached();
+ reset(mStatusBarIconController);
+
+ mController.onViewAttached();
+
+ verify(mStatusBarIconController, never()).addIconGroup(any());
+ }
+
+ @Test
+ public void onViewReAttached_flagOn_iconManagerReRegistered() {
+ mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+ mController.onViewAttached();
+ mController.onViewDetached();
+ reset(mStatusBarIconController);
+
+ mController.onViewAttached();
+
+ verify(mStatusBarIconController).addIconGroup(any());
+ }
+
+ @Test
public void setBatteryListening_true_callbackAdded() {
mController.setBatteryListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
index fe12051..e88fd95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 47f15a1..c5abd02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -27,7 +27,7 @@
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
index b70c6dd..d25a06b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
@@ -29,7 +29,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.util.concurrency.FakeExecutor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
index 4af1b24..c4568a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
@@ -19,7 +19,7 @@
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 3a629e6..b7560ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -27,7 +27,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ba4e8d3..bac8579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -976,4 +976,23 @@
verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
verify(mKeyguardMessageAreaController).setMessage(eq(""));
}
+
+ @Test
+ public void testShowBouncerOrKeyguard_needsFullScreen() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ verify(mCentralSurfaces).hideKeyguard();
+ verify(mPrimaryBouncerInteractor).show(true);
+ }
+
+ @Test
+ public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ verify(mCentralSurfaces, never()).hideKeyguard();
+ verify(mPrimaryBouncerInteractor, never()).show(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
index 63508e1..1455693 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
@@ -31,7 +31,7 @@
import android.widget.LinearLayout
import androidx.annotation.ColorInt
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.policy.FakeConfigurationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
index 4d79a53..0cd2214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
@@ -11,11 +11,9 @@
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
@@ -33,7 +31,6 @@
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var lightBarController: LightBarController
- @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var letterboxAppearanceCalculator: LetterboxAppearanceCalculator
@Mock private lateinit var centralSurfaces: CentralSurfaces
@@ -52,7 +49,6 @@
SystemBarAttributesListener(
centralSurfaces,
letterboxAppearanceCalculator,
- statusBarStateController,
lightBarController,
dumpManager)
}
@@ -81,23 +77,6 @@
}
@Test
- fun onSysBarAttrsChanged_forwardsAppearanceToStatusBarStateController() {
- changeSysBarAttrs(TEST_APPEARANCE)
-
- verify(statusBarStateController)
- .setSystemBarAttributes(eq(TEST_APPEARANCE), anyInt(), anyInt(), any())
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsLetterboxAppearanceToStatusBarStateCtrl() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
-
- verify(statusBarStateController)
- .setSystemBarAttributes(
- eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
- }
-
- @Test
fun onSysBarAttrsChanged_forwardsAppearanceToLightBarController() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS)
@@ -119,21 +98,9 @@
}
@Test
- fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToStatusBarStateController() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
-
- sysBarAttrsListener.onStatusBarBoundsChanged()
-
- verify(statusBarStateController)
- .setSystemBarAttributes(
- eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
- }
-
- @Test
fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToLightBarController() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
@@ -148,7 +115,7 @@
@Test
fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
@@ -158,11 +125,11 @@
@Test
fun onStatusBarBoundsChanged_previousCallEmptyLetterbox_doesNothing() {
changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf())
- reset(centralSurfaces, lightBarController, statusBarStateController)
+ reset(centralSurfaces, lightBarController)
sysBarAttrsListener.onStatusBarBoundsChanged()
- verifyZeroInteractions(centralSurfaces, lightBarController, statusBarStateController)
+ verifyZeroInteractions(centralSurfaces, lightBarController)
}
private fun changeSysBarAttrs(@Appearance appearance: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index b8f2cab..bcb34d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -48,7 +48,7 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
index ec074d7..f0a457e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
@@ -21,7 +21,7 @@
import android.view.LayoutInflater
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
index c3326b2..7e25aa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
@@ -21,7 +21,7 @@
import android.view.LayoutInflater
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 711e4ac..32320b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -30,21 +30,24 @@
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -62,7 +65,6 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.*
private const val CALL_UID = 900
@@ -75,23 +77,24 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
class OngoingCallControllerTest : SysuiTestCase() {
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
private val uiEventLoggerFake = UiEventLoggerFake()
+ private val testScope = TestScope()
+ private val statusBarModeRepository = FakeStatusBarModeRepository()
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
- @Mock private lateinit var mockOngoingCallFlags: OngoingCallFlags
@Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
@Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
- @Mock private lateinit var mockStatusBarStateController: StatusBarStateController
private lateinit var chipView: View
@@ -103,24 +106,23 @@
}
MockitoAnnotations.initMocks(this)
- `when`(mockOngoingCallFlags.isStatusBarChipEnabled()).thenReturn(true)
val notificationCollection = mock(CommonNotifCollection::class.java)
controller = OngoingCallController(
- context,
- notificationCollection,
- mockOngoingCallFlags,
- clock,
- mockActivityStarter,
- mainExecutor,
- mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake),
- DumpManager(),
- Optional.of(mockStatusBarWindowController),
- Optional.of(mockSwipeStatusBarAwayGestureHandler),
- mockStatusBarStateController,
- )
- controller.init()
+ testScope.backgroundScope,
+ context,
+ notificationCollection,
+ clock,
+ mockActivityStarter,
+ mainExecutor,
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ mockStatusBarWindowController,
+ mockSwipeStatusBarAwayGestureHandler,
+ statusBarModeRepository,
+ )
+ controller.start()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -494,44 +496,10 @@
}
@Test
- fun fullscreenIsTrue_thenCallNotificationAdded_chipNotClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+ fun fullscreenIsTrue_chipStillClickable() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- fun callNotificationAdded_thenFullscreenIsTrue_chipNotClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- fun fullscreenChangesToFalse_chipClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- // First, update to true
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
- // Then, update to false
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ false)
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
- @Test
- fun fullscreenIsTrue_butChipClickInImmersiveEnabled_chipClickable() {
- `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(true)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
assertThat(chipView.hasOnClickListeners()).isTrue()
}
@@ -540,7 +508,8 @@
@Test
fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -550,7 +519,8 @@
@Test
fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
- getStateListener().onFullscreenStateChanged(false)
+ statusBarModeRepository.isInFullscreenMode.value = false
+ testScope.runCurrent()
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -562,7 +532,8 @@
fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
verify(mockSwipeStatusBarAwayGestureHandler)
.addOnGestureDetectedCallback(anyString(), any())
@@ -570,11 +541,12 @@
@Test
fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockSwipeStatusBarAwayGestureHandler)
- getStateListener().onFullscreenStateChanged(false)
+ statusBarModeRepository.isInFullscreenMode.value = false
+ testScope.runCurrent()
verify(mockSwipeStatusBarAwayGestureHandler)
.removeOnGestureDetectedCallback(anyString())
@@ -582,7 +554,8 @@
@Test
fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
- getStateListener().onFullscreenStateChanged(true)
+ statusBarModeRepository.isInFullscreenMode.value = true
+ testScope.runCurrent()
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
reset(mockSwipeStatusBarAwayGestureHandler)
@@ -623,13 +596,6 @@
}
private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
-
- private fun getStateListener(): StatusBarStateController.StateListener {
- val statusBarStateListenerCaptor = ArgumentCaptor.forClass(
- StatusBarStateController.StateListener::class.java)
- verify(mockStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
- return statusBarStateListenerCaptor.value!!
- }
}
private val person = Person.Builder().setName("name").build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
index 5784be3..6028712 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 59fc0ac..218fd33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -24,7 +24,7 @@
import android.view.View
import android.widget.ImageView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.StatusBarIconView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 6624ec2..15b9d61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -17,8 +17,9 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -72,6 +74,8 @@
private val mobileConnectionRepository =
FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer)
+ private val internet = context.getString(R.string.quick_settings_internet_label)
+
@Before
fun setUp() {
mobileConnectionRepository.apply {
@@ -139,6 +143,9 @@
level = 4,
ssid = "test ssid",
)
+ val wifiIcon =
+ WifiIcon.fromModel(model = networkModel, context = context, showHotspotInfo = false)
+ as WifiIcon.Visible
connectivityRepository.setWifiConnected()
wifiRepository.setIsWifiDefault(true)
@@ -149,6 +156,10 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
assertThat(latest?.iconId).isNull()
+ assertThat(latest?.contentDescription.loadContentDescription(context))
+ .isEqualTo("$internet,test ssid")
+ val expectedSd = wifiIcon.contentDescription
+ assertThat(latest?.stateDescription).isEqualTo(expectedSd)
}
@Test
@@ -281,6 +292,11 @@
.isEqualTo(context.getString(R.string.quick_settings_networks_available))
assertThat(latest?.icon).isNull()
assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available)
+ assertThat(latest?.stateDescription).isNull()
+ val expectedCd =
+ "$internet,${context.getString(R.string.quick_settings_networks_available)}"
+ assertThat(latest?.contentDescription.loadContentDescription(context))
+ .isEqualTo(expectedCd)
}
@Test
@@ -300,6 +316,10 @@
assertThat(latest?.secondaryLabel).isNull()
assertThat(latest?.icon).isInstanceOf(SignalIcon::class.java)
assertThat(latest?.iconId).isNull()
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(latest?.secondaryTitle)
+ assertThat(latest?.contentDescription.loadContentDescription(context))
+ .isEqualTo(internet)
}
@Test
@@ -315,6 +335,9 @@
.isEqualTo(ethernetIcon!!.contentDescription.toString())
assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully)
assertThat(latest?.icon).isNull()
+ assertThat(latest?.stateDescription).isNull()
+ assertThat(latest?.contentDescription.loadContentDescription(context))
+ .isEqualTo(latest?.secondaryTitle)
}
@Test
@@ -330,6 +353,9 @@
.isEqualTo(ethernetIcon!!.contentDescription.toString())
assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet)
assertThat(latest?.icon).isNull()
+ assertThat(latest?.stateDescription).isNull()
+ assertThat(latest?.contentDescription.loadContentDescription(context))
+ .isEqualTo(latest?.secondaryTitle)
}
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index bbf048d..a27f899 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -25,7 +25,7 @@
import android.view.ViewGroup
import android.widget.ImageView
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
index 6557754..fb4ccb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index e5bbead..64ebcd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -49,7 +49,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index dc08aba..3fae3f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -38,7 +38,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index e431865..bf280c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -37,7 +37,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index b86ca6f..1250228 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -24,7 +24,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.user.UserSwitchDialogController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index 1ab62d0..0bd6a68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -25,7 +25,7 @@
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.internal.util.UserIcons
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.tiles.UserDetailItemView
import com.android.systemui.user.data.source.UserRecord
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
index cfb48a8..635d63e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
@@ -19,7 +19,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 79feb41..658e6b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -67,7 +67,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.flags.FakeFeatureFlags;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index e4e0dc9..3555a74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -29,7 +29,7 @@
import android.testing.TestableResources;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.DeviceConfigProxyFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index ae38958e..4b6c68a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -47,7 +47,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7f3d4b7..66c5aaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -132,12 +132,6 @@
}
@Test
- public void testAddNullCallback() {
- mController.addCallback(null);
- mController.fireConfigChanged(null);
- }
-
- @Test
public void testModeChange() {
List<Integer> states = List.of(
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index d212c02..5b9db4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -31,7 +31,7 @@
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
import com.android.systemui.InstanceIdSequenceFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 98bbb26..ae4dfbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 8c21fac..1e7e184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 7593e84..85359ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -62,7 +62,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.IntPair;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.PluginManager;
@@ -364,7 +364,7 @@
// THEN the view can have unlimited lines
assertThat(((TextView) mToastUI.mToast.getView()
- .findViewById(com.android.systemui.R.id.text))
+ .findViewById(com.android.systemui.res.R.id.text))
.getMaxLines()).isEqualTo(Integer.MAX_VALUE);
}
@@ -383,7 +383,7 @@
// THEN the view is limited to 2 lines
assertThat(((TextView) mToastUI.mToast.getView()
- .findViewById(com.android.systemui.R.id.text))
+ .findViewById(com.android.systemui.res.R.id.text))
.getMaxLines()).isEqualTo(2);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 5a54aff..bbc49c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -34,7 +34,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
-import com.android.systemui.R
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
@@ -120,7 +120,7 @@
whenever(manager.getUserIcon(anyInt())).thenReturn(ICON)
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
- overrideResource(R.drawable.ic_account_circle, GUEST_ICON)
+ overrideResource(com.android.settingslib.R.drawable.ic_account_circle, GUEST_ICON)
overrideResource(R.dimen.max_avatar_size, 10)
overrideResource(
com.android.internal.R.string.config_supervisedUserCreationPackage,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 3152fd1..8e57dc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,6 +41,7 @@
import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
+import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -56,7 +57,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
@@ -146,6 +147,10 @@
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+ // Ensure previous tests have not left messages on main looper
+ Handler localHandler = new Handler(mTestableLooper.getLooper());
+ localHandler.removeCallbacksAndMessages(null);
+
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 53e5e7d..8263174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -37,7 +37,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 692af6a..c1d11aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -459,7 +459,7 @@
WalletCard.CARD_TYPE_UNKNOWN),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_PAYMENT),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_NON_PAYMENT)
- );
+ );
GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
mController.onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
new file mode 100644
index 0000000..e46c1f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Test class for WalletCardUtils */
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class WalletCardUtilsTest : SysuiTestCase() {
+
+ private val paymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_PAYMENT)
+ private val nonPaymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_NON_PAYMENT)
+ private val unknownCard = createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+
+ @Test
+ fun paymentCards_cardTypesAllUnknown_getsAllCards() {
+ val walletCardList =
+ mutableListOf(
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+ )
+
+ assertThat(walletCardList).isEqualTo(getPaymentCards(walletCardList))
+ }
+
+ @Test
+ fun paymentCards_cardTypesDifferent_onlyGetsPayment() {
+ val walletCardList = mutableListOf(paymentCard, nonPaymentCard, unknownCard)
+
+ assertThat(getPaymentCards(walletCardList)).isEqualTo(mutableListOf(paymentCard))
+ }
+
+ private fun createWalletCardWithType(cardType: Int): WalletCard {
+ return WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ }
+
+ companion object {
+ private const val CARD_ID: String = "ID"
+ private const val CARD_DESCRIPTION: String = "Description"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 7595a54..d8511e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -87,7 +87,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.BubbleIconFactory;
-import com.android.systemui.R;
+import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -1273,9 +1273,9 @@
mBubbleController,
mBubbleController.getStackView(),
new BubbleIconFactory(mContext,
- mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size),
- mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
- mContext.getResources().getColor(R.color.important_conversation),
+ mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_size),
+ mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_badge_size),
+ mContext.getResources().getColor(com.android.launcher3.icons.R.color.important_conversation),
mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width)),
bubble,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ef0adbb..d2c8aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.sysui.ShellInterface;
@@ -79,6 +80,7 @@
@Mock ShellExecutor mSysUiMainExecutor;
@Mock NoteTaskInitializer mNoteTaskInitializer;
@Mock DesktopMode mDesktopMode;
+ @Mock RecentTasks mRecentTasks;
@Before
public void setUp() {
@@ -91,6 +93,7 @@
Optional.of(mSplitScreen),
Optional.of(mOneHanded),
Optional.of(mDesktopMode),
+ Optional.of(mRecentTasks),
mCommandQueue,
mConfigurationController,
mKeyguardStateController,
@@ -129,4 +132,10 @@
any(DesktopModeTaskRepository.VisibleTasksListener.class),
any(Executor.class));
}
+
+ @Test
+ public void initRecentTasks_registersListener() {
+ mWMShell.initRecentTasks(mRecentTasks);
+ verify(mRecentTasks).addAnimationStateListener(any(Executor.class), any());
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
index 5ff57aa..2d76027 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
@@ -28,6 +28,7 @@
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.res.R;
import java.util.Set;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index 60291ee..3fdeb30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -29,6 +29,8 @@
private val _currentRotation = MutableStateFlow<DisplayRotation>(DisplayRotation.ROTATION_0)
override val currentRotation: StateFlow<DisplayRotation> = _currentRotation.asStateFlow()
+ override val isReverseDefaultRotation = false
+
fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
_isInRearDisplayMode.value = isInRearDisplayMode
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt
deleted file mode 100644
index d9b926d..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.repository
-
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-class FakeBouncerMessageRepository : BouncerMessageRepository {
- private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val primaryAuthMessage: StateFlow<BouncerMessageModel?>
- get() = _primaryAuthMessage
-
- private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val faceAcquisitionMessage: StateFlow<BouncerMessageModel?>
- get() = _faceAcquisitionMessage
- private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val fingerprintAcquisitionMessage: StateFlow<BouncerMessageModel?>
- get() = _fingerprintAcquisitionMessage
- private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val customMessage: StateFlow<BouncerMessageModel?>
- get() = _customMessage
- private val _biometricAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val biometricAuthMessage: StateFlow<BouncerMessageModel?>
- get() = _biometricAuthMessage
- private val _authFlagsMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val authFlagsMessage: StateFlow<BouncerMessageModel?>
- get() = _authFlagsMessage
-
- private val _biometricLockedOutMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val biometricLockedOutMessage: Flow<BouncerMessageModel?>
- get() = _biometricLockedOutMessage
-
- override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
- _primaryAuthMessage.value = value
- }
-
- override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
- _faceAcquisitionMessage.value = value
- }
-
- override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
- _fingerprintAcquisitionMessage.value = value
- }
-
- override fun setCustomMessage(value: BouncerMessageModel?) {
- _customMessage.value = value
- }
-
- fun setBiometricAuthMessage(value: BouncerMessageModel?) {
- _biometricAuthMessage.value = value
- }
-
- fun setAuthFlagsMessage(value: BouncerMessageModel?) {
- _authFlagsMessage.value = value
- }
-
- fun setBiometricLockedOutMessage(value: BouncerMessageModel?) {
- _biometricLockedOutMessage.value = value
- }
-
- override fun clearMessage() {
- _primaryAuthMessage.value = null
- _faceAcquisitionMessage.value = null
- _fingerprintAcquisitionMessage.value = null
- _customMessage.value = null
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
new file mode 100644
index 0000000..e1c6dde
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.communal.data.repository
+
+/** Fake implementation of [CommunalRepository]. */
+class FakeCommunalRepository : CommunalRepository {
+ override var isCommunalEnabled = false
+
+ fun setIsCommunalEnabled(value: Boolean) {
+ isCommunalEnabled = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2b13dca..322fb28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -56,20 +56,7 @@
_isLockedOut.value = isLockedOut
}
- private val faceAuthPaused = MutableStateFlow(false)
- override fun pauseFaceAuth() {
- faceAuthPaused.value = true
- }
-
- override fun resumeFaceAuth() {
- faceAuthPaused.value = false
- }
-
- fun isFaceAuthPaused(): Boolean {
- return faceAuthPaused.value
- }
-
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
_runningAuthRequest.value = uiEvent to fallbackToDetection
_isAuthRunning.value = true
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 16442bb..dd513db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -38,7 +38,7 @@
}
override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
- return null
+ return if (info.animator == null) UUID.randomUUID() else null
}
override fun updateTransition(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
index 9d98f94..482126d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -25,6 +25,9 @@
import kotlinx.coroutines.flow.asStateFlow
class FakeTrustRepository : TrustRepository {
+ private val _isTrustUsuallyManaged = MutableStateFlow(false)
+ override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
+ get() = _isTrustUsuallyManaged
private val _isCurrentUserTrusted = MutableStateFlow(false)
override val isCurrentUserTrusted: Flow<Boolean>
get() = _isCurrentUserTrusted
@@ -55,4 +58,8 @@
fun setRequestDismissKeyguard(trustModel: TrustModel) {
_requestDismissKeyguard.value = trustModel
}
+
+ fun setTrustUsuallyManaged(value: Boolean) {
+ _isTrustUsuallyManaged.value = value
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 6dd41f4..ceab8e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -72,7 +72,6 @@
keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java),
featureFlags: FakeFeatureFlagsClassic =
FakeFeatureFlagsClassic().apply {
- set(Flags.DELAY_BOUNCER, true)
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, true)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
},
@@ -92,7 +91,6 @@
context,
keyguardUpdateMonitor,
trustRepository,
- featureFlags,
testScope.backgroundScope,
)
val alternateBouncerInteractor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
index 9383a0a..bf26e71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
@@ -16,21 +16,11 @@
package com.android.systemui.qs
-import android.content.Context
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
class FakeQSFactory(private val tileCreator: (String) -> QSTile?) : QSFactory {
override fun createTile(tileSpec: String): QSTile? {
return tileCreator(tileSpec)
}
-
- override fun createTileView(
- context: Context?,
- tile: QSTile?,
- collapsedView: Boolean
- ): QSTileView {
- throw NotImplementedError("Not implemented")
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
new file mode 100644
index 0000000..13437c9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import javax.annotation.CheckReturnValue
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+class FakeQSTileDataInteractor<T>(
+ private val dataFlow: MutableSharedFlow<FakeData<T>> =
+ MutableSharedFlow(replay = Int.MAX_VALUE),
+ private val availabilityFlow: MutableSharedFlow<Boolean> =
+ MutableSharedFlow(replay = Int.MAX_VALUE),
+) : QSTileDataInteractor<T> {
+
+ private val mutableDataRequests = mutableListOf<QSTileDataRequest>()
+ val dataRequests: List<QSTileDataRequest> = mutableDataRequests
+
+ private val mutableAvailabilityRequests = mutableListOf<Unit>()
+ val availabilityRequests: List<Unit> = mutableAvailabilityRequests
+
+ @CheckReturnValue
+ fun emitData(data: T): FilterEmit =
+ object : FilterEmit {
+ override fun forRequest(request: QSTileDataRequest): Boolean =
+ dataFlow.tryEmit(FakeData(data, DataFilter.ForRequest(request)))
+ override fun forAnyRequest(): Boolean = dataFlow.tryEmit(FakeData(data, DataFilter.Any))
+ }
+
+ fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
+ suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
+
+ override fun tileData(qsTileDataRequest: QSTileDataRequest): Flow<T> {
+ mutableDataRequests.add(qsTileDataRequest)
+ return dataFlow
+ .filter {
+ when (it.filter) {
+ is DataFilter.Any -> true
+ is DataFilter.ForRequest -> it.filter.request == qsTileDataRequest
+ }
+ }
+ .map { it.data }
+ }
+
+ override fun availability(): Flow<Boolean> {
+ mutableAvailabilityRequests.add(Unit)
+ return availabilityFlow
+ }
+
+ interface FilterEmit {
+ fun forRequest(request: QSTileDataRequest): Boolean
+ fun forAnyRequest(): Boolean
+ }
+
+ class FakeData<T>(
+ val data: T,
+ val filter: DataFilter,
+ )
+
+ sealed class DataFilter {
+ object Any : DataFilter()
+ class ForRequest(val request: QSTileDataRequest) : DataFilter()
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
new file mode 100644
index 0000000..4e0266e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+class FakeQSTileUserActionInteractor<T> : QSTileUserActionInteractor<T> {
+
+ private val mutex: Mutex = Mutex()
+ private val mutableInputs: MutableList<FakeInput<T>> = mutableListOf()
+
+ val inputs: List<FakeInput<T>> = mutableInputs
+
+ fun lastInput(): FakeInput<T>? = inputs.lastOrNull()
+
+ override suspend fun handleInput(userAction: QSTileUserAction, currentData: T) {
+ mutex.withLock { mutableInputs.add(FakeInput(userAction, currentData)) }
+ }
+
+ data class FakeInput<T>(val userAction: QSTileUserAction, val data: T)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 2d79e0f..1620dc27 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -31,6 +31,9 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -94,7 +97,10 @@
)
}
}
-
+ val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
+ private val communalWidgetRepository: FakeCommunalWidgetRepository by lazy {
+ FakeCommunalWidgetRepository()
+ }
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
private val context = test.context
@@ -171,6 +177,13 @@
)
}
+ fun communalInteractor(): CommunalInteractor {
+ return CommunalInteractor(
+ communalRepository = communalRepository,
+ widgetRepository = communalWidgetRepository,
+ )
+ }
+
fun bouncerInteractor(
authenticationInteractor: AuthenticationInteractor,
sceneInteractor: SceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 4242c16..f5f924d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -22,7 +22,6 @@
import android.os.UserHandle
import android.test.mock.MockContentResolver
import com.android.systemui.util.mockito.mock
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/** A fake [UserTracker] to be used in tests. */
@@ -73,8 +72,7 @@
fun onUserChanging(userId: Int = _userId) {
val copy = callbacks.toList()
- val latch = CountDownLatch(copy.size)
- copy.forEach { it.onUserChanging(userId, userContext, latch) }
+ copy.forEach { it.onUserChanging(userId, userContext) {} }
}
fun onUserChanged(userId: Int = _userId) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index e72544a..8b721b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -47,6 +47,17 @@
private val _legacyQsTracking = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyQsTracking = _legacyQsTracking
+ private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyExpandedOrAwaitingInputTransfer(
+ legacyExpandedOrAwaitingInputTransfer: Boolean
+ ) {
+ _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+ }
+
@Deprecated("Should only be called by NPVC and tests")
override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
_legacyQsTracking.value = legacyQsTracking
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 1f0181f..81fd8ce 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -23,6 +23,7 @@
android_library {
name: "SystemUIUnfoldLib",
+ use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
diff --git a/services/Android.bp b/services/Android.bp
index 9264172..f237095 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -34,17 +34,18 @@
},
}
-// Opt-in config for optimizing and shrinking the services target using R8.
-// Enabled via `export SYSTEM_OPTIMIZE_JAVA=true`, or explicitly in Make via the
+// Config to control optimizing and shrinking the services target using R8.
+// Set via `export SYSTEM_OPTIMIZE_JAVA=true|false`, or explicitly in Make via the
// `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA` variable.
-// TODO(b/196084106): Enable optimizations by default after stabilizing and
-// building out retrace infrastructure.
soong_config_module_type {
name: "system_optimized_java_defaults",
module_type: "java_defaults",
config_namespace: "ANDROID",
bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
- properties: ["optimize"],
+ properties: [
+ "optimize",
+ "dxflags",
+ ],
}
system_optimized_java_defaults {
@@ -75,6 +76,9 @@
// permission subpackage to prune unused jarjar'ed Kotlin dependencies.
proguard_flags_files: ["proguard_permission.flags"],
},
+ // Explicitly configure R8 to preserve debug info, as this path should
+ // really only allow stripping of permission-specific code and deps.
+ dxflags: ["--debug"],
},
},
},
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 3709f47..0480c22 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -19,4 +19,11 @@
namespace: "accessibility"
description: "Whether to enable joystick controls for magnification"
bug: "297211257"
+}
+
+flag {
+ name: "send_a11y_events_based_on_state"
+ namespace: "accessibility"
+ description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
+bug: "295575684"
}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 8060d5a..c418485 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -58,6 +58,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.BaseEventStreamTransformation;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
@@ -352,16 +353,34 @@
}
// The event for gesture end should be strictly after the
// last hover exit event.
- if (mSendTouchExplorationEndDelayed.isPending()) {
- mSendTouchExplorationEndDelayed.cancel();
- mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
- }
+ if (Flags.sendA11yEventsBasedOnState()) {
+ if (mSendTouchExplorationEndDelayed.isPending()) {
+ mSendTouchExplorationEndDelayed.cancel();
+ }
+ if (mState.isTouchExploring()) {
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ }
- // The event for touch interaction end should be strictly after the
- // last hover exit and the touch exploration gesture end events.
- if (mSendTouchInteractionEndDelayed.isPending()) {
- mSendTouchInteractionEndDelayed.cancel();
- mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ // The event for touch interaction end should be strictly after the
+ // last hover exit and the touch exploration gesture end events.
+ if (mSendTouchInteractionEndDelayed.isPending()) {
+ mSendTouchInteractionEndDelayed.cancel();
+ }
+ if (mState.isTouchInteracting()) {
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ }
+ } else {
+ if (mSendTouchExplorationEndDelayed.isPending()) {
+ mSendTouchExplorationEndDelayed.cancel();
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ }
+
+ // The event for touch interaction end should be strictly after the
+ // last hover exit and the touch exploration gesture end events.
+ if (mSendTouchInteractionEndDelayed.isPending()) {
+ mSendTouchInteractionEndDelayed.cancel();
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+ }
}
}
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 1e44de6..92e00ee 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -1 +1,8 @@
-package: "android.service.autofill"
\ No newline at end of file
+package: "android.service.autofill"
+
+flag {
+ name: "autofill_credman_integration"
+ namespace: "autofill"
+ description: "Guards Autofill Framework against Autofill-Credman integration"
+ bug: "296907283"
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
index ffb4632..db2e18a0 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
@@ -17,12 +17,19 @@
package com.android.server.autofill;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Optional;
/**
@@ -36,6 +43,29 @@
mEventInternal = Optional.empty();
}
+ public static final int STATUS_SUCCESS =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+ public static final int STATUS_UNKNOWN =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
+ public static final int STATUS_FAIL =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+ public static final int STATUS_CANCELLED =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+
+ /**
+ * Status of the FieldClassification IPC request. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillFieldClassificationEventReported.FieldClassificationRequestStatus}.
+ */
+ @IntDef(prefix = {"STATUS"}, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_FAIL,
+ STATUS_CANCELLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FieldClassificationStatus {
+ }
+
/**
* A factory constructor to create FieldClassificationEventLogger.
*/
@@ -56,7 +86,7 @@
}
/**
- * Set latency as long as mEventInternal presents.
+ * Set latency_millis as long as mEventInternal presents.
*/
public void maybeSetLatencyMillis(long timestamp) {
mEventInternal.ifPresent(event -> {
@@ -65,6 +95,69 @@
}
/**
+ * Set count_classifications as long as mEventInternal presents.
+ */
+ public void maybeSetCountClassifications(int countClassifications) {
+ mEventInternal.ifPresent(event -> {
+ event.mCountClassifications = countClassifications;
+ });
+ }
+
+ /**
+ * Set session_id as long as mEventInternal presents.
+ */
+ public void maybeSetSessionId(int sessionId) {
+ mEventInternal.ifPresent(event -> {
+ event.mSessionId = sessionId;
+ });
+ }
+
+ /**
+ * Set request_id as long as mEventInternal presents.
+ */
+ public void maybeSetRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mRequestId = requestId;
+ });
+ }
+
+ /**
+ * Set next_fill_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetNextFillRequestId(int nextFillRequestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mNextFillRequestId = nextFillRequestId;
+ });
+ }
+
+ /**
+ * Set app_package_uid as long as mEventInternal presents.
+ */
+ public void maybeSetAppPackageUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAppPackageUid = uid;
+ });
+ }
+
+ /**
+ * Set status as long as mEventInternal presents.
+ */
+ public void maybeSetRequestStatus(@FieldClassificationStatus int status) {
+ mEventInternal.ifPresent(event -> {
+ event.mStatus = status;
+ });
+ }
+
+ /**
+ * Set is_session_gc as long as mEventInternal presents.
+ */
+ public void maybeSetSessionGc(boolean isSessionGc) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsSessionGc = isSessionGc;
+ });
+ }
+
+ /**
* Log an AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED event.
*/
public void logAndEndEvent() {
@@ -77,16 +170,37 @@
if (sVerbose) {
Slog.v(TAG, "Log AutofillFieldClassificationEventReported:"
+ " mLatencyClassificationRequestMillis="
- + event.mLatencyClassificationRequestMillis);
+ + event.mLatencyClassificationRequestMillis
+ + " mCountClassifications=" + event.mCountClassifications
+ + " mSessionId=" + event.mSessionId
+ + " mRequestId=" + event.mRequestId
+ + " mNextFillRequestId=" + event.mNextFillRequestId
+ + " mAppPackageUid=" + event.mAppPackageUid
+ + " mStatus=" + event.mStatus
+ + " mIsSessionGc=" + event.mIsSessionGc);
}
FrameworkStatsLog.write(
AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED,
- event.mLatencyClassificationRequestMillis);
+ event.mLatencyClassificationRequestMillis,
+ event.mCountClassifications,
+ event.mSessionId,
+ event.mRequestId,
+ event.mNextFillRequestId,
+ event.mAppPackageUid,
+ event.mStatus,
+ event.mIsSessionGc);
mEventInternal = Optional.empty();
}
private static final class FieldClassificationEventInternal {
long mLatencyClassificationRequestMillis = -1;
+ int mCountClassifications = -1;
+ int mSessionId = -1;
+ int mRequestId = -1;
+ int mNextFillRequestId = -1;
+ int mAppPackageUid = -1;
+ int mStatus;
+ boolean mIsSessionGc;
FieldClassificationEventInternal() {
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 11b45db..6b0fdb5 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -211,15 +211,25 @@
public static final int DETECTION_PREFER_PCC =
AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
private final int mSessionId;
+
+ /**
+ * For app_package_uid.
+ */
+ private final int mCallingAppUid;
private Optional<PresentationStatsEventInternal> mEventInternal;
- private PresentationStatsEventLogger(int sessionId) {
+ private PresentationStatsEventLogger(int sessionId, int callingAppUid) {
mSessionId = sessionId;
+ mCallingAppUid = callingAppUid;
mEventInternal = Optional.empty();
}
- public static PresentationStatsEventLogger forSessionId(int sessionId) {
- return new PresentationStatsEventLogger(sessionId);
+ /**
+ * Create PresentationStatsEventLogger, populated with sessionId and the callingAppUid
+ */
+ public static PresentationStatsEventLogger createPresentationLog(
+ int sessionId, int callingAppUid) {
+ return new PresentationStatsEventLogger(sessionId, callingAppUid);
}
public void startNewEvent() {
@@ -508,6 +518,14 @@
return PICK_REASON_UNKNOWN;
}
+ /**
+ * Set field_classification_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetFieldClassificationRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mFieldClassificationRequestId = requestId;
+ });
+ }
public void logAndEndEvent() {
if (!mEventInternal.isPresent()) {
@@ -547,7 +565,9 @@
+ " mAvailablePccCount=" + event.mAvailablePccCount
+ " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+ " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
- + " mDetectionPreference=" + event.mDetectionPreference);
+ + " mDetectionPreference=" + event.mDetectionPreference
+ + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
+ + " mAppPackageUid=" + mCallingAppUid);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -584,7 +604,9 @@
event.mAvailablePccCount,
event.mAvailablePccOnlyCount,
event.mSelectedDatasetPickedReason,
- event.mDetectionPreference);
+ event.mDetectionPreference,
+ event.mFieldClassificationRequestId,
+ mCallingAppUid);
mEventInternal = Optional.empty();
}
@@ -617,6 +639,7 @@
int mAvailablePccOnlyCount = -1;
@DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
@DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
+ int mFieldClassificationRequestId = -1;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
index bcca006..50fabfd 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
@@ -69,6 +69,9 @@
void onClassificationRequestFailure(int requestId, @Nullable CharSequence message);
void onClassificationRequestTimeout(int requestId);
void onServiceDied(@NonNull RemoteFieldClassificationService service);
+ void logFieldClassificationEvent(
+ long startTime, @NonNull FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status);
}
RemoteFieldClassificationService(Context context, ComponentName serviceName,
@@ -149,15 +152,24 @@
new IFieldClassificationCallback.Stub() {
@Override
public void onCancellable(ICancellationSignal cancellation) {
- logLatency(startTime);
if (sDebug) {
Log.d(TAG, "onCancellable");
}
+ FieldClassificationServiceCallbacks
+ fieldClassificationServiceCallbacks =
+ Helper.weakDeref(
+ fieldClassificationServiceCallbacksWeakRef,
+ TAG, "onCancellable "
+ );
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_CANCELLED,
+ null);
}
@Override
public void onSuccess(FieldClassificationResponse response) {
- logLatency(startTime);
if (sDebug) {
if (Build.IS_DEBUGGABLE) {
Slog.d(TAG, "onSuccess Response: " + response);
@@ -179,6 +191,11 @@
fieldClassificationServiceCallbacksWeakRef,
TAG, "onSuccess "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_SUCCESS,
+ response);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -188,7 +205,6 @@
@Override
public void onFailure() {
- logLatency(startTime);
if (sDebug) {
Slog.d(TAG, "onFailure");
}
@@ -198,6 +214,11 @@
fieldClassificationServiceCallbacksWeakRef,
TAG, "onFailure "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_FAIL,
+ null);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -215,11 +236,24 @@
}));
}
- private void logLatency(long startTime) {
- final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
- logger.startNewLogForRequest();
- logger.maybeSetLatencyMillis(
- SystemClock.elapsedRealtime() - startTime);
- logger.logAndEndEvent();
+ private void logFieldClassificationEvent(
+ long startTime,
+ @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status,
+ FieldClassificationResponse response) {
+ if (fieldClassificationServiceCallbacks == null) {
+ final FieldClassificationEventLogger logger =
+ FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetSessionGc(true);
+ logger.maybeSetRequestStatus(status);
+ logger.logAndEndEvent();
+ } else {
+ fieldClassificationServiceCallbacks.logFieldClassificationEvent(
+ startTime, response, status);
+ }
+
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ae9125..4e5b058 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -229,6 +229,8 @@
private static final String PCC_HINTS_DELIMITER = ",";
public static final String EXTRA_KEY_DETECTIONS = "detections";
+ private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
+ private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
final Object mLock;
@@ -412,6 +414,20 @@
@GuardedBy("mLock")
private long mUiShownTime;
+ /**
+ * Tracks the value of the fill request id at the time of issuing request for field
+ * classification.
+ */
+ @GuardedBy("mLock")
+ private int mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+
+ /**
+ * Tracks the value of the field classification id at the time of issuing request for fill
+ * request.
+ */
+ @GuardedBy("mLock")
+ private int mFieldClassificationIdSnapshot = DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT;
+
@GuardedBy("mLock")
private final LocalLog mUiLatencyHistory;
@@ -673,6 +689,7 @@
if (mPendingFillRequest == null) {
return;
}
+ mFieldClassificationIdSnapshot = sIdCounterForPcc.get();
if (mWaitForInlineRequest) {
if (mPendingInlineSuggestionsRequest == null) {
@@ -1239,6 +1256,8 @@
+ ", flags=" + flags);
}
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mFillRequestEventLogger.maybeSetRequestId(requestId);
mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
if (mSessionFlags.mInlineSupportedByService) {
@@ -1327,6 +1346,7 @@
@GuardedBy("mLock")
private void requestAssistStructureForPccLocked(int flags) {
if (!mClassificationState.shouldTriggerRequest()) return;
+ mFillRequestIdSnapshot = sIdCounter.get();
mClassificationState.updatePendingRequest();
// Get request id
int requestId;
@@ -1411,7 +1431,8 @@
mStartTime = SystemClock.elapsedRealtime();
mLatencyBaseTime = mStartTime;
mRequestCount = 0;
- mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
+ mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog(
+ sessionId, uid);
mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
@@ -4301,6 +4322,8 @@
if (viewState.getResponse() != null) {
FillResponse response = viewState.getResponse();
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mPresentationStatsEventLogger.maybeSetAvailableCount(
response.getDatasets(), mCurrentViewId);
}
@@ -4478,6 +4501,8 @@
public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
@Nullable AutofillValue value, int flags) {
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
+ id + " destroyed");
@@ -5389,6 +5414,7 @@
List<Dataset> datasetList = newResponse.getDatasets();
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(sIdCounterForPcc.get());
mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
@@ -6451,7 +6477,7 @@
return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
}
- // FieldClassificationServiceCallbacks
+ // FieldClassificationServiceCallbacks start
public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) {
mClassificationState.updateResponseReceived(response);
}
@@ -6472,6 +6498,28 @@
// forceRemoveFromServiceLocked();
}
}
- // DetectionServiceCallbacks end
+
+ @Override
+ public void logFieldClassificationEvent(
+ long startTime, FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status) {
+ final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetAppPackageUid(uid);
+ logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1);
+ logger.maybeSetRequestId(sIdCounterForPcc.get());
+ logger.maybeSetSessionId(id);
+ int count = -1;
+ if (response != null) {
+ count = response.getClassifications().size();
+ }
+ logger.maybeSetRequestStatus(status);
+ logger.maybeSetCountClassifications(count);
+ logger.logAndEndEvent();
+ mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+ }
+ // FieldClassificationServiceCallbacks end
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java
new file mode 100644
index 0000000..06bbc2b
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+/**
+ {@link LinearLayout} that displays content of save dialog.
+ */
+public class BottomSheetLayout extends LinearLayout {
+
+ private static final String TAG = "BottomSheetLayout";
+
+ public BottomSheetLayout(Context context) {
+ super(context);
+ }
+
+ public BottomSheetLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BottomSheetLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void onMeasure(int widthSpec, int heightSpec) {
+ if (getContext() == null || getContext().getResources() == null) {
+ super.onMeasure(widthSpec, heightSpec);
+ Slog.w(TAG, "onMeasure failed due to missing context or missing resources.");
+ return;
+ }
+
+ if (getChildCount() == 0) {
+ // Should not happen
+ super.onMeasure(widthSpec, heightSpec);
+ Slog.wtf(TAG, "onMeasure failed due to missing children views.");
+ return;
+ }
+
+ DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+
+ final int pxOffset = getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_dialog_offset);
+ final int outerMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_save_outer_margin);
+
+ final boolean includeHorizontalSpace =
+ getContext().getResources().getBoolean(
+ R.bool.autofill_dialog_horizontal_space_included);
+
+
+ final int screenHeight = displayMetrics.heightPixels;
+ final int screenWidth = displayMetrics.widthPixels;
+
+ final int maxHeight = screenHeight - pxOffset - outerMargin;
+
+ int maxWidth = screenWidth;
+
+ if (includeHorizontalSpace) {
+ maxWidth -= 2 * pxOffset;
+ }
+
+ maxWidth =
+ Math.min(maxWidth, getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_dialog_max_width));
+
+ super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
+
+
+ if (sDebug) {
+ Slog.d(TAG, "onMeasure() values in dp:"
+ + " screenHeight: " + screenHeight / displayMetrics.density + ", screenWidth: "
+ + screenWidth / displayMetrics.density
+ + ", maxHeight: " + maxHeight / displayMetrics.density
+ + ", maxWidth: " + maxWidth / displayMetrics.density + ", getMeasuredWidth(): "
+ + getMeasuredWidth() / displayMetrics.density + ", getMeasuredHeight(): "
+ + getMeasuredHeight() / displayMetrics.density);
+ }
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
+ }
+
+
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 4488d86..5635dd5 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -48,7 +48,6 @@
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -363,13 +362,6 @@
window.setCloseOnTouchOutside(true);
final WindowManager.LayoutParams params = window.getAttributes();
- DisplayMetrics displayMetrics = new DisplayMetrics();
- window.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
- final int screenWidth = displayMetrics.widthPixels;
- final int maxWidth =
- context.getResources().getDimensionPixelSize(R.dimen.autofill_dialog_max_width);
- params.width = Math.min(screenWidth, maxWidth);
-
params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title);
params.windowAnimations = R.style.AutofillSaveAnimation;
params.setTrustedOverlay();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 3b02be5..976504a 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -487,6 +487,13 @@
File baseStateDir,
File dataDir,
TransportManager transportManager) {
+ // check if we are past the retention period for BMM Events,
+ // if so delete expired events and do not print them to dumpsys
+ BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
+ new BackupManagerMonitorDumpsysUtils();
+ if (backupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents() && DEBUG){
+ Slog.d(TAG, "BMM Events recorded for dumpsys have expired");
+ }
return new UserBackupManagerService(
userId,
context,
@@ -657,6 +664,13 @@
// the pending backup set
mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+ // check if we are past the retention period for BMM Events,
+ // if so delete expired events and do not print them to dumpsys
+ BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
+ new BackupManagerMonitorDumpsysUtils();
+ mBackupHandler.postDelayed(backupManagerMonitorDumpsysUtils::deleteExpiredBMMEvents,
+ INITIALIZATION_DELAY_MILLIS);
+
mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir);
// Power management
@@ -4181,7 +4195,19 @@
private void dumpBMMEvents(PrintWriter pw) {
BackupManagerMonitorDumpsysUtils bm =
new BackupManagerMonitorDumpsysUtils();
+ if (bm.deleteExpiredBMMEvents()) {
+ pw.println("BACKUP MANAGER MONITOR EVENTS HAVE EXPIRED");
+ return;
+ }
File events = bm.getBMMEventsFile();
+ if (events.length() == 0){
+ // We have not recorded BMMEvents yet.
+ pw.println("NO BACKUP MANAGER MONITOR EVENTS");
+ return;
+ } else if (bm.isFileLargerThanSizeLimit(events)){
+ pw.println("BACKUP MANAGER MONITOR EVENTS FILE OVER SIZE LIMIT - "
+ + "future events will not be recorded");
+ }
pw.println("START OF BACKUP MANAGER MONITOR EVENTS");
try (BufferedReader reader = new BufferedReader(new FileReader(events))) {
String line;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e04bf11..bbec79d 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -394,6 +394,20 @@
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
AppWidgetBackupBridge.systemRestoreStarting(mUserId);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+ } else {
+ //We are either performing RestoreAtInstall or Bmgr.
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
}
try {
@@ -421,6 +435,12 @@
mStatus = transport.startRestore(mToken, packages);
if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(UnifiedRestoreState.FINAL);
return;
@@ -528,6 +548,12 @@
final String pkgName = (mRestoreDescription != null)
? mRestoreDescription.getPackageName() : null;
if (pkgName == null) {
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
Slog.e(TAG, "Failure getting next package name");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
nextState = UnifiedRestoreState.FINAL;
@@ -550,6 +576,14 @@
Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
if (metaInfo == null) {
+ PackageInfo pkgInfo = new PackageInfo();
+ pkgInfo.packageName = pkgName;
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA,
+ pkgInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
Slog.e(TAG, "No metadata for " + pkgName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
"Package metadata missing");
@@ -560,6 +594,13 @@
try {
mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser(
pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+
} catch (NameNotFoundException e) {
// Whoops, we thought we could restore this package but it
// turns out not to be present. Skip it.
@@ -641,12 +682,24 @@
} else {
// Unknown restore type; ignore this package and move on
Slog.e(TAG, "Unrecognized restore type " + type);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
nextState = UnifiedRestoreState.RUNNING_QUEUE;
return;
}
} catch (Exception e) {
Slog.e(TAG, "Can't get next restore target from transport; halting: "
+ e.getMessage());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
nextState = UnifiedRestoreState.FINAL;
return;
@@ -663,6 +716,10 @@
final String packageName = mCurrentPackage.packageName;
// Validate some semantic requirements that apply in this way
// only to the key/value restore API flow
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null));
if (mCurrentPackage.applicationInfo.backupAgentName == null
|| "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
if (MORE_DEBUG) {
@@ -721,6 +778,11 @@
++mCount;
} catch (Exception e) {
Slog.e(TAG, "Error when attempting restore: " + e.toString());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
keyValueAgentErrorCleanup(false);
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
}
@@ -759,6 +821,12 @@
// Transport-level failure. This failure could be specific to package currently in
// restore.
Slog.e(TAG, "Error getting restore data for " + packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
stage.close();
downloadFile.delete();
@@ -815,6 +883,11 @@
new ArrayList<>(getExcludedKeysForPackage(packageName)));
} catch (Exception e) {
Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
packageName, e.toString());
// Clears any pending timeout messages as well.
@@ -888,6 +961,10 @@
//
// When finished, StreamFeederThread executes next state as appropriate on the
// backup looper, and the overall unified restore task resumes
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null));
try {
StreamFeederThread feeder = new StreamFeederThread();
if (MORE_DEBUG) {
@@ -903,6 +980,11 @@
// current target. We haven't asked the transport for data yet, though,
// so we can do that simply by going back to running the restore queue.
Slog.e(TAG, "Unable to construct pipes for stream restore!");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
}
}
@@ -927,6 +1009,10 @@
} catch (Exception e) {
final String packageName = mCurrentPackage.packageName;
Slog.e(TAG, "Unable to finalize restore of " + packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
packageName, e.toString());
keyValueAgentErrorCleanup(true);
@@ -1020,6 +1106,12 @@
// handling will deal properly with that.
Slog.e(TAG, "Error " + result + " streaming restore for "
+ mCurrentPackage.packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
status = result;
}
@@ -1032,6 +1124,12 @@
// but potentially recoverable; abandon this package's restore but
// carry on with the next restore target.
Slog.e(TAG, "Unable to route data for restore");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
mCurrentPackage.packageName, "I/O error on pipes");
status = BackupTransport.AGENT_ERROR;
@@ -1040,6 +1138,12 @@
// the sockets will wake up the engine and it will then tidy up the
// remote end.
Slog.e(TAG, "Transport failed during restore: " + e.getMessage());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
status = BackupTransport.TRANSPORT_ERROR;
} finally {
@@ -1213,6 +1317,13 @@
}
Slog.i(TAG, "Restore complete.");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+
mListener.onFinished(callerLogString);
}
@@ -1313,6 +1424,7 @@
@Override
public void operationComplete(long unusedResult) {
mOperationStorage.removeOperation(mEphemeralOpToken);
+
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -1341,6 +1453,11 @@
// Okay, we're done with this package. Tidy up and go on to the next
// app in the queue.
int size = (int) mBackupDataName.length();
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
mCurrentPackage.packageName, size);
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
index 0b55ca2..797aed9 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
@@ -23,15 +23,22 @@
import android.os.Environment;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/*
@@ -46,12 +53,27 @@
// Name of the subdirectory where the text file containing the BMM events will be stored.
// Same as {@link UserBackupManagerFiles}
private static final String BACKUP_PERSISTENT_DIR = "backup";
+ private static final String INITIAL_SETUP_TIMESTAMP_KEY = "initialSetupTimestamp";
+ // Retention period of 60 days (in millisec) for the BMM Events.
+ // After tha time has passed the text file containing the BMM events will be emptied
+ private static final long LOGS_RETENTION_PERIOD_MILLISEC = 60 * TimeUnit.DAYS.toMillis(1);
+ // Size limit for the text file containing the BMM events
+ private static final long BMM_FILE_SIZE_LIMIT_BYTES = 25 * 1024 * 1000; // 2.5 MB
+
+ // We cache the value of IsAfterRetentionPeriod() to avoid unnecessary disk I/O
+ // mIsAfterRetentionPeriodCached tracks if we have cached the value of IsAfterRetentionPeriod()
+ private boolean mIsAfterRetentionPeriodCached = false;
+ // The cached value of IsAfterRetentionPeriod()
+ private boolean mIsAfterRetentionPeriod;
+ // If isFileLargerThanSizeLimit(bmmEvents) returns true we cache the value to avoid
+ // unnecessary disk I/O
+ private boolean mIsFileLargerThanSizeLimit = false;
/**
* Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that
* will be persisted in a text file and printed in the dumpsys.
*
- * If the evenntBundle passed is not a RESTORE event, return early
+ * If the eventBundle passed is not a RESTORE event, return early
*
* Key information related to the event:
* - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT)
@@ -62,17 +84,22 @@
* - Agent logs (if available)
*
* Example of formatting:
- * RESTORE Event: [2023-08-18 17:16:00.735] Agent - Agent logging results
- * Package name: com.android.wallpaperbackup
- * Agent Logs:
- * Data Type: wlp_img_system
- * Item restored: 0/1
- * Agent Error - Category: no_wallpaper, Count: 1
- * Data Type: wlp_img_lock
- * Item restored: 0/1
- * Agent Error - Category: no_wallpaper, Count: 1
+ * [2023-09-21 14:43:33.824] - Agent logging results
+ * Package: com.android.wallpaperbackup
+ * Agent Logs:
+ * Data Type: wlp_img_system
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
+ * Data Type: wlp_img_lock
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
*/
public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) {
+ if (isAfterRetentionPeriod()) {
+ // We only log data for the first 60 days since setup
+ return;
+ }
+
if (eventBundle == null) {
return;
}
@@ -89,8 +116,19 @@
}
File bmmEvents = getBMMEventsFile();
+ if (bmmEvents.length() == 0) {
+ // We are parsing the first restore event.
+ // Time to also record the setup timestamp of the device
+ recordSetUpTimestamp();
+ }
+
+ if(isFileLargerThanSizeLimit(bmmEvents)){
+ // Do not write more events if the file is over size limit
+ return;
+ }
+
try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true);
- PrintWriter pw = new FastPrintWriter(out);) {
+ PrintWriter pw = new FastPrintWriter(out);) {
int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
@@ -101,17 +139,16 @@
return;
}
- pw.println("RESTORE Event: [" + timestamp() + "] " +
- getCategory(eventCategory) + " - " +
- getId(eventId));
+ pw.println("[" + timestamp() + "] - " + getId(eventId));
if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) {
- pw.println("\tPackage name: "
+ pw.println("\tPackage: "
+ eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
}
// TODO(b/296818666): add extras to the events
addAgentLogsIfAvailable(eventBundle, pw);
+ addExtrasIfAvailable(eventBundle, pw);
} catch (java.io.IOException e) {
Slog.e(TAG, "IO Exception when writing BMM events to file: " + e);
}
@@ -156,6 +193,37 @@
}
}
+ /**
+ * Extracts some extras (defined in BackupManagerMonitor as EXTRA_LOG_<description>)
+ * from the BackupManagerMonitor event. Not all extras have the same importance. For now only
+ * focus on extras relating to version mismatches between packages on the source and target.
+ *
+ * When an event with ID LOG_EVENT_ID_RESTORE_VERSION_HIGHER (trying to restore from higher to
+ * lower version of a package) parse:
+ * EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source
+ * EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version
+ * EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target
+ */
+ private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) {
+ if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) ==
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER) {
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)) {
+ pw.println("\t\tPackage supports RestoreAnyVersion: "
+ + eventBundle.getBoolean(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY));
+ }
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)) {
+ pw.println("\t\tPackage version on source: "
+ + eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION));
+ }
+ if (eventBundle.containsKey(
+ BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
+ pw.println("\t\tPackage version on target: "
+ + eventBundle.getLong(
+ BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION));
+ }
+ }
+ }
+
/*
* Get the path of the text files which stores the BMM events
*/
@@ -165,6 +233,13 @@
return fname;
}
+ public boolean isFileLargerThanSizeLimit(File events){
+ if (!mIsFileLargerThanSizeLimit) {
+ mIsFileLargerThanSizeLimit = events.length() > getBMMEventsFileSizeLimit();
+ }
+ return mIsFileLargerThanSizeLimit;
+ }
+
private String timestamp() {
long currentTime = System.currentTimeMillis();
Date date = new Date(currentTime);
@@ -245,6 +320,32 @@
case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED ->
"Transport non-incremental backup required";
case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE -> "Start system restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL ->
+ "Start restore at install";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE ->
+ "Transport error during start restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME ->
+ "Cannot get next package name";
+ case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE -> "Unknown restore type";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE -> "KV restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE -> "Full restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET ->
+ "No next restore target";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR -> "KV agent error";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED ->
+ "Package restore finished";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE ->
+ "Transport error KV restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD -> "No feeder thread";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR -> "Full agent error";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE ->
+ "Transport error full restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE ->
+ "Start package restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE ->
+ "Agent failure";
default -> "Unknown log event ID: " + code;
};
return id;
@@ -257,4 +358,128 @@
default -> false;
};
}
+
+ /**
+ * Store the timestamp when the device was set up (date when the first BMM event is parsed)
+ * in a text file.
+ */
+ @VisibleForTesting
+ void recordSetUpTimestamp() {
+ File setupDateFile = getSetUpDateFile();
+ // record setup timestamp only once
+ if (setupDateFile.length() == 0) {
+ try (FileOutputStream out = new FileOutputStream(setupDateFile, /*append*/ true);
+ PrintWriter pw = new FastPrintWriter(out);) {
+ long currentDate = System.currentTimeMillis();
+ pw.println(currentDate);
+ } catch (IOException e) {
+ Slog.w(TAG, "An error occurred while recording the setup date: "
+ + e.getMessage());
+ }
+ }
+
+ }
+
+ @VisibleForTesting
+ String getSetUpDate() {
+ File fname = getSetUpDateFile();
+ try (FileInputStream inputStream = new FileInputStream(fname);
+ InputStreamReader reader = new InputStreamReader(inputStream);
+ BufferedReader bufferedReader = new BufferedReader(reader);) {
+ return bufferedReader.readLine();
+ } catch (Exception e) {
+ Slog.w(TAG, "An error occurred while reading the date: " + e.getMessage());
+ return "Could not retrieve setup date";
+ }
+ }
+
+ @VisibleForTesting
+ static boolean isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec) {
+ if (startTimeStamp > endTimeStamp) {
+ // Something has gone wrong, timeStamp1 should always precede timeStamp2.
+ // Out of caution return true: we would delete the logs rather than
+ // risking them being kept for longer than the retention period
+ return true;
+ }
+ long timeDifferenceMillis = endTimeStamp - startTimeStamp;
+ return (timeDifferenceMillis >= millisec);
+ }
+
+ /**
+ * Check if current date is after retention period
+ */
+ @VisibleForTesting
+ boolean isAfterRetentionPeriod() {
+ if (mIsAfterRetentionPeriodCached) {
+ return mIsAfterRetentionPeriod;
+ } else {
+ File setUpDateFile = getSetUpDateFile();
+ if (setUpDateFile.length() == 0) {
+ // We are yet to record a setup date. This means we haven't parsed the first event.
+ mIsAfterRetentionPeriod = false;
+ mIsAfterRetentionPeriodCached = true;
+ return false;
+ }
+ try {
+ long setupTimestamp = Long.parseLong(getSetUpDate());
+ long currentTimestamp = System.currentTimeMillis();
+ mIsAfterRetentionPeriod = isDateAfterNMillisec(setupTimestamp, currentTimestamp,
+ getRetentionPeriodInMillisec());
+ mIsAfterRetentionPeriodCached = true;
+ return mIsAfterRetentionPeriod;
+ } catch (NumberFormatException e) {
+ // An error occurred when parsing the setup timestamp.
+ // Out of caution return true: we would delete the logs rather than
+ // risking them being kept for longer than the retention period
+ mIsAfterRetentionPeriod = true;
+ mIsAfterRetentionPeriodCached = true;
+ return true;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ File getSetUpDateFile() {
+ File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+ File setupDateFile = new File(dataDir, INITIAL_SETUP_TIMESTAMP_KEY + ".txt");
+ return setupDateFile;
+ }
+
+ @VisibleForTesting
+ long getRetentionPeriodInMillisec() {
+ return LOGS_RETENTION_PERIOD_MILLISEC;
+ }
+
+ @VisibleForTesting
+ long getBMMEventsFileSizeLimit(){
+ return BMM_FILE_SIZE_LIMIT_BYTES;
+ }
+
+ /**
+ * Delete the BMM Events file after the retention period has passed.
+ *
+ * @return true if the retention period has passed false otherwise.
+ * we want to return true even if we were unable to delete the file, as this will prevent
+ * expired BMM events from being printed to the dumpsys
+ */
+ public boolean deleteExpiredBMMEvents() {
+ try {
+ if (isAfterRetentionPeriod()) {
+ File bmmEvents = getBMMEventsFile();
+ if (bmmEvents.exists()) {
+ if (bmmEvents.delete()) {
+ Slog.i(TAG, "Deleted expired BMM Events");
+ } else {
+ Slog.e(TAG, "Unable to delete expired BMM Events");
+ }
+ }
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ // Handle any unexpected exceptions
+ // To be safe we return true as we want to avoid exposing expired BMMEvents
+ return true;
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
index 92e3107..549d08c0 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
@@ -222,4 +222,21 @@
extras.putBoolean(key, value);
return extras;
}
+
+ /**
+ * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
+ * be created.
+ *
+ * @param extras - bundle where to add key-value to, if null a new bundle will be created.
+ * @param key - key.
+ * @param value - value.
+ * @return extras if it was not null and new bundle otherwise.
+ */
+ public static Bundle putMonitoringExtra(Bundle extras, String key, int value) {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(key, value);
+ return extras;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 102c262..a3ccb16 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -133,6 +133,7 @@
@GuardedBy("mGenericWindowPolicyControllerLock")
private boolean mShowTasksInHostDeviceRecents;
+ @Nullable private final ComponentName mCustomHomeComponent;
/**
* Creates a window policy controller that is generic to the different use cases of virtual
@@ -157,6 +158,10 @@
* @param intentListenerCallback Callback that is called to intercept intents when matching
* passed in filters.
* @param showTasksInHostDeviceRecents whether to show activities in recents on the host device.
+ * @param customHomeComponent The component acting as a home activity on the virtual display. If
+ * {@code null}, then the system-default secondary home activity will be used. This is only
+ * applicable to displays that support home activities, i.e. they're created with the relevant
+ * virtual display flag.
*/
public GenericWindowPolicyController(
int windowFlags,
@@ -172,7 +177,8 @@
@Nullable SecureWindowCallback secureWindowCallback,
@Nullable IntentListenerCallback intentListenerCallback,
@NonNull Set<String> displayCategories,
- boolean showTasksInHostDeviceRecents) {
+ boolean showTasksInHostDeviceRecents,
+ @Nullable ComponentName customHomeComponent) {
super();
mAllowedUsers = allowedUsers;
mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
@@ -187,6 +193,7 @@
mIntentListenerCallback = intentListenerCallback;
mDisplayCategories = displayCategories;
mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents;
+ mCustomHomeComponent = customHomeComponent;
}
/**
@@ -384,6 +391,11 @@
return false;
}
+ @Override
+ public @Nullable ComponentName getCustomHomeComponent() {
+ return mCustomHomeComponent;
+ }
+
/**
* Returns true if an app with the given UID has an activity running on the virtual display for
* this controller.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 3b13410..203a152 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -37,6 +37,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -51,6 +52,8 @@
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -75,6 +78,7 @@
import android.hardware.input.VirtualTouchEvent;
import android.hardware.input.VirtualTouchscreenConfig;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -115,12 +119,33 @@
private static final String TAG = "VirtualDeviceImpl";
+ /**
+ * Virtual displays created by a {@link VirtualDeviceManager.VirtualDevice} are more consistent
+ * with virtual displays created via {@link DisplayManager} and allow for the creation of
+ * private, auto-mirror, and fixed orientation displays since
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}.
+ *
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER =
+ 294837146L;
+
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
| DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
| DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
+ private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC =
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+
private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:";
/**
@@ -130,6 +155,8 @@
private final Object mVirtualDeviceLock = new Object();
+ private final int mBaseVirtualDisplayFlags;
+
private final Context mContext;
private final AssociationInfo mAssociationInfo;
private final VirtualDeviceManagerService mService;
@@ -311,6 +338,16 @@
? mParams.getBlockedActivities()
: mParams.getAllowedActivities();
}
+
+ int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
+ if (!CompatChanges.isChangeEnabled(
+ MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER, mOwnerUid)) {
+ flags |= DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC;
+ }
+ if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+ }
+ mBaseVirtualDisplayFlags = flags;
}
@VisibleForTesting
@@ -323,11 +360,7 @@
* device.
*/
int getBaseVirtualDisplayFlags() {
- int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
- if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
- flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
- }
- return flags;
+ return mBaseVirtualDisplayFlags;
}
/** Returns the camera access controller of this device. */
@@ -905,6 +938,8 @@
mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED;
final boolean showTasksInHostDeviceRecents =
getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT;
+ final ComponentName homeComponent =
+ Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
FLAG_SECURE,
@@ -922,7 +957,8 @@
this::onSecureWindowShown,
this::shouldInterceptIntent,
displayCategories,
- showTasksInHostDeviceRecents);
+ showTasksInHostDeviceRecents,
+ homeComponent);
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
return gwpc;
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 838aae8..cd87908 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -112,7 +112,14 @@
*/
public static final int INTEGRITY_VERIFICATION_REJECT = 0;
- /** Observer called whenever the list of packages changes */
+ /**
+ * Observer called whenever the list of packages changes.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
+ */
+ @Deprecated
public interface PackageListObserver {
/** A package was added to the system. */
default void onPackageAdded(@NonNull String packageName, int uid) {}
@@ -723,7 +730,12 @@
* notified if a package is updated.
* <p>The package list will not be updated automatically as packages are
* installed / uninstalled. Any changes must be handled within the observer.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
*/
+ @Deprecated
public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
/**
@@ -733,7 +745,12 @@
* <p>Does nothing if the observer isn't currently registered.
* <p>Observers are notified asynchronously and it's possible for an observer to be
* invoked after its been removed.
+ *
+ * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+ * PackageMonitor covers more installation and uninstallation corner cases than
+ * PackageListObserver.
*/
+ @Deprecated
public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
/**
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d94f4f2..556eba6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -167,6 +167,8 @@
private int mBatteryNearlyFullLevel;
private int mShutdownBatteryTemperature;
+ private static String sSystemUiPackage;
+
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -228,6 +230,8 @@
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
+ sSystemUiPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUi);
mBatteryLevelsEventQueue = new ArrayDeque<>();
mMetricsLogger = new MetricsLogger();
@@ -750,8 +754,21 @@
+ ", info:" + mHealthInfo.toString());
}
- mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
- mBatteryChangedOptions, UserHandle.USER_ALL));
+ mHandler.post(() -> broadcastBatteryChangedIntent(intent, mBatteryChangedOptions));
+ }
+
+ private static void broadcastBatteryChangedIntent(Intent intent, Bundle options) {
+ // TODO (293959093): It is important that SystemUI receives this broadcast as soon as
+ // possible. Ideally, it should be using binder callbacks but until then, dispatch this
+ // as a foreground broadcast to SystemUI.
+ final Intent fgIntent = new Intent(intent);
+ fgIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ fgIntent.setPackage(sSystemUiPackage);
+ ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE,
+ options, UserHandle.USER_ALL);
+
+ ActivityManager.broadcastStickyIntent(intent, new String[] {sSystemUiPackage},
+ AppOpsManager.OP_NONE, options, UserHandle.USER_ALL);
}
private void sendBatteryLevelChangedIntentLocked() {
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
index 8996926..44f1e76 100644
--- a/services/core/java/com/android/server/SmartStorageMaintIdler.java
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -25,6 +25,7 @@
import android.util.Slog;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
public class SmartStorageMaintIdler extends JobService {
private static final String TAG = "SmartStorageMaintIdler";
@@ -34,15 +35,15 @@
private static final int SMART_MAINT_JOB_ID = 2808;
- private boolean mStarted;
+ private final AtomicBoolean mStarted = new AtomicBoolean(false);
private JobParameters mJobParams;
private final Runnable mFinishCallback = new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Got smart storage maintenance service completion callback");
- if (mStarted) {
+ if (mStarted.get()) {
jobFinished(mJobParams, false);
- mStarted = false;
+ mStarted.set(false);
}
// ... and try again in a next period
scheduleSmartIdlePass(SmartStorageMaintIdler.this,
@@ -52,18 +53,26 @@
@Override
public boolean onStartJob(JobParameters params) {
- mJobParams = params;
- StorageManagerService ms = StorageManagerService.sSelf;
- if (ms != null) {
- mStarted = true;
- ms.runSmartIdleMaint(mFinishCallback);
+ final StorageManagerService ms = StorageManagerService.sSelf;
+ if (mStarted.compareAndSet(false, true)) {
+ new Thread() {
+ public void run() {
+ mJobParams = params;
+ if (ms != null) {
+ ms.runSmartIdleMaint(mFinishCallback);
+ } else {
+ mStarted.set(false);
+ }
+ }
+ }.start();
+ return ms != null;
}
- return ms != null;
+ return false;
}
@Override
public boolean onStopJob(JobParameters params) {
- mStarted = false;
+ mStarted.set(false);
return false;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a787644..25ca509c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2842,7 +2842,7 @@
return true;
}
- void runSmartIdleMaint(Runnable callback) {
+ synchronized void runSmartIdleMaint(Runnable callback) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
try {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 93fe0c9..553b085 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -97,6 +97,11 @@
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_BG_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER;
import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT;
@@ -122,6 +127,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.Manifest;
+import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -901,7 +907,10 @@
showFgsBgRestrictedNotificationLocked(r);
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
}
@@ -2066,6 +2075,7 @@
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
+ final boolean origFgRequired = r.fgRequired;
if (r.fgRequired) {
if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service called startForeground() as required: " + r);
@@ -2117,6 +2127,9 @@
// Whether to extend the SHORT_SERVICE time out.
boolean extendShortServiceTimeout = false;
+ // Whether setFgsRestrictionLocked() is called in here. Only used for logging.
+ boolean fgsRestrictionRecalculated = false;
+
int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN;
if (!ignoreForeground) {
if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
@@ -2182,6 +2195,7 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
if (!r.isFgsAllowedStart()) {
Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+ " BFSL DENIED.");
@@ -2246,6 +2260,7 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
r.mInfoAllowStartForeground += "; " + temp;
@@ -2266,6 +2281,25 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ fgsRestrictionRecalculated = true;
+ }
+
+ // When startForeground() is called on a bound service, without having
+ // it started (i.e. no Context.startService() or startForegroundService() was
+ // called.)
+ // called on it, then we probably didn't call setFgsRestrictionLocked()
+ // in startService(). If fgsRestrictionRecalculated is false, then we
+ // didn't call setFgsRestrictionLocked() here either.
+ //
+ // In this situation, we call setFgsRestrictionLocked() with
+ // forBoundFgs = false, so we'd set the FGS allowed reason to the
+ // by-bindings fields, so we can put it in the log, without affecting the
+ // logic.
+ if (!fgsRestrictionRecalculated && !r.startRequested) {
+ setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+ BackgroundStartPrivileges.NONE,
+ false /* isBindService */, true /* forBoundFgs */);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -2291,7 +2325,10 @@
ignoreForeground = true;
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
r.appInfo.uid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
@@ -2331,7 +2368,10 @@
if (fgsTypeResult.second != null) {
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first);
+ 0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
throw fgsTypeResult.second;
}
}
@@ -2403,9 +2443,24 @@
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+
+ int fgsStartApi = FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+ if (r.startRequested) {
+ if (origFgRequired) {
+ fgsStartApi =
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+ } else {
+ fgsStartApi =
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
+ }
+ }
+
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode);
+ 0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode,
+ fgsStartApi,
+ fgsRestrictionRecalculated
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStart(r.appInfo.uid, 0, r);
}
@@ -2499,7 +2554,10 @@
r.mFgsExitTime > r.mFgsEnterTime
? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
FGS_STOP_REASON_STOP_FOREGROUND,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
@@ -3338,7 +3396,10 @@
FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0,
FGS_STOP_REASON_UNKNOWN,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
try {
sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
} catch (RemoteException e) {
@@ -5705,7 +5766,10 @@
r.mFgsExitTime > r.mFgsEnterTime
? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
FGS_STOP_REASON_STOP_SERVICE,
- FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
synchronized (mFGSLogger) {
mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
}
@@ -7452,6 +7516,13 @@
}
}
+ private void setFgsRestrictionLocked(String callingPackage,
+ int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
+ backgroundStartPrivileges, isBindService, /*forBoundFgs*/ false);
+ }
+
/**
* There are two FGS restrictions:
* In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
@@ -7463,11 +7534,14 @@
* @param intent intent to start/bind service.
* @param r the service to start.
* @param isBindService True if it's called from bindService().
+ * @param forBoundFgs set to true if it's called from Service.startForeground() for a
+ * service that's not started but bound.
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ boolean forBoundFgs) {
@ReasonCode int allowWIU;
@ReasonCode int allowStart;
@@ -7511,9 +7585,19 @@
r.mAllowWIUInBindService = allowWIU;
r.mAllowStartInBindService = allowStart;
} else {
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
- r.mAllowStartForegroundNoBinding = allowStart;
-
+ if (!forBoundFgs) {
+ // This is for "normal" situation.
+ r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
+ r.mAllowStartForegroundNoBinding = allowStart;
+ } else {
+ // This logic is only for logging, so we only update the "by-binding" fields.
+ if (r.mAllowWIUByBindings == REASON_DENIED) {
+ r.mAllowWIUByBindings = allowWIU;
+ }
+ if (r.mAllowStartByBindings == REASON_DENIED) {
+ r.mAllowStartByBindings = allowStart;
+ }
+ }
// Also do a binding client check, unless called from bindService().
if (r.mAllowWIUByBindings == REASON_DENIED) {
r.mAllowWIUByBindings =
@@ -8137,7 +8221,10 @@
*/
private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs,
@FgsStopReason int fgsStopReason,
- @ForegroundServicePolicyCheckCode int fgsTypeCheckCode) {
+ @ForegroundServicePolicyCheckCode int fgsTypeCheckCode,
+ int fgsStartApi, // from ForegroundServiceStateChanged.FgsStartApi
+ boolean fgsRestrictionRecalculated
+ ) {
if (!ActivityManagerUtils.shouldSamplePackageForAtom(
r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
return;
@@ -8194,7 +8281,9 @@
r.mAllowWIUByBindings,
r.mAllowStartForegroundNoBinding,
r.mAllowStartInBindService,
- r.mAllowStartByBindings);
+ r.mAllowStartByBindings,
+ fgsStartApi,
+ fgsRestrictionRecalculated);
int event = 0;
if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
@@ -8373,7 +8462,10 @@
}
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+ 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE,
+ false /* fgsRestrictionRecalculated */
+ );
// Notify the caller.
if (connection != null) {
mAm.mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b5911f6..d3ce47c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4070,21 +4070,6 @@
profile.addPss(mi.getTotalPss(),
mi.getTotalUss(), mi.getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- final ProcessState state = holder.state;
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- state != null ? state.getName() : proc.processName,
- state != null ? state.getPackage() : proc.info.packageName,
- mi.getTotalPss(),
- mi.getTotalUss(),
- mi.getTotalRss(),
- ProcessStats.ADD_PSS_EXTERNAL_SLOW,
- duration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
}
}
}
@@ -4131,20 +4116,6 @@
// Record this for posterity if the process has been stable.
profile.addPss(pi, tmpUss[0], tmpUss[2], false,
ProcessStats.ADD_PSS_EXTERNAL, duration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- pi,
- tmpUss[0],
- tmpUss[2],
- ProcessStats.ADD_PSS_EXTERNAL,
- duration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
}
}
}
@@ -9851,6 +9822,10 @@
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
+ private static final String TICK =
+ "---------------------------------------"
+ + "----------------------------------------";
+
private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
boolean dumpAll, String dumpPackage, int displayIdFilter, boolean dumpClient,
boolean dumpNormalPriority, int dumpAppId, boolean dumpProxies) {
@@ -9906,6 +9881,11 @@
sdumper.dumpLocked();
}
}
+
+ // No need to hold the lock.
+ pw.println(TICK);
+ AnrTimer.dump(pw, false);
+
// We drop the lock here because we can't call dumpWithClient() with the lock held;
// if the caller wants a consistent state for the !dumpClient case, it can call this
// method with the lock held.
@@ -10351,6 +10331,8 @@
mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
mOomAdjuster.dumpCacheOomRankerSettings(pw);
}
+ } else if ("timers".equals(cmd)) {
+ AnrTimer.dump(pw, true);
} else if ("services".equals(cmd) || "s".equals(cmd)) {
if (dumpClient) {
ActiveServices.ServiceDumper dumper;
@@ -12077,17 +12059,6 @@
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
- r.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- r.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- myTotalPss, myTotalUss, myTotalRss, reportType,
- endTime-startTime,
- holder.appVersion,
- r.mProfile.getCurrentHostingComponentTypes(),
- r.mProfile.getHistoricalHostingComponentTypes());
- });
}
}
@@ -12723,16 +12694,6 @@
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
- r.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- r.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
- holder.appVersion,
- r.mProfile.getCurrentHostingComponentTypes(),
- r.mProfile.getHistoricalHostingComponentTypes());
- });
}
}
@@ -15866,7 +15827,15 @@
activeInstr.mWatcher = watcher;
activeInstr.mUiAutomationConnection = uiAutomationConnection;
activeInstr.mResultClass = className;
- activeInstr.mHasBackgroundActivityStartsPermission = false;
+ activeInstr.mHasBackgroundActivityStartsPermission =
+ isSdkInSandbox
+ // TODO(b/261864298): consider using START_ACTIVITIES_FROM_BACKGROUND.
+ && checkPermission(
+ android.Manifest.permission
+ .START_ACTIVITIES_FROM_SDK_SANDBOX,
+ Binder.getCallingPid(),
+ Binder.getCallingUid())
+ == PackageManager.PERMISSION_GRANTED;
activeInstr.mHasBackgroundForegroundServiceStartsPermission = false;
// Instrumenting sdk sandbox without a restart is not supported
activeInstr.mNoRestart = false;
@@ -16863,7 +16832,7 @@
for (int i = 0; i < N; i++) {
PendingTempAllowlist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag,
+ ptw.duration, ptw.type, false, ptw.reasonCode, ptw.tag,
ptw.callingUid);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 942d35a..a057f32 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -4095,6 +4095,7 @@
pw.println(" lru: raw LRU process list");
pw.println(" binder-proxies: stats on binder objects and IPCs");
pw.println(" settings: currently applied config settings");
+ pw.println(" timers: the current ANR timer state");
pw.println(" service [COMP_SPEC]: service client-side state");
pw.println(" package [PACKAGE_NAME]: all state related to given package");
pw.println(" all: dump all activities");
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 7d98443..e0a2246 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -20,6 +20,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.content.pm.ApplicationInfo;
+import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArraySet;
@@ -267,6 +268,7 @@
private class AnrRecord {
final ProcessRecord mApp;
final int mPid;
+ final int mUid;
final String mActivityShortComponentName;
final String mParentShortComponentName;
final TimeoutRecord mTimeoutRecord;
@@ -283,6 +285,7 @@
Future<File> firstPidFilePromise) {
mApp = anrProcess;
mPid = anrProcess.mPid;
+ mUid = anrProcess.uid;
mActivityShortComponentName = activityShortComponentName;
mParentShortComponentName = parentShortComponentName;
mTimeoutRecord = timeoutRecord;
diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
new file mode 100644
index 0000000..378a386
--- /dev/null
+++ b/services/core/java/com/android/server/am/AnrTimer.java
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.RingBuffer;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class managers AnrTimers. An AnrTimer is a substitute for a delayed Message. In legacy
+ * mode, the timer just sends a delayed message. In modern mode, the timer is implemented in
+ * native code; on expiration, the message is sent without delay.
+ *
+ * <p>There are four external operations on a timer:
+ * <ul>
+ *
+ * <li>{@link #start} starts a timer. The timer is started with an object that the message
+ * argument. The timer is also given the pid and uid of the target. A timer that is started must
+ * be canceled, accepted, or discarded.
+ *
+ * <li>{@link #cancel} stops a timer and removes any in-flight expiration messages.
+ *
+ * <li>{@link #accept} acknowledges that the timer has expired, and that an ANR should be
+ * generated. This clears bookkeeping information for the timer.
+ *
+ * <li>{@link #discard} acknowledges that the timer has expired but, for other reasons, no ANR
+ * will be generated. This clears bookkeeping information for the timer.
+ *
+ *</li></p>
+ *
+ * <p>There is one internal operation on a timer: {@link #expire}. A timer may have automatic
+ * extensions enabled. If so, the extension is computed and if the extension is non-zero, the timer
+ * is restarted with the extension timeout. If extensions are disabled or if the extension is zero,
+ * the client process is notified of the expiration.
+ *
+ * @hide
+ */
+class AnrTimer<V> {
+
+ /**
+ * The log tag.
+ */
+ final static String TAG = "AnrTimer";
+
+ /**
+ * The trace track for these events. There is a single track for all AnrTimer instances. The
+ * tracks give a sense of handler latency: the time between timer expiration and ANR
+ * collection.
+ */
+ private final static String TRACK = "AnrTimer";
+
+ /**
+ * Enable debug messages.
+ */
+ private static boolean DEBUG = false;
+
+ /**
+ * The trace tag.
+ */
+ private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+ /**
+ * Enable tracing from the time a timer expires until it is accepted or discarded. This is
+ * used to diagnose long latencies in the client.
+ */
+ private static final boolean ENABLE_TRACING = false;
+
+ /**
+ * The status of an ANR timer. TIMER_INVALID status is returned when an error is detected.
+ */
+ private static final int TIMER_INVALID = 0;
+ private static final int TIMER_RUNNING = 1;
+ private static final int TIMER_EXPIRED = 2;
+
+ @IntDef(prefix = { "TIMER_" }, value = {
+ TIMER_INVALID, TIMER_RUNNING, TIMER_EXPIRED
+ })
+ private @interface TimerStatus {}
+
+ /**
+ * A static list of all known AnrTimer instances, used for dumping and testing.
+ */
+ @GuardedBy("sAnrTimerList")
+ private static final ArrayList<WeakReference<AnrTimer>> sAnrTimerList = new ArrayList<>();
+
+ /**
+ * An error is defined by its issue, the operation that detected the error, the tag of the
+ * affected service, a short stack of the bad call, and the stringified arg associated with
+ * the error.
+ */
+ private static final class Error {
+ /** The issue is the kind of error that was detected. This is a free-form string. */
+ final String issue;
+ /** The operation that detected the error: start, cancel, accept, or discard. */
+ final String operation;
+ /** The argument (stringified) passed in to the operation. */
+ final String arg;
+ /** The tag of the associated AnrTimer. */
+ final String tag;
+ /** A partial stack that localizes the caller of the operation. */
+ final StackTraceElement[] stack;
+ /** The date, in local time, the error was created. */
+ final String date;
+
+ Error(@NonNull String issue, @NonNull String operation, @NonNull String tag,
+ @NonNull StackTraceElement[] stack, @NonNull String arg) {
+ this.issue = issue;
+ this.operation = operation;
+ this.tag = tag;
+ this.stack = stack;
+ this.arg = arg;
+ this.date = new Date().toString();
+ }
+ }
+
+ /**
+ * A list of errors detected during processing. Errors correspond to "timer not found"
+ * conditions. The stack trace identifies the source of the call. The list is
+ * first-in/first-out, and the size is limited to 20.
+ */
+ @GuardedBy("sErrors")
+ private static final RingBuffer<Error> sErrors = new RingBuffer<>(Error.class, 20);
+
+ /**
+ * A record of a single anr timer. The pid and uid are retained for reference but they do not
+ * participate in the equality tests. A {@link Timer} is bound to its parent {@link AnrTimer}
+ * through the owner field. Access to timer fields is guarded by the mLock of the owner.
+ */
+ private static class Timer {
+ /** The AnrTimer that is managing this Timer. */
+ final AnrTimer owner;
+
+ /** The argument that uniquely identifies the Timer in the context of its current owner. */
+ final Object arg;
+ /** The pid of the process being tracked by this Timer. */
+ final int pid;
+ /** The uid of the process being tracked by this Timer as reported by the kernel. */
+ final int uid;
+ /** The original timeout. */
+ final long timeoutMs;
+
+ /** The status of the Timer. */
+ @GuardedBy("owner.mLock")
+ @TimerStatus
+ int status;
+
+ /** The absolute time the timer was startd */
+ final long startedMs;
+
+ /** Fields used by the native timer service. */
+
+ /** The timer ID: used to exchange information with the native service. */
+ int timerId;
+
+ /** Fields used by the legacy timer service. */
+
+ /**
+ * The process's cpu delay time when the timer starts . It is meaningful only if
+ * extendable is true. The cpu delay is cumulative, so the incremental delay that occurs
+ * during a timer is the delay at the end of the timer minus this value. Units are in
+ * milliseconds.
+ */
+ @GuardedBy("owner.mLock")
+ long initialCpuDelayMs;
+
+ /** True if the timer has been extended. */
+ @GuardedBy("owner.mLock")
+ boolean extended;
+
+ /**
+ * Fetch a new Timer. This is private. Clients should get a new timer using the obtain()
+ * method.
+ */
+ private Timer(int pid, int uid, @Nullable Object arg, long timeoutMs,
+ @NonNull AnrTimer service) {
+ this.arg = arg;
+ this.pid = pid;
+ this.uid = uid;
+ this.timerId = 0;
+ this.timeoutMs = timeoutMs;
+ this.startedMs = now();
+ this.owner = service;
+ this.initialCpuDelayMs = 0;
+ this.extended = false;
+ this.status = TIMER_INVALID;
+ }
+
+ /** Get a timer. This implementation constructs a new timer. */
+ static Timer obtain(int pid, int uid, @Nullable Object arg, long timeout,
+ @NonNull AnrTimer service) {
+ return new Timer(pid, uid, arg, timeout, service);
+ }
+
+ /** Release a timer. This implementation simply drops the timer. */
+ void release() {
+ }
+
+ /** Return the age of the timer. This is used for debugging. */
+ long age() {
+ return now() - startedMs;
+ }
+
+ /**
+ * The hash code is generated from the owner and the argument. By definition, the
+ * combination must be unique for the lifetime of an in-use Timer.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(owner, arg);
+ }
+
+ /**
+ * The equality check compares the owner and the argument. By definition, the combination
+ * must be unique for the lifetime of an in-use Timer.
+ */
+ @Override
+ public boolean equals(Object r) {
+ if (r instanceof Timer) {
+ Timer t = (Timer) r;
+ return Objects.equals(owner, t.owner) && Objects.equals(arg, t.arg);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final int myStatus;
+ synchronized (owner.mLock) {
+ myStatus = status;
+ }
+ return "timerId=" + timerId + " pid=" + pid + " uid=" + uid
+ + " " + statusString(myStatus) + " " + owner.mLabel;
+ }
+ }
+
+ /** A lock for the AnrTimer instance. */
+ private final Object mLock = new Object();
+
+ /**
+ * The map from client argument to the associated timer.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<V, Timer> mTimerMap = new ArrayMap<>();
+
+ /** The highwater mark of started, but not closed, timers. */
+ @GuardedBy("mLock")
+ private int mMaxStarted = 0;
+
+ /**
+ * The total number of timers started.
+ */
+ @GuardedBy("mLock")
+ private int mTotalStarted = 0;
+
+ /**
+ * The total number of errors detected.
+ */
+ @GuardedBy("mLock")
+ private int mTotalErrors = 0;
+
+ /**
+ * The total number of timers that have expired.
+ */
+ @GuardedBy("mLock")
+ private int mTotalExpired = 0;
+
+ /**
+ * A TimerService that generates a timeout event <n> milliseconds in the future. See the
+ * class documentation for an explanation of the operations.
+ */
+ private abstract class TimerService {
+ /** Start a timer. The timeout must be initialized. */
+ abstract boolean start(@NonNull Timer timer);
+
+ abstract void cancel(@NonNull Timer timer);
+
+ abstract void accept(@NonNull Timer timer);
+
+ abstract void discard(@NonNull Timer timer);
+ }
+
+ /**
+ * A class to assist testing. All methods are null by default but can be overridden as
+ * necessary for a test.
+ */
+ @VisibleForTesting
+ static class Injector {
+ /**
+ * Return a handler for the given Callback.
+ */
+ Handler getHandler(@NonNull Handler.Callback callback) {
+ return null;
+ };
+
+ /**
+ * Return a CpuTracker.
+ */
+ CpuTracker getTracker() {
+ return null;
+ }
+ }
+
+ /**
+ * A helper class to measure CPU delays. Given a process ID, this class will return the
+ * cumulative CPU delay for the PID, since process inception. This class is defined to assist
+ * testing.
+ */
+ @VisibleForTesting
+ static class CpuTracker {
+ /**
+ * The parameter to ProcessCpuTracker indicates that statistics should be collected on a
+ * single process and not on the collection of threads associated with that process.
+ */
+ private final ProcessCpuTracker mCpu = new ProcessCpuTracker(false);
+
+ /** A simple wrapper to fetch the delay. This method can be overridden for testing. */
+ long delay(int pid) {
+ return mCpu.getCpuDelayTimeForPid(pid);
+ }
+ }
+
+ /**
+ * The "user-space" implementation of the timer service. This service uses its own message
+ * handler to create timeouts.
+ */
+ private class HandlerTimerService extends TimerService {
+ /** The lock for this handler */
+ private final Object mLock = new Object();
+
+ /** The message handler for scheduling future events. */
+ private final Handler mHandler;
+
+ /** The interface to fetch process statistics that might extend an ANR timeout. */
+ private final CpuTracker mCpu;
+
+ /** Create a HandlerTimerService based on the input handler. */
+ HandlerTimerService(@NonNull Handler handler) {
+ mHandler = new Handler(handler.getLooper(), this::expires);
+ mCpu = new CpuTracker();
+ }
+
+ /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */
+ @VisibleForTesting
+ HandlerTimerService(@NonNull Injector injector) {
+ mHandler = injector.getHandler(this::expires);
+ mCpu = injector.getTracker();
+ }
+
+ /** Post a message with the specified timeout. The timer is not modified. */
+ private void post(@NonNull Timer t, long timeoutMillis) {
+ final Message msg = mHandler.obtainMessage();
+ msg.obj = t;
+ mHandler.sendMessageDelayed(msg, timeoutMillis);
+ }
+
+ /**
+ * The local expiration handler first attempts to compute a timer extension. If the timer
+ * should be extended, it is rescheduled in the future (granting more time to the
+ * associated process). If the timer should not be extended then the timeout is delivered
+ * to the client.
+ *
+ * A process is extended to account for the time the process was swapped out and was not
+ * runnable through no fault of its own. A timer can only be extended once and only if
+ * the AnrTimer permits extensions. Finally, a timer will never be extended by more than
+ * the original timeout, so the total timeout will never be more than twice the originally
+ * configured timeout.
+ */
+ private boolean expires(Message msg) {
+ Timer t = (Timer) msg.obj;
+ synchronized (mLock) {
+ long extension = 0;
+ if (mExtend && !t.extended) {
+ extension = mCpu.delay(t.pid) - t.initialCpuDelayMs;
+ if (extension < 0) extension = 0;
+ if (extension > t.timeoutMs) extension = t.timeoutMs;
+ t.extended = true;
+ }
+ if (extension > 0) {
+ post(t, extension);
+ } else {
+ onExpiredLocked(t);
+ }
+ }
+ return true;
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ boolean start(@NonNull Timer t) {
+ if (mExtend) {
+ t.initialCpuDelayMs = mCpu.delay(t.pid);
+ }
+ post(t, t.timeoutMs);
+ return true;
+ }
+
+ @Override
+ void cancel(@NonNull Timer t) {
+ mHandler.removeMessages(0, t);
+ }
+
+ @Override
+ void accept(@NonNull Timer t) {
+ // Nothing to do.
+ }
+
+ @Override
+ void discard(@NonNull Timer t) {
+ // Nothing to do.
+ }
+
+ /** The string identifies this subclass of AnrTimerService as being based on handlers. */
+ @Override
+ public String toString() {
+ return "handler";
+ }
+ }
+
+ /**
+ * The handler for messages sent from this instance.
+ */
+ private final Handler mHandler;
+
+ /**
+ * The message type for messages sent from this interface.
+ */
+ private final int mWhat;
+
+ /**
+ * A label that identifies the AnrTimer associated with a Timer in log messages.
+ */
+ private final String mLabel;
+
+ /**
+ * Whether this timer instance supports extending timeouts.
+ */
+ private final boolean mExtend;
+
+ /**
+ * The timer service to use for this AnrTimer.
+ */
+ private final TimerService mTimerService;
+
+ /**
+ * Whether or not canceling a non-existent timer is an error. Clients often cancel freely
+ * preemptively, without knowing if the timer was ever started. Keeping this variable true
+ * means that such behavior is not an error.
+ */
+ private final boolean mLenientCancel = true;
+
+ /**
+ * The common constructor. A null injector results in a normal, production timer.
+ */
+ @VisibleForTesting
+ AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend,
+ @Nullable Injector injector) {
+ mHandler = handler;
+ mWhat = what;
+ mLabel = label;
+ mExtend = extend;
+ if (injector == null) {
+ mTimerService = new HandlerTimerService(handler);
+ } else {
+ mTimerService = new HandlerTimerService(injector);
+ }
+ synchronized (sAnrTimerList) {
+ sAnrTimerList.add(new WeakReference(this));
+ }
+ Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label));
+ }
+
+ /**
+ * Create one timer instance for production. The client can ask for extensible timeouts.
+ */
+ AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
+ this(handler, what, label, extend, null);
+ }
+
+ /**
+ * Create one timer instance for production. There are no extensible timeouts.
+ */
+ AnrTimer(@NonNull Handler handler, int what, @NonNull String label) {
+ this(handler, what, label, false, null);
+ }
+
+ /**
+ * Start a trace on the timer. The trace is laid down in the AnrTimerTrack.
+ */
+ private void traceBegin(Timer t, String what) {
+ if (ENABLE_TRACING) {
+ final String label = formatSimple("%s(%d,%d,%s)", what, t.pid, t.uid, mLabel);
+ final int cookie = t.hashCode();
+ Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK, label, cookie);
+ }
+ }
+
+ /**
+ * End a trace on the timer.
+ */
+ private void traceEnd(Timer t) {
+ if (ENABLE_TRACING) {
+ final int cookie = t.hashCode();
+ Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK, cookie);
+ }
+ }
+
+ /**
+ * Return the string representation for a timer status.
+ */
+ private static String statusString(int s) {
+ switch (s) {
+ case TIMER_INVALID: return "invalid";
+ case TIMER_RUNNING: return "running";
+ case TIMER_EXPIRED: return "expired";
+ }
+ return formatSimple("unknown: %d", s);
+ }
+
+ /**
+ * Delete the timer associated with arg from the maps and return it. Return null if the timer
+ * was not found.
+ */
+ @GuardedBy("mLock")
+ private Timer removeLocked(V arg) {
+ Timer timer = mTimerMap.remove(arg);
+ return timer;
+ }
+
+ /**
+ * Return the number of timers currently running.
+ */
+ @VisibleForTesting
+ static int sizeOfTimerList() {
+ synchronized (sAnrTimerList) {
+ int totalTimers = 0;
+ for (int i = 0; i < sAnrTimerList.size(); i++) {
+ AnrTimer client = sAnrTimerList.get(i).get();
+ if (client != null) totalTimers += client.mTimerMap.size();
+ }
+ return totalTimers;
+ }
+ }
+
+ /**
+ * Clear out all existing timers. This will lead to unexpected behavior if used carelessly.
+ * It is available only for testing. It returns the number of times that were actually
+ * erased.
+ */
+ @VisibleForTesting
+ static int resetTimerListForHermeticTest() {
+ synchronized (sAnrTimerList) {
+ int mapLen = 0;
+ for (int i = 0; i < sAnrTimerList.size(); i++) {
+ AnrTimer client = sAnrTimerList.get(i).get();
+ if (client != null) {
+ mapLen += client.mTimerMap.size();
+ client.mTimerMap.clear();
+ }
+ }
+ if (mapLen > 0) {
+ Log.w(TAG, formatSimple("erasing timer list: clearing %d timers", mapLen));
+ }
+ return mapLen;
+ }
+ }
+
+ /**
+ * Report something about a timer.
+ */
+ private void report(@NonNull Timer timer, @NonNull String msg) {
+ Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg));
+ }
+
+ /**
+ * Start a timer.
+ */
+ boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this);
+ synchronized (mLock) {
+ Timer old = mTimerMap.get(arg);
+ if (old != null) {
+ // There is an existing timer. This is a protocol error in the client. Record
+ // the error and then clean up by canceling running timers and discarding expired
+ // timers.
+ restartedLocked(old.status, arg);
+ if (old.status == TIMER_EXPIRED) {
+ discard(arg);
+ } else {
+ cancel(arg);
+ }
+ }
+ if (mTimerService.start(timer)) {
+ timer.status = TIMER_RUNNING;
+ mTimerMap.put(arg, timer);
+ mTotalStarted++;
+ mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
+ if (DEBUG) report(timer, "start");
+ return true;
+ } else {
+ Log.e(TAG, "AnrTimer.start failed");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Cancel a timer. Return false if the timer was not found.
+ */
+ boolean cancel(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ if (!mLenientCancel) notFoundLocked("cancel", arg);
+ return false;
+ }
+ mTimerService.cancel(timer);
+ // There may be an expiration message in flight. Cancel it.
+ mHandler.removeMessages(mWhat, arg);
+ if (DEBUG) report(timer, "cancel");
+ timer.release();
+ return true;
+ }
+ }
+
+ /**
+ * Accept a timer in the framework-level handler. The timeout has been accepted and the
+ * timeout handler is executing. Return false if the timer was not found.
+ */
+ boolean accept(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ notFoundLocked("accept", arg);
+ return false;
+ }
+ mTimerService.accept(timer);
+ traceEnd(timer);
+ if (DEBUG) report(timer, "accept");
+ timer.release();
+ return true;
+ }
+ }
+
+ /**
+ * Discard a timer in the framework-level handler. For whatever reason, the timer is no
+ * longer interesting. No statistics are collected. Return false if the time was not found.
+ */
+ boolean discard(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ notFoundLocked("discard", arg);
+ return false;
+ }
+ mTimerService.discard(timer);
+ traceEnd(timer);
+ if (DEBUG) report(timer, "discard");
+ timer.release();
+ return true;
+ }
+ }
+
+ /**
+ * The notifier that a timer has fired. The timer is not modified.
+ */
+ @GuardedBy("mLock")
+ private void onExpiredLocked(@NonNull Timer timer) {
+ if (DEBUG) report(timer, "expire");
+ traceBegin(timer, "expired");
+ mHandler.sendMessage(Message.obtain(mHandler, mWhat, timer.arg));
+ synchronized (mLock) {
+ mTotalExpired++;
+ }
+ }
+
+ /**
+ * Dump a single AnrTimer.
+ */
+ private void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.format("timer: %s\n", mLabel);
+ pw.increaseIndent();
+ pw.format("started=%d maxStarted=%d running=%d expired=%d error=%d\n",
+ mTotalStarted, mMaxStarted, mTimerMap.size(),
+ mTotalExpired, mTotalErrors);
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Enable or disable debugging.
+ */
+ static void debug(boolean f) {
+ DEBUG = f;
+ }
+
+ /**
+ * The current time in milliseconds.
+ */
+ private static long now() {
+ return SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Log an error. A limited stack trace leading to the client call that triggered the error is
+ * recorded. The stack trace assumes that this method is not called directly.
+ *
+ * If DEBUG is true, a log message is generated as well.
+ */
+ @GuardedBy("mLock")
+ private void recordErrorLocked(String operation, String errorMsg, Object arg) {
+ StackTraceElement[] s = Thread.currentThread().getStackTrace();
+ final String what = Objects.toString(arg);
+ // The copy range starts at the caller of the timer operation, and includes three levels.
+ // This should be enough to isolate the location of the call.
+ StackTraceElement[] location = Arrays.copyOfRange(s, 6, 9);
+ synchronized (sErrors) {
+ sErrors.append(new Error(errorMsg, operation, mLabel, location, what));
+ }
+ if (DEBUG) Log.w(TAG, operation + " " + errorMsg + " " + mLabel + " timer " + what);
+ mTotalErrors++;
+ }
+
+ /**
+ * Log an error about a timer not found.
+ */
+ @GuardedBy("mLock")
+ private void notFoundLocked(String operation, Object arg) {
+ recordErrorLocked(operation, "notFound", arg);
+ }
+
+ /**
+ * Log an error about a timer that is started when there is an existing timer.
+ */
+ @GuardedBy("mLock")
+ private void restartedLocked(@TimerStatus int status, Object arg) {
+ recordErrorLocked("start", status == TIMER_EXPIRED ? "autoDiscard" : "autoCancel", arg);
+ }
+
+ /**
+ * Dump a single error to the output stream.
+ */
+ private static void dump(IndentingPrintWriter ipw, int seq, Error err) {
+ ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag,
+ err.issue, err.arg);
+ ipw.format(" date:%s\n", err.date);
+ ipw.increaseIndent();
+ for (int i = 0; i < err.stack.length; i++) {
+ ipw.println(" " + err.stack[i].toString());
+ }
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Dump all errors to the output stream.
+ */
+ private static void dumpErrors(IndentingPrintWriter ipw) {
+ Error errors[];
+ synchronized (sErrors) {
+ if (sErrors.size() == 0) return;
+ errors = sErrors.toArray();
+ }
+ ipw.println("Errors");
+ ipw.increaseIndent();
+ for (int i = 0; i < errors.length; i++) {
+ if (errors[i] != null) dump(ipw, i, errors[i]);
+ }
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Dumpsys output.
+ */
+ static void dump(@NonNull PrintWriter pw, boolean verbose) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.println("AnrTimer statistics");
+ ipw.increaseIndent();
+ synchronized (sAnrTimerList) {
+ for (int i = 0; i < sAnrTimerList.size(); i++) {
+ AnrTimer client = sAnrTimerList.get(i).get();
+ if (client != null) client.dump(ipw);
+ }
+ }
+ if (verbose) dumpErrors(ipw);
+ ipw.format("AnrTimerEnd\n");
+ ipw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 46e5523..928b5d8 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -770,17 +770,6 @@
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
profile.setLastPssTime(now);
profile.addPss(pss, uss, rss, true, statType, pssDuration);
- proc.getPkgList().forEachPackageProcessStats(holder -> {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- pss, uss, rss,
- statType, pssDuration,
- holder.appVersion,
- profile.getCurrentHostingComponentTypes(),
- profile.getHistoricalHostingComponentTypes());
- });
if (DEBUG_PSS) {
Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index fc8175b..5d31d15 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -101,10 +101,9 @@
boolean runningOomAdjusted;
/**
- * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
- * used when deciding if we should extend the soft ANR timeout.
+ * True if a timer has been started against this queue.
*/
- long lastCpuDelayTime;
+ private boolean mTimeoutScheduled;
/**
* Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before
@@ -1344,6 +1343,21 @@
return head;
}
+ /**
+ * Set the timeout flag to indicate that an ANR timer has been started. A value of true means a
+ * timer is running; a value of false means there is no timer running.
+ */
+ void setTimeoutScheduled(boolean timeoutScheduled) {
+ mTimeoutScheduled = timeoutScheduled;
+ }
+
+ /**
+ * Get the timeout flag
+ */
+ boolean timeoutScheduled() {
+ return mTimeoutScheduled;
+ }
+
@Override
public String toString() {
if (mCachedToString == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8d2edaa..eb219a8 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -149,6 +149,8 @@
// We configure runnable size only once at boot; it'd be too complex to
// try resizing dynamically at runtime
mRunning = new BroadcastProcessQueue[mConstants.getMaxRunningQueues()];
+
+ mAnrTimer = new BroadcastAnrTimer(mLocalHandler);
}
/**
@@ -242,14 +244,19 @@
*/
private @UptimeMillisLong long mLastTestFailureTime;
+ /**
+ * The ANR timer service for broadcasts.
+ */
+ @GuardedBy("mService")
+ private final BroadcastAnrTimer mAnrTimer;
+
private static final int MSG_UPDATE_RUNNING_LIST = 1;
- private static final int MSG_DELIVERY_TIMEOUT_SOFT = 2;
- private static final int MSG_DELIVERY_TIMEOUT_HARD = 3;
- private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4;
- private static final int MSG_CHECK_HEALTH = 5;
- private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 6;
- private static final int MSG_PROCESS_FREEZABLE_CHANGED = 7;
- private static final int MSG_UID_STATE_CHANGED = 8;
+ private static final int MSG_DELIVERY_TIMEOUT = 2;
+ private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 3;
+ private static final int MSG_CHECK_HEALTH = 4;
+ private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 5;
+ private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
+ private static final int MSG_UID_STATE_CHANGED = 7;
private void enqueueUpdateRunningList() {
mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
@@ -264,12 +271,8 @@
updateRunningList();
return true;
}
- case MSG_DELIVERY_TIMEOUT_SOFT: {
- deliveryTimeoutSoft((BroadcastProcessQueue) msg.obj, msg.arg1);
- return true;
- }
- case MSG_DELIVERY_TIMEOUT_HARD: {
- deliveryTimeoutHard((BroadcastProcessQueue) msg.obj);
+ case MSG_DELIVERY_TIMEOUT: {
+ deliveryTimeout((BroadcastProcessQueue) msg.obj);
return true;
}
case MSG_BG_ACTIVITY_START_TIMEOUT: {
@@ -1024,12 +1027,12 @@
// immediately assume delivery success
final boolean assumeDelivered = r.isAssumedDelivered(index);
if (mService.mProcessesReady && !r.timeoutExempt && !assumeDelivered) {
- queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
-
+ queue.setTimeoutScheduled(true);
final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT
: mBgConstants.TIMEOUT);
- mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,
- MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
+ mAnrTimer.start(queue, softTimeoutMillis);
+ } else {
+ queue.setTimeoutScheduled(false);
}
if (r.mBackgroundStartPrivileges.allowsAny()) {
@@ -1107,7 +1110,7 @@
// If we were trying to deliver a manifest broadcast, throw the error as we need
// to try redelivering the broadcast to this receiver.
if (receiver instanceof ResolveInfo) {
- mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
+ mAnrTimer.cancel(queue);
throw new BroadcastDeliveryFailedException(e);
}
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
@@ -1156,41 +1159,29 @@
r.resultTo = null;
}
- private void deliveryTimeoutSoft(@NonNull BroadcastProcessQueue queue,
- int softTimeoutMillis) {
+ private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) {
synchronized (mService) {
- deliveryTimeoutSoftLocked(queue, softTimeoutMillis);
+ deliveryTimeoutLocked(queue);
}
}
- private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
- int softTimeoutMillis) {
- if (queue.app != null) {
- // Instead of immediately triggering an ANR, extend the timeout by
- // the amount of time the process was runnable-but-waiting; we're
- // only willing to do this once before triggering an hard ANR
- final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime;
- final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
- mLocalHandler.sendMessageDelayed(
- Message.obtain(mLocalHandler, MSG_DELIVERY_TIMEOUT_HARD, queue),
- hardTimeoutMillis);
- } else {
- deliveryTimeoutHardLocked(queue);
- }
- }
-
- private void deliveryTimeoutHard(@NonNull BroadcastProcessQueue queue) {
- synchronized (mService) {
- deliveryTimeoutHardLocked(queue);
- }
- }
-
- private void deliveryTimeoutHardLocked(@NonNull BroadcastProcessQueue queue) {
+ private void deliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_TIMEOUT,
- "deliveryTimeoutHardLocked");
+ "deliveryTimeoutLocked");
demoteFromRunningLocked(queue);
}
+ private class BroadcastAnrTimer extends AnrTimer<BroadcastProcessQueue> {
+ BroadcastAnrTimer(@NonNull Handler handler) {
+ super(Objects.requireNonNull(handler),
+ MSG_DELIVERY_TIMEOUT, "BROADCAST_TIMEOUT", true);
+ }
+
+ void start(@NonNull BroadcastProcessQueue queue, long timeoutMillis) {
+ start(queue, queue.app.getPid(), queue.app.uid, timeoutMillis);
+ }
+ }
+
@Override
public boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,
@Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
@@ -1292,14 +1283,16 @@
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
r.anrCount++;
if (app != null && !app.isDebugging()) {
+ mAnrTimer.accept(queue);
final String packageName = getReceiverPackageName(receiver);
final String className = getReceiverClassName(receiver);
mService.appNotResponding(queue.app,
TimeoutRecord.forBroadcastReceiver(r.intent, packageName, className));
+ } else {
+ mAnrTimer.discard(queue);
}
- } else {
- mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
- mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_HARD, queue);
+ } else if (queue.timeoutScheduled()) {
+ mAnrTimer.cancel(queue);
}
// Given that a receiver just finished, check if the "waitingFor" conditions are met.
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index f6859d1..caafb42 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -27,6 +27,8 @@
import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
import static android.os.Process.INVALID_UID;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.ActivityManager.ForegroundServiceApiType;
@@ -218,6 +220,24 @@
final ArrayList<Long> timestampsFound = new ArrayList<>();
for (int i = 0, size = apiTypes.size(); i < size; i++) {
final int apiType = apiTypes.get(i);
+
+ // remove the FGS record from the stack
+ final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
+ uidState.mRunningFgs.get(apiType);
+ if (runningFgsOfType == null) {
+ Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
+ + " in package " + record.packageName);
+ continue;
+ }
+
+ runningFgsOfType.remove(record.getComponentName());
+ if (runningFgsOfType.size() == 0) {
+ // there's no more FGS running for this type, just get rid of it
+ uidState.mRunningFgs.remove(apiType);
+ // but we need to keep track of the timestamp in case an API stops
+ uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
+ }
+
final int apiTypeIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
if (apiTypeIndex < 0) {
Slog.w(TAG, "Logger should be tracking FGS types correctly for UID " + uid
@@ -236,22 +256,6 @@
// remove the last API close call
uidState.mApiClosedCalls.remove(apiType);
}
- // remove the FGS record from the stack
- final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
- uidState.mRunningFgs.get(apiType);
- if (runningFgsOfType == null) {
- Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
- + " in package " + record.packageName);
- continue;
- }
-
- runningFgsOfType.remove(record.getComponentName());
- if (runningFgsOfType.size() == 0) {
- // there's no more FGS running for this type, just get rid of it
- uidState.mRunningFgs.remove(apiType);
- // but we need to keep track of the timestamp in case an API stops
- uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
- }
}
if (!apisFound.isEmpty()) {
// time to log the call
@@ -381,9 +385,14 @@
// initialize if we don't contain
uidState.mOpenedWithoutFgsCount.put(apiType, 0);
}
- if (uidState.mOpenedWithoutFgsCount.get(apiType) != 0) {
+ int apiOpenWithoutFgsCount = uidState.mOpenedWithoutFgsCount.get(apiType);
+ if (apiOpenWithoutFgsCount != 0) {
+ apiOpenWithoutFgsCount -= 1;
+ if (apiOpenWithoutFgsCount == 0) {
+ uidState.mApiOpenCalls.remove(apiType);
+ }
uidState.mOpenedWithoutFgsCount
- .put(apiType, uidState.mOpenedWithoutFgsCount.get(apiType) - 1);
+ .put(apiType, apiOpenWithoutFgsCount);
return System.currentTimeMillis();
}
// This is a part of a valid active FGS
@@ -520,7 +529,10 @@
r.mAllowWIUByBindings,
r.mAllowStartForegroundNoBinding,
r.mAllowStartInBindService,
- r.mAllowStartByBindings);
+ r.mAllowStartByBindings,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false
+ );
}
/**
@@ -578,7 +590,10 @@
0,
0,
0,
- 0);
+ 0,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false
+ );
}
/**
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 905589f..824bdd4 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -417,59 +417,6 @@
}
}
- public enum FrameRate {
- FPS_DEFAULT(0),
- FPS_30(30),
- FPS_36(36),
- FPS_40(40),
- FPS_45(45),
- FPS_48(48),
- FPS_60(60),
- FPS_72(72),
- FPS_90(90),
- FPS_120(120),
- FPS_144(144),
- FPS_INVALID(-1);
-
- public final int fps;
-
- FrameRate(int fps) {
- this.fps = fps;
- }
- }
-
- // Turn the raw string to the corresponding fps int.
- // Return 0 when disabling, -1 for invalid fps.
- static int getFpsInt(String raw) {
- // TODO(b/243448953): make sure this translates to proper values based on current display
- switch (raw) {
- case "30":
- return FrameRate.FPS_30.fps;
- case "36":
- return FrameRate.FPS_36.fps;
- case "40":
- return FrameRate.FPS_40.fps;
- case "45":
- return FrameRate.FPS_45.fps;
- case "48":
- return FrameRate.FPS_48.fps;
- case "60":
- return FrameRate.FPS_60.fps;
- case "72":
- return FrameRate.FPS_72.fps;
- case "90":
- return FrameRate.FPS_90.fps;
- case "120":
- return FrameRate.FPS_120.fps;
- case "144":
- return FrameRate.FPS_144.fps;
- case "disable":
- case "":
- return FrameRate.FPS_DEFAULT.fps;
- }
- return FrameRate.FPS_INVALID.fps;
- }
-
/**
* Called by games to communicate the current state to the platform.
*
@@ -717,7 +664,12 @@
}
public synchronized int getFps() {
- return GameManagerService.getFpsInt(mFps);
+ try {
+ final int fpsInt = Integer.parseInt(mFps);
+ return fpsInt;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
synchronized String getFpsStr() {
@@ -757,7 +709,12 @@
}
android.app.GameModeConfiguration toPublicGameModeConfig() {
- int fpsOverride = getFpsInt(mFps);
+ int fpsOverride;
+ try {
+ fpsOverride = Integer.parseInt(mFps);
+ } catch (NumberFormatException e) {
+ fpsOverride = 0;
+ }
// TODO(b/243448953): match to proper value in case of display change?
fpsOverride = fpsOverride > 0 ? fpsOverride
: android.app.GameModeConfiguration.FPS_OVERRIDE_NONE;
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 00ff489..ab57c4f 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -241,8 +241,10 @@
case "--fps":
if (fpsStr == null) {
fpsStr = getNextArgRequired();
- if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
- pw.println("Invalid frame rate '" + fpsStr + "'");
+ try {
+ Integer.parseInt(fpsStr);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid frame rate: '" + fpsStr + "'");
return -1;
}
} else {
@@ -375,8 +377,8 @@
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
pw.println(" specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
- pw.println(" if supported.");
+ pw.println(" --fps: Integer value to set app to run at the specified fps,");
+ pw.println(" if supported. 0 to disable.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
pw.println(" This should only be used to reset any override to non custom game mode");
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 60af280..e08fdd6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1437,6 +1437,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dp")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dp")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1459,6 +1460,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dpSink")
.record();
toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
}
@@ -1474,6 +1476,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectHearingAid")
+ .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1531,6 +1534,7 @@
}
});
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+ .set(MediaMetrics.Property.EVENT, "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(device,
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 95b6c2c..81365bf 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -719,7 +719,9 @@
/*package*/ void initSafeMediaVolumeIndex() {
for (int i = 0; i < mSafeMediaVolumeDevices.size(); ++i) {
int deviceType = mSafeMediaVolumeDevices.keyAt(i);
- mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+ if (mSafeMediaVolumeDevices.valueAt(i) == SAFE_MEDIA_VOLUME_UNINITIALIZED) {
+ mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+ }
}
}
@@ -743,7 +745,7 @@
}
/*package*/ boolean safeDevicesContains(int device) {
- return mSafeMediaVolumeDevices.indexOfKey(device) >= 0;
+ return mSafeMediaVolumeDevices.get(device, SAFE_MEDIA_VOLUME_UNINITIALIZED) >= 0;
}
/*package*/ void invalidatPendingVolumeCommand() {
@@ -1014,6 +1016,7 @@
initCsd();
synchronized (mSafeMediaVolumeStateLock) {
+ initSafeMediaVolumeIndex();
updateSafeMediaVolume_l(caller);
}
}
@@ -1065,11 +1068,18 @@
}
private int getSafeDeviceMediaVolumeIndex(int deviceType) {
- // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
- // instead of computing it from the volume curves
- if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
- || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) {
- return mSafeMediaVolumeIndex;
+ if (!mEnableCsd.get()) {
+ // legacy hearing safety only for wired and USB HS/HP
+ if (deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+ // legacy hearing safety uses mSafeMediaVolumeIndex for wired HS/HP
+ // instead of computing it from the volume curves
+ return mSafeMediaVolumeIndex;
+ }
+
+ if (deviceType != AudioSystem.DEVICE_OUT_USB_HEADSET) {
+ return SAFE_MEDIA_VOLUME_UNINITIALIZED;
+ }
}
// determine UI volume index corresponding to the wanted safe gain in dBFS
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 6edbfb7..4df2581 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -57,6 +57,7 @@
@NonNull private final FaceManager mFaceManager;
@NonNull private final FingerprintManager mFingerprintManager;
+ private final boolean mEnabled;
private final float mThreshold;
private final int mModality;
@@ -80,6 +81,7 @@
public AuthenticationStatsCollector(@NonNull Context context, int modality,
@NonNull BiometricNotification biometricNotification) {
mContext = context;
+ mEnabled = context.getResources().getBoolean(R.bool.config_biometricFrrNotificationEnabled);
mThreshold = context.getResources()
.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1);
mUserAuthenticationStatsMap = new HashMap<>();
@@ -110,6 +112,11 @@
/** Update total authentication and rejected attempts. */
public void authenticate(int userId, boolean authenticated) {
+ // Don't collect data if the feature is disabled.
+ if (!mEnabled) {
+ return;
+ }
+
// Don't collect data for single-modality devices or user has both biometrics enrolled.
if (isSingleModalityDevice()
|| (hasEnrolledFace(userId) && hasEnrolledFingerprint(userId))) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 50d375c..35fc43a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -43,7 +43,6 @@
import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -242,9 +241,6 @@
vendorCode,
getTargetUserId()));
- if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) {
- BiometricNotificationUtils.showReEnrollmentNotification(getContext());
- }
super.onError(error, vendorCode);
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index df16c5b..4ef2f1e 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -77,6 +77,7 @@
import com.android.framework.protobuf.nano.MessageNano;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.camera.flags.Flags;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -244,6 +245,7 @@
public List<CameraStreamStats> mStreamStats;
public String mUserTag;
public int mVideoStabilizationMode;
+ public boolean mUsedUltraWide;
public final long mLogId;
public final int mSessionIndex;
@@ -271,7 +273,8 @@
public void markCompleted(int internalReconfigure, long requestCount,
long resultErrorCount, boolean deviceError,
List<CameraStreamStats> streamStats, String userTag,
- int videoStabilizationMode, CameraExtensionSessionStats extStats) {
+ int videoStabilizationMode, boolean usedUltraWide,
+ CameraExtensionSessionStats extStats) {
if (mCompleted) {
return;
}
@@ -284,6 +287,7 @@
mStreamStats = streamStats;
mUserTag = userTag;
mVideoStabilizationMode = videoStabilizationMode;
+ mUsedUltraWide = usedUltraWide;
mExtSessionStats = extStats;
if (CameraServiceProxy.DEBUG) {
Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
@@ -873,6 +877,10 @@
streamCount = e.mStreamStats.size();
}
if (CameraServiceProxy.DEBUG) {
+ String ultrawideDebug = Flags.logUltrawideUsage()
+ ? ", wideAngleUsage " + e.mUsedUltraWide
+ : "";
+
Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
+ " clientName " + e.mClientName
+ ", duration " + e.getDuration()
@@ -889,6 +897,7 @@
+ ", streamCount is " + streamCount
+ ", userTag is " + e.mUserTag
+ ", videoStabilizationMode " + e.mVideoStabilizationMode
+ + ultrawideDebug
+ ", logId " + e.mLogId
+ ", sessionIndex " + e.mSessionIndex
+ ", mExtSessionStats {type " + extensionType
@@ -952,8 +961,9 @@
MessageNano.toByteArray(streamProtos[2]),
MessageNano.toByteArray(streamProtos[3]),
MessageNano.toByteArray(streamProtos[4]),
- e.mUserTag, e.mVideoStabilizationMode, e.mLogId, e.mSessionIndex,
- extensionType, extensionIsAdvanced);
+ e.mUserTag, e.mVideoStabilizationMode,
+ e.mLogId, e.mSessionIndex,
+ extensionType, extensionIsAdvanced, e.mUsedUltraWide);
}
}
@@ -1148,6 +1158,7 @@
List<CameraStreamStats> streamStats = cameraState.getStreamStats();
String userTag = cameraState.getUserTag();
int videoStabilizationMode = cameraState.getVideoStabilizationMode();
+ boolean usedUltraWide = Flags.logUltrawideUsage() ? cameraState.getUsedUltraWide() : false;
long logId = cameraState.getLogId();
int sessionIdx = cameraState.getSessionIndex();
CameraExtensionSessionStats extSessionStats = cameraState.getExtensionSessionStats();
@@ -1205,7 +1216,7 @@
Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
/*resultErrorCount*/0, /*deviceError*/false, streamStats,
- /*userTag*/"", /*videoStabilizationMode*/-1,
+ /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false,
new CameraExtensionSessionStats());
mCameraUsageHistory.add(oldEvent);
}
@@ -1217,7 +1228,7 @@
doneEvent.markCompleted(internalReconfigureCount, requestCount,
resultErrorCount, deviceError, streamStats, userTag,
- videoStabilizationMode, extSessionStats);
+ videoStabilizationMode, usedUltraWide, extSessionStats);
mCameraUsageHistory.add(doneEvent);
// Do not double count device error
deviceError = false;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 1012bc1..fac727f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -37,7 +37,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.TaskStackListener;
+import android.app.ActivityManagerInternal;
+import android.app.IProcessObserver;
import android.content.Context;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -184,7 +185,30 @@
private final SystemPropertySetter mSystemPropertySetter;
@VisibleForTesting
- TaskStackListener mOverrideRequestTaskStackListener = new OverrideRequestTaskStackListener();
+ final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+ synchronized (mLock) {
+ if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
+ return;
+ }
+
+ OverrideRequest request = mActiveOverride.get();
+ if (pid != request.getPid() || uid != request.getUid()) {
+ return;
+ }
+ if (!fg) {
+ mOverrideRequestController.cancelRequest(request);
+ }
+ }
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {}
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {}
+ };
@VisibleForTesting
ActivityTaskManagerInternal.ScreenObserver mOverrideRequestScreenObserver =
new OverrideRequestScreenObserver();
@@ -239,8 +263,9 @@
mFoldedDeviceStates = readFoldedStates();
}
- mActivityTaskManagerInternal.registerTaskStackListener(mOverrideRequestTaskStackListener);
mActivityTaskManagerInternal.registerScreenObserver(mOverrideRequestScreenObserver);
+ LocalServices.getService(ActivityManagerInternal.class).registerProcessObserver(
+ mProcessObserver);
}
@VisibleForTesting
@@ -1288,23 +1313,6 @@
return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
}
- private class OverrideRequestTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
- throws RemoteException {
- synchronized (mLock) {
- if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
- return;
- }
-
- OverrideRequest request = mActiveOverride.get();
- if (!isTopApp(request.getPid())) {
- mOverrideRequestController.cancelRequest(request);
- }
- }
- }
- }
-
private class OverrideRequestScreenObserver implements
ActivityTaskManagerInternal.ScreenObserver {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index f4c84e7..f9aefd0 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -109,6 +109,7 @@
.setPackage(mContext.getPackageName());
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+
showNotification(
info.name, info.activeNotificationTitle,
String.format(info.activeNotificationContent, requesterApplicationLabel),
@@ -175,7 +176,7 @@
if (getNotificationInfos().get(state) == null) {
return;
}
- mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ mHandler.post(() -> mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID));
}
@Override
@@ -219,8 +220,10 @@
builder.addAction(action);
}
- mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ mHandler.post(() -> {
+ mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ });
}
private SparseArray<NotificationInfo> getNotificationInfos() {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 4bfc090..13ee47e 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -17,10 +17,12 @@
package com.android.server.display;
import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
import android.os.IBinder;
-import android.provider.DeviceConfigInterface;
+import android.os.PowerManager;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.brightness.clamper.HdrClamper;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.function.BooleanSupplier;
@@ -31,31 +33,46 @@
private final NormalBrightnessModeController mNormalBrightnessModeController =
new NormalBrightnessModeController();
+ private final HdrClamper mHdrClamper;
+
private final Runnable mModeChangeCallback;
private final boolean mUseNbmController;
+ private final boolean mUseHdrClamper;
+
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+ new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- DeviceConfigParameterProvider configParameterProvider) {
+ HdrClamper hdrClamper, DisplayManagerFlags flags) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
- mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
- mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
+ mHdrClamper = hdrClamper;
+ mUseHdrClamper = flags.isHdrClamperEnabled();
+ mUseNbmController = flags.isNbmControllerEnabled();
+ if (mUseNbmController) {
+ mNormalBrightnessModeController.resetNbmData(
+ displayDeviceConfig.getLuxThrottlingData());
+ }
+ if (mUseHdrClamper) {
+ mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
+ }
+
}
void dump(PrintWriter pw) {
pw.println("BrightnessRangeController:");
pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
+ pw.println(" mUseHdrClamper=" + mUseHdrClamper);
mHbmController.dump(pw);
mNormalBrightnessModeController.dump(pw);
-
+ mHdrClamper.dump(pw);
}
void onAmbientLuxChange(float ambientLux) {
@@ -63,6 +80,9 @@
() -> mNormalBrightnessModeController.onAmbientLuxChange(ambientLux),
() -> mHbmController.onAmbientLuxChange(ambientLux)
);
+ if (mUseHdrClamper) {
+ mHdrClamper.onAmbientLuxChange(ambientLux);
+ }
}
float getNormalBrightnessMax() {
@@ -81,6 +101,9 @@
displayDeviceConfig::getHdrBrightnessFromSdr);
}
);
+ if (mUseHdrClamper) {
+ mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
+ }
}
void stop() {
@@ -118,7 +141,10 @@
}
float getHdrBrightnessValue() {
- return mHbmController.getHdrBrightnessValue();
+ float hdrBrightness = mHbmController.getHdrBrightnessValue();
+ float brightnessMax = mUseHdrClamper ? mHdrClamper.getMaxBrightness()
+ : PowerManager.BRIGHTNESS_MAX;
+ return Math.min(hdrBrightness, brightnessMax);
}
float getTransitionPoint() {
@@ -138,4 +164,8 @@
hbmChangesFunc.run();
}
}
+
+ public float getHdrTransitionRate() {
+ return mUseHdrClamper ? mHdrClamper.getTransitionRate() : -1;
+ }
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 2d763bc..0d6635d 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -407,12 +407,6 @@
}
}
- void stop() {
- if (mEglContext != null && mEglDisplay != null) {
- EGL14.eglDestroyContext(mEglDisplay, mEglContext);
- }
- }
-
/**
* Draws an animation frame showing the color fade activated at the
* specified level.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e3dafa4..507ae26 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -53,6 +53,7 @@
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
+import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
@@ -232,7 +233,22 @@
* </point>
* </sdrHdrRatioMap>
* </highBrightnessMode>
- *
+ * <hdrBrightnessConfig>
+ * <brightnessMap>
+ * <point>
+ * <first>500</first>
+ * <second>0.3</second>
+ * </point>
+ * <point>
+ * <first>1200</first>
+ * <second>0.6</second>
+ * </point>
+ * </brightnessMap>
+ * <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>
+ * <brightnessIncreaseDurationMillis>10000</brightnessIncreaseDurationMillis>
+ * <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>
+ * <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>
+ * </hdrBrightnessConfig>
* <luxThrottling>
* <brightnessLimitMap>
* <type>default</type>
@@ -287,6 +303,8 @@
*
* <screenBrightnessRampIncreaseMaxMillis>2000</screenBrightnessRampIncreaseMaxMillis>
* <screenBrightnessRampDecreaseMaxMillis>3000</screenBrightnessRampDecreaseMaxMillis>
+ * <screenBrightnessRampIncreaseMaxIdleMillis>2000</screenBrightnessRampIncreaseMaxIdleMillis>
+ * <screenBrightnessRampDecreaseMaxIdleMillis>2000</screenBrightnessRampDecreaseMaxIdleMillis>
*
* <lightSensor>
* <type>android.sensor.light</type>
@@ -603,6 +621,8 @@
private float mBrightnessRampSlowIncreaseIdle = Float.NaN;
private long mBrightnessRampDecreaseMaxMillis = 0;
private long mBrightnessRampIncreaseMaxMillis = 0;
+ private long mBrightnessRampDecreaseMaxIdleMillis = 0;
+ private long mBrightnessRampIncreaseMaxIdleMillis = 0;
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is
@@ -769,6 +789,9 @@
@Nullable
private HostUsiVersion mHostUsiVersion;
+ @Nullable
+ private HdrBrightnessData mHdrBrightnessData;
+
@VisibleForTesting
DisplayDeviceConfig(Context context) {
mContext = context;
@@ -1059,6 +1082,14 @@
return mBrightnessRampIncreaseMaxMillis;
}
+ public long getBrightnessRampDecreaseMaxIdleMillis() {
+ return mBrightnessRampDecreaseMaxIdleMillis;
+ }
+
+ public long getBrightnessRampIncreaseMaxIdleMillis() {
+ return mBrightnessRampIncreaseMaxIdleMillis;
+ }
+
public int getAmbientHorizonLong() {
return mAmbientHorizonLong;
}
@@ -1544,6 +1575,14 @@
}
/**
+ * @return HDR brightness related configuration
+ */
+ @Nullable
+ public HdrBrightnessData getHdrBrightnessData() {
+ return mHdrBrightnessData;
+ }
+
+ /**
* @return Refresh rate range for specific profile id or null
*/
@Nullable
@@ -1670,6 +1709,8 @@
+ ", mBrightnessRampSlowIncreaseIdle=" + mBrightnessRampSlowIncreaseIdle
+ ", mBrightnessRampDecreaseMaxMillis=" + mBrightnessRampDecreaseMaxMillis
+ ", mBrightnessRampIncreaseMaxMillis=" + mBrightnessRampIncreaseMaxMillis
+ + ", mBrightnessRampDecreaseMaxIdleMillis=" + mBrightnessRampDecreaseMaxIdleMillis
+ + ", mBrightnessRampIncreaseMaxIdleMillis=" + mBrightnessRampIncreaseMaxIdleMillis
+ "\n"
+ "mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
@@ -1759,7 +1800,8 @@
+ "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ "\n"
- + "mUsiVersion= " + mHostUsiVersion
+ + "mUsiVersion= " + mHostUsiVersion + "\n"
+ + "mHdrBrightnessData" + mHdrBrightnessData
+ "}";
}
@@ -1823,6 +1865,7 @@
loadRefreshRateSetting(config);
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
loadUsiVersion(config);
+ mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -1863,6 +1906,8 @@
mBrightnessRampSlowIncreaseIdle = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampDecreaseMaxMillis = 0;
mBrightnessRampIncreaseMaxMillis = 0;
+ mBrightnessRampDecreaseMaxIdleMillis = 0;
+ mBrightnessRampIncreaseMaxIdleMillis = 0;
setSimpleMappingStrategyValues();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
@@ -2738,6 +2783,19 @@
mBrightnessRampSlowDecreaseIdle = mBrightnessRampSlowDecrease;
mBrightnessRampSlowIncreaseIdle = mBrightnessRampSlowIncrease;
}
+
+ final BigInteger increaseMaxIdle = config.getScreenBrightnessRampIncreaseMaxIdleMillis();
+ if (increaseMaxIdle != null) {
+ mBrightnessRampIncreaseMaxIdleMillis = increaseMaxIdle.intValue();
+ } else {
+ mBrightnessRampIncreaseMaxIdleMillis = mBrightnessRampIncreaseMaxMillis;
+ }
+ final BigInteger decreaseMaxIdle = config.getScreenBrightnessRampDecreaseMaxIdleMillis();
+ if (decreaseMaxIdle != null) {
+ mBrightnessRampDecreaseMaxIdleMillis = decreaseMaxIdle.intValue();
+ } else {
+ mBrightnessRampDecreaseMaxIdleMillis = mBrightnessRampDecreaseMaxMillis;
+ }
}
private void loadBrightnessRampsFromConfigXml() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 90c7ce7..46ef6c3 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -119,6 +119,7 @@
import android.os.UserManager;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
+import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
@@ -489,6 +490,9 @@
private boolean mBootCompleted = false;
+ // If we would like to keep a particular eye on a package, we can set the package name.
+ private final boolean mExtraDisplayEventLogging;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -572,6 +576,8 @@
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
}
public void setupSchedulerPolicies() {
@@ -2956,9 +2962,10 @@
// Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
@DisplayEvent int event) {
- if (DEBUG) {
+ if (DEBUG || mExtraDisplayEventLogging) {
Slog.d(TAG, "Delivering display event: displayId="
- + displayId + ", event=" + event);
+ + displayId + ", event=" + event
+ + (uids != null ? ", uids=" + uids : ""));
}
// Grab the lock and copy the callbacks.
@@ -3267,12 +3274,12 @@
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+ () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
return displayPowerController;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 40dbabf..83f4df9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -75,6 +75,7 @@
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.utils.SensorUtils;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
@@ -316,10 +317,14 @@
private final Clock mClock;
private final Injector mInjector;
- // Maximum time a ramp animation can take.
+ // Maximum time a ramp animation can take.
private long mBrightnessRampIncreaseMaxTimeMillis;
private long mBrightnessRampDecreaseMaxTimeMillis;
+ // Maximum time a ramp animation can take in idle mode.
+ private long mBrightnessRampIncreaseMaxTimeIdleMillis;
+ private long mBrightnessRampDecreaseMaxTimeIdleMillis;
+
// The pending power request.
// Initially null until the first call to requestPowerState.
@GuardedBy("mLock")
@@ -584,6 +589,8 @@
private boolean mBootCompleted;
+ private final DisplayManagerFlags mFlags;
+
/**
* Creates the display power controller.
*/
@@ -592,7 +599,7 @@
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
- boolean bootCompleted) {
+ boolean bootCompleted, DisplayManagerFlags flags) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -677,7 +684,7 @@
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -749,6 +756,8 @@
mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mBootCompleted = bootCompleted;
+
+ mFlags = flags;
}
private void applyReduceBrightColorsSplineAdjustment() {
@@ -1056,11 +1065,7 @@
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
- if (mScreenBrightnessRampAnimator != null) {
- mScreenBrightnessRampAnimator.setAnimationTimeLimits(
- mBrightnessRampIncreaseMaxTimeMillis,
- mBrightnessRampDecreaseMaxTimeMillis);
- }
+ setAnimatorRampSpeeds(/* isIdleMode= */ false);
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
@@ -1101,9 +1106,8 @@
mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
- mScreenBrightnessRampAnimator.setAnimationTimeLimits(
- mBrightnessRampIncreaseMaxTimeMillis,
- mBrightnessRampDecreaseMaxTimeMillis);
+ setAnimatorRampSpeeds(mAutomaticBrightnessController != null
+ && mAutomaticBrightnessController.isInIdleMode());
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
noteScreenState(mPowerState.getScreenState());
@@ -1322,6 +1326,10 @@
mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
mBrightnessRampIncreaseMaxTimeMillis =
mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
+ mBrightnessRampDecreaseMaxTimeIdleMillis =
+ mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis();
+ mBrightnessRampIncreaseMaxTimeIdleMillis =
+ mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis();
}
private void loadNitsRange(Resources resources) {
@@ -1348,6 +1356,7 @@
} else {
mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
}
+ setAnimatorRampSpeeds(isIdle);
}
Message msg = mHandler.obtainMessage();
@@ -1356,6 +1365,21 @@
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
+ private void setAnimatorRampSpeeds(boolean isIdle) {
+ if (mScreenBrightnessRampAnimator == null) {
+ return;
+ }
+ if (mFlags.isAdaptiveTone1Enabled() && isIdle) {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeIdleMillis,
+ mBrightnessRampDecreaseMaxTimeIdleMillis);
+ } else {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ }
+ }
+
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -1921,8 +1945,25 @@
if (isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
- if (initialRampSkip || hasBrightnessBuckets
- || !isDisplayContentVisible || brightnessIsTemporary) {
+ boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+ || !isDisplayContentVisible || brightnessIsTemporary;
+ if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+ sdrAnimateValue, currentSdrBrightness)) {
+ // Going from HDR to no HDR; visually this should be a "no-op" anyway
+ // as the remaining SDR content's brightness should be holding steady
+ // due to the sdr brightness not shifting
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+ skipAnimation = true;
+ }
+
+ // Going from no HDR to HDR; visually this is a significant scene change
+ // and the animation just prevents advanced clients from doing their own
+ // handling of enter/exit animations if they would like to do such a thing
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+ skipAnimation = true;
+ }
+ }
+ if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
} else {
@@ -2407,7 +2448,7 @@
Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ ", rate=" + rate);
}
- if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+ if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, false)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
String propertyKey = "debug.tracing.screen_brightness";
@@ -3536,8 +3577,7 @@
DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
int displayId, int displayState) {
- return new DisplayPowerState(blanker, colorFade, displayId, displayState,
- new Handler(/*async=*/ true));
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
}
DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 460c351..b0d293a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -77,6 +77,7 @@
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.state.DisplayStateController;
import com.android.server.display.utils.SensorUtils;
@@ -284,10 +285,14 @@
private final Clock mClock;
private final Injector mInjector;
- // Maximum time a ramp animation can take.
+ // Maximum time a ramp animation can take.
private long mBrightnessRampIncreaseMaxTimeMillis;
private long mBrightnessRampDecreaseMaxTimeMillis;
+ // Maximum time a ramp animation can take in idle mode.
+ private long mBrightnessRampIncreaseMaxTimeIdleMillis;
+ private long mBrightnessRampDecreaseMaxTimeIdleMillis;
+
// The pending power request.
// Initially null until the first call to requestPowerState.
@GuardedBy("mLock")
@@ -319,6 +324,8 @@
// Must only be accessed on the handler thread.
private DisplayPowerState mPowerState;
+
+
// The currently active screen on unblocker. This field is non-null whenever
// we are waiting for a callback to release it and unblock the screen.
private ScreenOnUnblocker mPendingScreenOnUnblocker;
@@ -464,6 +471,8 @@
private boolean mBootCompleted;
+ private final DisplayManagerFlags mFlags;
+
/**
* Creates the display power controller.
*/
@@ -472,7 +481,7 @@
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
- boolean bootCompleted) {
+ boolean bootCompleted, DisplayManagerFlags flags) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -539,8 +548,8 @@
modeChangeCallback);
mBrightnessThrottler = createBrightnessThrottlerLocked();
- mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
@@ -614,6 +623,7 @@
R.bool.config_displayBrightnessBucketsInDoze);
mBootCompleted = bootCompleted;
+ mFlags = flags;
}
private void applyReduceBrightColorsSplineAdjustment() {
@@ -861,11 +871,7 @@
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
- if (mScreenBrightnessRampAnimator != null) {
- mScreenBrightnessRampAnimator.setAnimationTimeLimits(
- mBrightnessRampIncreaseMaxTimeMillis,
- mBrightnessRampDecreaseMaxTimeMillis);
- }
+ setAnimatorRampSpeeds(/* isIdleMode= */ false);
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
@@ -907,9 +913,8 @@
mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
- mScreenBrightnessRampAnimator.setAnimationTimeLimits(
- mBrightnessRampIncreaseMaxTimeMillis,
- mBrightnessRampDecreaseMaxTimeMillis);
+ setAnimatorRampSpeeds(mAutomaticBrightnessController != null
+ && mAutomaticBrightnessController.isInIdleMode());
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
noteScreenState(mPowerState.getScreenState());
@@ -1135,6 +1140,10 @@
mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
mBrightnessRampIncreaseMaxTimeMillis =
mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
+ mBrightnessRampDecreaseMaxTimeIdleMillis =
+ mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis();
+ mBrightnessRampIncreaseMaxTimeIdleMillis =
+ mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis();
}
private void loadNitsRange(Resources resources) {
@@ -1161,6 +1170,7 @@
} else {
mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
}
+ setAnimatorRampSpeeds(isIdle);
}
Message msg = mHandler.obtainMessage();
msg.what = MSG_SET_DWBC_STRONG_MODE;
@@ -1168,6 +1178,21 @@
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
+ private void setAnimatorRampSpeeds(boolean isIdle) {
+ if (mScreenBrightnessRampAnimator == null) {
+ return;
+ }
+ if (mFlags.isAdaptiveTone1Enabled() && isIdle) {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeIdleMillis,
+ mBrightnessRampDecreaseMaxTimeIdleMillis);
+ } else {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ }
+ }
+
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -1497,6 +1522,9 @@
// allowed range.
float animateValue = clampScreenBrightness(brightnessState);
+ // custom transition duration
+ float customTransitionRate = -1f;
+
// If there are any HDR layers on the screen, we have a special brightness value that we
// use instead. We still preserve the calculated brightness for Standard Dynamic Range
// (SDR) layers, but the main brightness value will be the one for HDR.
@@ -1511,6 +1539,7 @@
// We want to scale HDR brightness level with the SDR level, we also need to restore
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+ customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
}
final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1519,10 +1548,30 @@
if (BrightnessUtils.isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
- if (initialRampSkip || hasBrightnessBuckets
- || !isDisplayContentVisible || brightnessIsTemporary) {
+ boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+ || !isDisplayContentVisible || brightnessIsTemporary;
+ if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+ sdrAnimateValue, currentSdrBrightness)) {
+ // Going from HDR to no HDR; visually this should be a "no-op" anyway
+ // as the remaining SDR content's brightness should be holding steady
+ // due to the sdr brightness not shifting
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+ skipAnimation = true;
+ }
+
+ // Going from no HDR to HDR; visually this is a significant scene change
+ // and the animation just prevents advanced clients from doing their own
+ // handling of enter/exit animations if they would like to do such a thing
+ if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+ skipAnimation = true;
+ }
+ }
+ if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
+ } else if (customTransitionRate > 0) {
+ animateScreenBrightness(animateValue, sdrAnimateValue,
+ customTransitionRate, /* ignoreAnimationLimits = */true);
} else {
boolean isIncreasing = animateValue > currentBrightness;
final float rampSpeed;
@@ -1992,11 +2041,17 @@
}
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+ animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false);
+ }
+
+ private void animateScreenBrightness(float target, float sdrTarget, float rate,
+ boolean ignoreAnimationLimits) {
if (DEBUG) {
Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ ", rate=" + rate);
}
- if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+ if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate,
+ ignoreAnimationLimits)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
String propertyKey = "debug.tracing.screen_brightness";
@@ -2861,8 +2916,7 @@
DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
int displayId, int displayState) {
- return new DisplayPowerState(blanker, colorFade, displayId, displayState,
- new Handler(/*async=*/ true));
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
}
DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
@@ -2968,6 +3022,14 @@
hbmChangeCallback, hbmMetadata, context);
}
+ BrightnessRangeController getBrightnessRangeController(
+ HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+ DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
+ return new BrightnessRangeController(hbmController,
+ modeChangeCallback, displayDeviceConfig, handler, flags);
+ }
+
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
SensorManager sensorManager, Resources resources) {
return DisplayWhiteBalanceFactory.create(handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 85c6a6d..2c257a1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -74,9 +74,8 @@
private volatile boolean mStopped;
DisplayPowerState(
- DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState,
- Handler handler) {
- mHandler = handler;
+ DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
+ mHandler = new Handler(true /*async*/);
mChoreographer = Choreographer.getInstance();
mBlanker = blanker;
mColorFade = colorFade;
@@ -318,7 +317,6 @@
mStopped = true;
mPhotonicModulator.interrupt();
dismissColorFade();
- stopColorFade();
mCleanListener = null;
mHandler.removeCallbacksAndMessages(null);
}
@@ -378,11 +376,6 @@
}
}
- // Clears up color fade resources.
- private void stopColorFade() {
- if (mColorFade != null) mColorFade.stop();
- }
-
private final Runnable mScreenUpdateRunnable = new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index fcaa957..9439eaa 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -6,5 +6,6 @@
flc@google.com
wilczynskip@google.com
brup@google.com
+petsjonkin@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 52b92c4..5ba042c 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -30,8 +30,15 @@
private final T mObject;
private final FloatProperty<T> mProperty;
+ private final Clock mClock;
+
private float mCurrentValue;
- private float mTargetValue;
+
+ // target in HLG space
+ private float mTargetHlgValue;
+
+ // target in linear space
+ private float mTargetLinearValue;
private float mRate;
private float mAnimationIncreaseMaxTimeSecs;
private float mAnimationDecreaseMaxTimeSecs;
@@ -42,10 +49,14 @@
private boolean mFirstTime = true;
-
RampAnimator(T object, FloatProperty<T> property) {
+ this(object, property, System::nanoTime);
+ }
+
+ RampAnimator(T object, FloatProperty<T> property, Clock clock) {
mObject = object;
mProperty = property;
+ mClock = clock;
}
/**
@@ -61,15 +72,24 @@
/**
* Sets the animation target and the rate of this ramp animator.
- *
+ * Animation rate will be set ignoring maxTime animation limits
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
*
* @param targetLinear The target value.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
+ * @param ignoreAnimationLimits if mAnimationIncreaseMaxTimeSecs and
+ * mAnimationDecreaseMaxTimeSecs should be respected when adjusting
+ * animation speed
* @return True if the target differs from the previous target.
*/
- boolean setAnimationTarget(float targetLinear, float rate) {
+ boolean setAnimationTarget(float targetLinear, float rate, boolean ignoreAnimationLimits) {
+ float maxIncreaseTimeSecs = ignoreAnimationLimits ? 0 : mAnimationIncreaseMaxTimeSecs;
+ float maxDecreaseTimeSecs = ignoreAnimationLimits ? 0 : mAnimationDecreaseMaxTimeSecs;
+ return setAnimationTarget(targetLinear, rate, maxIncreaseTimeSecs, maxDecreaseTimeSecs);
+ }
+ private boolean setAnimationTarget(float targetLinear, float rate,
+ float maxIncreaseTimeSecs, float maxDecreaseTimeSecs) {
// Convert the target from the linear into the HLG space.
final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
@@ -78,7 +98,8 @@
if (mFirstTime || target != mCurrentValue) {
mFirstTime = false;
mRate = 0;
- mTargetValue = target;
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
mCurrentValue = target;
setPropertyValue(target);
mAnimating = false;
@@ -88,12 +109,12 @@
}
// Adjust the rate so that we do not exceed our maximum animation time.
- if (target > mCurrentValue && mAnimationIncreaseMaxTimeSecs > 0.0f
- && ((target - mCurrentValue) / rate) > mAnimationIncreaseMaxTimeSecs) {
- rate = (target - mCurrentValue) / mAnimationIncreaseMaxTimeSecs;
- } else if (target < mCurrentValue && mAnimationDecreaseMaxTimeSecs > 0.0f
- && ((mCurrentValue - target) / rate) > mAnimationDecreaseMaxTimeSecs) {
- rate = (mCurrentValue - target) / mAnimationDecreaseMaxTimeSecs;
+ if (target > mCurrentValue && maxIncreaseTimeSecs > 0.0f
+ && ((target - mCurrentValue) / rate) > maxIncreaseTimeSecs) {
+ rate = (target - mCurrentValue) / maxIncreaseTimeSecs;
+ } else if (target < mCurrentValue && maxDecreaseTimeSecs > 0.0f
+ && ((mCurrentValue - target) / rate) > maxDecreaseTimeSecs) {
+ rate = (mCurrentValue - target) / maxDecreaseTimeSecs;
}
// Adjust the rate based on the closest target.
@@ -105,19 +126,20 @@
// Otherwise, continue at the previous rate.
if (!mAnimating
|| rate > mRate
- || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
- || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+ || (target <= mCurrentValue && mCurrentValue <= mTargetHlgValue)
+ || (mTargetHlgValue <= mCurrentValue && mCurrentValue <= target)) {
mRate = rate;
}
- final boolean changed = (mTargetValue != target);
- mTargetValue = target;
+ final boolean changed = (mTargetHlgValue != target);
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
// Start animating.
if (!mAnimating && target != mCurrentValue) {
mAnimating = true;
mAnimatedValue = mCurrentValue;
- mLastFrameTimeNanos = System.nanoTime();
+ mLastFrameTimeNanos = mClock.nanoTime();
}
return changed;
@@ -135,7 +157,11 @@
* into linear space.
*/
private void setPropertyValue(float val) {
- final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+ // To avoid linearVal inconsistency when converting to HLG and back to linear space
+ // used original target linear value for final animation step
+ float linearVal =
+ val == mTargetHlgValue ? mTargetLinearValue : BrightnessUtils.convertGammaToLinear(
+ val);
mProperty.setValue(mObject, linearVal);
}
@@ -150,13 +176,13 @@
final float scale = ValueAnimator.getDurationScale();
if (scale == 0) {
// Animation off.
- mAnimatedValue = mTargetValue;
+ mAnimatedValue = mTargetHlgValue;
} else {
final float amount = timeDelta * mRate / scale;
- if (mTargetValue > mCurrentValue) {
- mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+ if (mTargetHlgValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetHlgValue);
} else {
- mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetHlgValue);
}
}
final float oldCurrentValue = mCurrentValue;
@@ -164,7 +190,7 @@
if (oldCurrentValue != mCurrentValue) {
setPropertyValue(mCurrentValue);
}
- if (mTargetValue == mCurrentValue) {
+ if (mTargetHlgValue == mCurrentValue) {
mAnimating = false;
}
}
@@ -173,6 +199,13 @@
void onAnimationEnd();
}
+ interface Clock {
+ /**
+ * Returns current system time in nanoseconds.
+ */
+ long nanoTime();
+ }
+
static class DualRampAnimator<T> {
private final Choreographer mChoreographer;
private final RampAnimator<T> mFirst;
@@ -208,11 +241,17 @@
* @param linearFirstTarget The first target value in linear space.
* @param linearSecondTarget The second target value in linear space.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
+ * @param ignoreAnimationLimits if mAnimationIncreaseMaxTimeSecs and
+ * mAnimationDecreaseMaxTimeSecs should be respected
+ * when adjusting animation speed
* @return True if either target differs from the previous target.
*/
- public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
- boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
- animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
+ public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate,
+ boolean ignoreAnimationLimits) {
+ boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate,
+ ignoreAnimationLimits);
+ animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate,
+ ignoreAnimationLimits);
boolean shouldBeAnimating = isAnimating();
if (shouldBeAnimating != mAwaitingCallback) {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6936112..d910e16 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -167,6 +167,8 @@
int width, int height, int densityDpi) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
+ Slog.v(TAG, "Resize VirtualDisplay " + device.mName + " to " + width
+ + " " + height);
device.resizeLocked(width, height, densityDpi);
}
}
@@ -183,6 +185,7 @@
public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
+ Slog.v(TAG, "Update surface for VirtualDisplay " + device.mName);
device.setSurfaceLocked(surface);
}
}
@@ -197,6 +200,7 @@
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
+ Slog.v(TAG, "Release VirtualDisplay " + device.mName);
device.destroyLocked(true);
appToken.unlinkToDeath(device, 0);
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
new file mode 100644
index 0000000..a514136
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.PowerManager;
+
+import com.android.server.display.config.HdrBrightnessData;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+public class HdrClamper {
+
+ private final BrightnessClamperController.ClamperChangeListener mClamperChangeListener;
+
+ private final Handler mHandler;
+
+ private final Runnable mDebouncer;
+
+ @Nullable
+ private HdrBrightnessData mHdrBrightnessData = null;
+
+ private float mAmbientLux = Float.MAX_VALUE;
+
+ private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+
+ // brightness change speed, in units per seconds,
+ private float mTransitionRate = -1f;
+ private float mDesiredTransitionRate = -1f;
+
+ public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+ Handler handler) {
+ mClamperChangeListener = clamperChangeListener;
+ mHandler = handler;
+ mDebouncer = () -> {
+ mTransitionRate = mDesiredTransitionRate;
+ mMaxBrightness = mDesiredMaxBrightness;
+ mClamperChangeListener.onChanged();
+ };
+ }
+
+ // Called in same looper: mHandler.getLooper()
+ public float getMaxBrightness() {
+ return mMaxBrightness;
+ }
+
+ // Called in same looper: mHandler.getLooper()
+ public float getTransitionRate() {
+ return mTransitionRate;
+ }
+
+ /**
+ * Updates brightness cap in response to ambient lux change.
+ * Called by ABC in same looper: mHandler.getLooper()
+ */
+ public void onAmbientLuxChange(float ambientLux) {
+ mAmbientLux = ambientLux;
+ recalculateBrightnessCap(mHdrBrightnessData, ambientLux);
+ }
+
+ /**
+ * Updates brightness cap config.
+ * Called in same looper: mHandler.getLooper()
+ */
+ public void resetHdrConfig(HdrBrightnessData data) {
+ mHdrBrightnessData = data;
+ recalculateBrightnessCap(data, mAmbientLux);
+ }
+
+ /**
+ * Dumps the state of HdrClamper.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("HdrClamper:");
+ pw.println(" mMaxBrightness=" + mMaxBrightness);
+ pw.println(" mDesiredMaxBrightness=" + mDesiredMaxBrightness);
+ pw.println(" mTransitionRate=" + mTransitionRate);
+ pw.println(" mDesiredTransitionRate=" + mDesiredTransitionRate);
+ pw.println(" mHdrBrightnessData=" + (mHdrBrightnessData == null ? "null"
+ : mHdrBrightnessData.toString()));
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ }
+
+ private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux) {
+ if (data == null) {
+ mHandler.removeCallbacks(mDebouncer);
+ return;
+ }
+ float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
+
+ if (mMaxBrightness == expectedMaxBrightness) {
+ mDesiredMaxBrightness = mMaxBrightness;
+ mDesiredTransitionRate = -1f;
+ mTransitionRate = -1f;
+ mHandler.removeCallbacks(mDebouncer);
+ } else if (mDesiredMaxBrightness != expectedMaxBrightness) {
+ mDesiredMaxBrightness = expectedMaxBrightness;
+ long debounceTime;
+ long transitionDuration;
+ if (mDesiredMaxBrightness > mMaxBrightness) {
+ debounceTime = mHdrBrightnessData.mBrightnessIncreaseDebounceMillis;
+ transitionDuration = mHdrBrightnessData.mBrightnessIncreaseDurationMillis;
+ } else {
+ debounceTime = mHdrBrightnessData.mBrightnessDecreaseDebounceMillis;
+ transitionDuration = mHdrBrightnessData.mBrightnessDecreaseDurationMillis;
+ }
+ mDesiredTransitionRate = Math.abs(
+ (mMaxBrightness - mDesiredMaxBrightness) * 1000f / transitionDuration);
+
+ mHandler.removeCallbacks(mDebouncer);
+ mHandler.postDelayed(mDebouncer, debounceTime);
+ }
+ }
+
+ private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
+ float foundAmbientBoundary = Float.MAX_VALUE;
+ float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ for (Map.Entry<Float, Float> brightnessPoint :
+ data.mMaxBrightnessLimits.entrySet()) {
+ float ambientBoundary = brightnessPoint.getKey();
+ // find ambient lux upper boundary closest to current ambient lux
+ if (ambientBoundary > ambientLux && ambientBoundary < foundAmbientBoundary) {
+ foundMaxBrightness = brightnessPoint.getValue();
+ foundAmbientBoundary = ambientBoundary;
+ }
+ }
+ return foundMaxBrightness;
+ }
+}
diff --git a/services/core/java/com/android/server/display/config/HdrBrightnessData.java b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
new file mode 100644
index 0000000..48d671d
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.config;
+
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Brightness config for HDR content
+ */
+public class HdrBrightnessData {
+
+ /**
+ * Lux to brightness map
+ */
+ public final Map<Float, Float> mMaxBrightnessLimits;
+
+ /**
+ * Debounce time for brightness increase
+ */
+ public final long mBrightnessIncreaseDebounceMillis;
+
+ /**
+ * Brightness increase animation duration
+ */
+ public final long mBrightnessIncreaseDurationMillis;
+
+ /**
+ * Debounce time for brightness decrease
+ */
+ public final long mBrightnessDecreaseDebounceMillis;
+
+ /**
+ * Brightness decrease animation duration
+ */
+ public final long mBrightnessDecreaseDurationMillis;
+
+ @VisibleForTesting
+ public HdrBrightnessData(Map<Float, Float> maxBrightnessLimits,
+ long brightnessIncreaseDebounceMillis, long brightnessIncreaseDurationMillis,
+ long brightnessDecreaseDebounceMillis, long brightnessDecreaseDurationMillis) {
+ mMaxBrightnessLimits = maxBrightnessLimits;
+ mBrightnessIncreaseDebounceMillis = brightnessIncreaseDebounceMillis;
+ mBrightnessIncreaseDurationMillis = brightnessIncreaseDurationMillis;
+ mBrightnessDecreaseDebounceMillis = brightnessDecreaseDebounceMillis;
+ mBrightnessDecreaseDurationMillis = brightnessDecreaseDurationMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "HdrBrightnessData {"
+ + "mMaxBrightnessLimits: " + mMaxBrightnessLimits
+ + ", mBrightnessIncreaseDebounceMillis: " + mBrightnessIncreaseDebounceMillis
+ + ", mBrightnessIncreaseDurationMillis: " + mBrightnessIncreaseDurationMillis
+ + ", mBrightnessDecreaseDebounceMillis: " + mBrightnessDecreaseDebounceMillis
+ + ", mBrightnessDecreaseDurationMillis: " + mBrightnessDecreaseDurationMillis
+ + "} ";
+ }
+
+ /**
+ * Loads HdrBrightnessData from DisplayConfiguration
+ */
+ @Nullable
+ public static HdrBrightnessData loadConfig(DisplayConfiguration config) {
+ HdrBrightnessConfig hdrConfig = config.getHdrBrightnessConfig();
+ if (hdrConfig == null) {
+ return null;
+ }
+
+ List<NonNegativeFloatToFloatPoint> points = hdrConfig.getBrightnessMap().getPoint();
+ Map<Float, Float> brightnessLimits = new HashMap<>();
+ for (NonNegativeFloatToFloatPoint point: points) {
+ brightnessLimits.put(point.getFirst().floatValue(), point.getSecond().floatValue());
+ }
+
+ return new HdrBrightnessData(brightnessLimits,
+ hdrConfig.getBrightnessIncreaseDebounceMillis().longValue(),
+ hdrConfig.getBrightnessIncreaseDurationMillis().longValue(),
+ hdrConfig.getBrightnessDecreaseDebounceMillis().longValue(),
+ hdrConfig.getBrightnessDecreaseDurationMillis().longValue());
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/Android.bp b/services/core/java/com/android/server/display/feature/Android.bp
index 27c48ed..a0ead38 100644
--- a/services/core/java/com/android/server/display/feature/Android.bp
+++ b/services/core/java/com/android/server/display/feature/Android.bp
@@ -5,8 +5,3 @@
"*.aconfig",
],
}
-
-java_aconfig_library {
- name: "display_flags_lib",
- aconfig_declarations: "display_flags",
-}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index fddac6d..3f6bf1a 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -30,43 +30,87 @@
public class DisplayManagerFlags {
private static final boolean DEBUG = false;
private static final String TAG = "DisplayManagerFlags";
- private boolean mIsConnectedDisplayManagementEnabled = false;
- private boolean mIsConnectedDisplayManagementEnabledSet = false;
- private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
- // TODO(b/299462337) Remove when the infrastructure is ready.
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
- return true;
- }
- try {
- return flagFunction.get();
- } catch (Throwable ex) {
- if (DEBUG) {
- Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
- }
- return false;
- }
- }
+ private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
+ Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
+ Flags::enableConnectedDisplayManagement);
- // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+ private final FlagState mNbmControllerFlagState = new FlagState(
+ Flags.FLAG_ENABLE_NBM_CONTROLLER,
+ Flags::enableNbmController);
+
+ private final FlagState mHdrClamperFlagState = new FlagState(
+ Flags.FLAG_ENABLE_HDR_CLAMPER,
+ Flags::enableHdrClamper);
+
+ private final FlagState mAdaptiveToneImprovements1 = new FlagState(
+ Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
+ Flags::enableAdaptiveToneImprovements1);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
- if (mIsConnectedDisplayManagementEnabledSet) {
- if (DEBUG) {
- Slog.d(TAG, "isConnectedDisplayManagementEnabled. Recall = "
- + mIsConnectedDisplayManagementEnabled);
+ return mConnectedDisplayManagementFlagState.isEnabled();
+ }
+
+ /** Returns whether hdr clamper is enabled on not*/
+ public boolean isNbmControllerEnabled() {
+ return mNbmControllerFlagState.isEnabled();
+ }
+
+ public boolean isHdrClamperEnabled() {
+ return mHdrClamperFlagState.isEnabled();
+ }
+
+ /**
+ * Returns whether adaptive tone improvements are enabled
+ */
+ public boolean isAdaptiveTone1Enabled() {
+ return mAdaptiveToneImprovements1.isEnabled();
+ }
+
+ private static class FlagState {
+
+ private final String mName;
+
+ private final Supplier<Boolean> mFlagFunction;
+ private boolean mEnabledSet;
+ private boolean mEnabled;
+
+ private FlagState(String name, Supplier<Boolean> flagFunction) {
+ mName = name;
+ mFlagFunction = flagFunction;
+ }
+
+ // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+ private boolean isEnabled() {
+ if (mEnabledSet) {
+ if (DEBUG) {
+ Slog.d(TAG, mName + ": mEnabled. Recall = " + mEnabled);
+ }
+ return mEnabled;
}
- return mIsConnectedDisplayManagementEnabled;
+ mEnabled = flagOrSystemProperty(mFlagFunction, mName);
+ if (DEBUG) {
+ Slog.d(TAG, mName + ": mEnabled. Flag value = " + mEnabled);
+ }
+ mEnabledSet = true;
+ return mEnabled;
}
- mIsConnectedDisplayManagementEnabled =
- flagOrSystemProperty(Flags::enableConnectedDisplayManagement,
- Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT);
- if (DEBUG) {
- Slog.d(TAG, "isConnectedDisplayManagementEnabled. Flag value = "
- + mIsConnectedDisplayManagementEnabled);
+
+ private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+ // TODO(b/299462337) Remove when the infrastructure is ready.
+ if ((Build.IS_ENG || Build.IS_USERDEBUG)
+ && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
+ return true;
+ }
+ try {
+ return flagFunction.get();
+ } catch (Throwable ex) {
+ if (DEBUG) {
+ Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
+ }
+ return false;
+ }
}
- mIsConnectedDisplayManagementEnabledSet = true;
- return mIsConnectedDisplayManagementEnabled;
}
}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 2c3c66e..4d86004 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -9,3 +9,28 @@
bug: "280739508"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_nbm_controller"
+ namespace: "display_manager"
+ description: "Feature flag for Normal Brightness Mode Controller"
+ bug: "299527549"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_hdr_clamper"
+ namespace: "display_manager"
+ description: "Feature flag for HDR Clamper"
+ bug: "295100043"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_adaptive_tone_improvements_1"
+ namespace: "display_manager"
+ description: "Feature flag for Adaptive Tone Improvements"
+ bug: "299550755"
+ is_fixed_read_only: true
+}
+
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index dadcebe..bdd2ab7 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
@@ -104,6 +105,9 @@
votes.remove(priority);
}
}
+ Trace.traceCounter(Trace.TRACE_TAG_POWER,
+ TAG + "." + displayId + ":" + Vote.priorityToString(priority),
+ getMaxPhysicalRefreshRate(vote));
if (mLoggingEnabled) {
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
}
@@ -146,6 +150,15 @@
}
}
+ private int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
+ if (vote == null) {
+ return -1;
+ } else if (vote.refreshRateRanges.physical.max == Float.POSITIVE_INFINITY) {
+ return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
+ }
+ return (int) vote.refreshRateRanges.physical.max;
+ }
+
interface Listener {
void onChanged();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 7045e65..c01bc20 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -868,6 +868,28 @@
}
@ServiceThreadOnly
+ void removeUnusedLocalDevices(ArrayList<HdmiCecLocalDevice> allocatedDevices) {
+ ArrayList<Integer> deviceTypesToRemove = new ArrayList<>();
+ for (int i = 0; i < mLocalDevices.size(); i++) {
+ int deviceType = mLocalDevices.keyAt(i);
+ boolean shouldRemoveLocalDevice = allocatedDevices.stream().noneMatch(
+ localDevice -> localDevice.getDeviceInfo() != null
+ && localDevice.getDeviceInfo().getDeviceType() == deviceType);
+ if (shouldRemoveLocalDevice) {
+ deviceTypesToRemove.add(deviceType);
+ }
+ }
+ for (Integer deviceType : deviceTypesToRemove) {
+ mLocalDevices.remove(deviceType);
+ }
+ }
+
+ @ServiceThreadOnly
+ void removeLocalDeviceWithType(int deviceType) {
+ mLocalDevices.remove(deviceType);
+ }
+
+ @ServiceThreadOnly
public void clearDeviceList() {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 232bc47..429db5e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1313,9 +1313,6 @@
localDevice.init();
localDevices.add(localDevice);
}
- // It's now safe to flush existing local devices from mCecController since they were
- // already moved to 'localDevices'.
- clearCecLocalDevices();
mHdmiCecNetwork.clearDeviceList();
allocateLogicalAddress(localDevices, initiatedBy);
}
@@ -1344,6 +1341,7 @@
if (logicalAddress == Constants.ADDR_UNREGISTERED) {
Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType
+ "]");
+ mHdmiCecNetwork.removeLocalDeviceWithType(deviceType);
} else {
// Set POWER_STATUS_ON to all local devices because they share
// lifetime
@@ -1352,6 +1350,8 @@
deviceType,
HdmiControlManager.POWER_STATUS_ON, getCecVersion());
localDevice.setDeviceInfo(deviceInfo);
+ // If a local device of the same type already exists, it will be
+ // replaced.
mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
mHdmiCecNetwork.addCecDevice(localDevice.getDeviceInfo());
mCecController.addLogicalAddress(logicalAddress);
@@ -1367,6 +1367,10 @@
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
+ // We remove local devices here, instead of before the start of
+ // address allocation, to prevent multiple local devices of the
+ // same type from existing simultaneously.
+ mHdmiCecNetwork.removeUnusedLocalDevices(allocatedDevices);
mAddressAllocated = true;
notifyAddressAllocated(allocatedDevices, initiatedBy);
// Reinvoke the saved display status callback once the local
@@ -1386,9 +1390,19 @@
}
}
+ /**
+ * Notifies local devices that address allocation finished.
+ * @param devices - list of local devices allocated.
+ * @param initiatedBy - reason for the address allocation.
+ */
+ @VisibleForTesting
@ServiceThreadOnly
- private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
+ public void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
assertRunOnServiceThread();
+ if (devices == null || devices.isEmpty()) {
+ Slog.w(TAG, "No local device to notify.");
+ return;
+ }
List<HdmiCecMessage> bufferedMessages = mCecMessageBuffer.getBuffer();
for (HdmiCecLocalDevice device : devices) {
int address = device.getDeviceInfo().getLogicalAddress();
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index a2c8748..2ede56d 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -62,10 +62,10 @@
mWindowHandle.ownerUid = uid;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
- mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY;
+ mWindowHandle.inputConfig =
+ InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 62660c4..6b399de 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -95,6 +95,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -682,6 +683,12 @@
return mNative.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
+ @Override // Binder call
+ public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+ Objects.requireNonNull(layoutDescriptor, "layoutDescriptor must not be null");
+ return mKeyboardLayoutManager.getKeyCharacterMap(layoutDescriptor);
+ }
+
/**
* Transfer the current touch gesture to the provided window.
*
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index a5162c0..0eb620f 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -63,6 +63,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
@@ -430,6 +431,23 @@
return result[0];
}
+ @AnyThread
+ public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+ final String[] overlay = new String[1];
+ visitKeyboardLayout(layoutDescriptor,
+ (resources, keyboardLayoutResId, layout) -> {
+ try (InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
+ overlay[0] = Streams.readFully(stream);
+ } catch (IOException | Resources.NotFoundException ignored) {
+ }
+ });
+ if (TextUtils.isEmpty(overlay[0])) {
+ return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ }
+ return KeyCharacterMap.load(layoutDescriptor, overlay[0]);
+ }
+
private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
final PackageManager pm = mContext.getPackageManager();
Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index dbbbed3..7726f40 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -57,13 +57,13 @@
InputConfig.NOT_FOCUSABLE
| InputConfig.NOT_TOUCHABLE
| InputConfig.SPY
- | InputConfig.INTERCEPTS_STYLUS;
+ | InputConfig.INTERCEPTS_STYLUS
+ | InputConfig.TRUSTED_OVERLAY;
// Configure the surface to receive stylus events across the entire display.
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 699e9c8..d3ad6c4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -116,7 +116,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -124,7 +123,6 @@
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
@@ -2989,9 +2987,13 @@
ConcurrentUtils.waitForFutureNoInterrupt(mImeDrawsImeNavBarResLazyInitFuture,
"Waiting for the lazy init of mImeDrawsImeNavBarRes");
}
+ // Whether the current display has a navigation bar. When this is false (e.g. emulator),
+ // the IME should not draw the IME navigation bar.
+ final boolean hasNavigationBar = mWindowManagerInternal
+ .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
+ ? mCurTokenDisplayId : DEFAULT_DISPLAY);
final boolean canImeDrawsImeNavBar =
- mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get()
- && hasNavigationBarOnCurrentDisplay();
+ mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
@@ -2999,21 +3001,6 @@
? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
}
- /**
- * Whether the current display has a navigation bar. When this is {@code false} (e.g. emulator),
- * the IME should <em>not</em> draw the IME navigation bar.
- */
- @GuardedBy("ImfLock.class")
- private boolean hasNavigationBarOnCurrentDisplay() {
- final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- try {
- return wm.hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
- ? mCurTokenDisplayId : DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
@GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
@@ -3316,6 +3303,10 @@
}
// Changing to a different IME.
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
+ curMethod.removeStylusHandwritingWindow();
+ }
final long ident = Binder.clearCallingIdentity();
try {
// Set a subtype to this input method.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index a7c986d..ec858ee2 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -513,6 +513,7 @@
List<String> apkFiles =
filesList
.map(path -> path.toAbsolutePath().toString())
+ .filter(str -> str.endsWith(".apk"))
.collect(Collectors.toList());
sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 0cfdaf2..10b6052 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -195,8 +195,12 @@
mApplicationKeyStorage = applicationKeyStorage;
mTestCertHelper = testOnlyInsecureCertificateHelper;
mCleanupManager = cleanupManager;
- // Clears data for removed users.
- mCleanupManager.verifyKnownUsers();
+ try {
+ // Clears data for removed users.
+ mCleanupManager.verifyKnownUsers();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to verify known users", e);
+ }
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index e31a7fc..1980403 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -508,7 +508,11 @@
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
if (device != null) {
- addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
+ if (DEBUG) {
+ Log.d(TAG, "Setting active a2dp devices. device=" + device);
+ }
+
+ addActiveDevices(device);
}
notifyBluetoothRoutesUpdated();
break;
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 55511ad..66cafab 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -117,24 +117,11 @@
/**
* Gets the map of key code -> {@link KeyEventType} that have been overridden.
- * <p>
- * The list of valid key codes are the following:
- * <ul>
- * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
- * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
- * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
- * <li> {@link KeyEvent#KEYCODE_MUTE}
- * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
- * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
- * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
- * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
- * <li> {@link KeyEvent#KEYCODE_VOLUME_UP}
- * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN}
- * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE}
- * </ul>
- * @see {@link KeyEvent#isMediaSessionKey(int)}
+ *
+ * <p>For the list of relevant key codes, see {@link KeyEvent#isMediaSessionKey(int)}.
*/
- @KeyEventType Map<Integer, Integer> getOverriddenKeyEvents() {
+ @KeyEventType
+ Map<Integer, Integer> getOverriddenKeyEvents() {
return mOverriddenKeyEvents;
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 69e4a5b..13d1662 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -932,6 +932,7 @@
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
+ Slog.v(TAG, "Start the token instance " + this);
// Cache result of calling into ActivityManagerService outside of the lock, to prevent
// deadlock with WindowManagerService.
final boolean hasFGS = mActivityManagerInternal.hasRunningForegroundService(
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
new file mode 100644
index 0000000..75a0cf5
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.Notification.FLAG_INSISTENT;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+
+import android.annotation.IntDef;
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.IRingtonePlayer;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.EventLogTags;
+import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * NotificationManagerService helper for handling notification attention effects:
+ * make noise, vibrate, or flash the LED.
+ * @hide
+ */
+public final class NotificationAttentionHelper {
+ static final String TAG = "NotifAttentionHelper";
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
+ "debug.notification.interruptiveness", false);
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final TelephonyManager mTelephonyManager;
+ private final NotificationManagerPrivate mNMP;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private AccessibilityManager mAccessibilityManager;
+ private KeyguardManager mKeyguardManager;
+ private AudioManager mAudioManager;
+ private final LightsManager mLightsManager;
+ private final NotificationUsageStats mUsageStats;
+ private final ZenModeHelper mZenModeHelper;
+
+ private VibratorHelper mVibratorHelper;
+ // The last key in this list owns the hardware.
+ ArrayList<String> mLights = new ArrayList<>();
+ private LogicalLight mNotificationLight;
+ private LogicalLight mAttentionLight;
+
+ private final boolean mUseAttentionLight;
+ boolean mHasLight = true;
+
+ private final SettingsObserver mSettingsObserver;
+
+ private boolean mIsAutomotive;
+ private boolean mNotificationEffectsEnabledForAutomotive;
+ private boolean mDisableNotificationEffects;
+ private int mCallState;
+ private String mSoundNotificationKey;
+ private String mVibrateNotificationKey;
+ private boolean mSystemReady;
+ private boolean mInCallStateOffHook = false;
+ private boolean mScreenOn = true;
+ private boolean mUserPresent = false;
+ boolean mNotificationPulseEnabled;
+ private final Uri mInCallNotificationUri;
+ private final AudioAttributes mInCallNotificationAudioAttributes;
+ private final float mInCallNotificationVolume;
+ private Binder mCallNotificationToken = null;
+
+
+ public NotificationAttentionHelper(Context context, LightsManager lightsManager,
+ AccessibilityManager accessibilityManager, PackageManager packageManager,
+ NotificationUsageStats usageStats,
+ NotificationManagerPrivate notificationManagerPrivate,
+ ZenModeHelper zenModeHelper, SystemUiSystemPropertiesFlags.FlagResolver flagResolver) {
+ mContext = context;
+ mPackageManager = packageManager;
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mAccessibilityManager = accessibilityManager;
+ mLightsManager = lightsManager;
+ mNMP = notificationManagerPrivate;
+ mUsageStats = usageStats;
+ mZenModeHelper = zenModeHelper;
+ mFlagResolver = flagResolver;
+
+ mVibratorHelper = new VibratorHelper(context);
+
+ mNotificationLight = mLightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
+ mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+
+ Resources resources = context.getResources();
+ mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+ mHasLight =
+ resources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
+
+ // Don't start allowing notifications until the setup wizard has run once.
+ // After that, including subsequent boots, init with notifications turned on.
+ // This works on the first boot because the setup wizard will toggle this
+ // flag at least once and we'll go back to 0 after that.
+ if (Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ mDisableNotificationEffects = true;
+ }
+
+ mInCallNotificationUri = Uri.parse(
+ "file://" + resources.getString(R.string.config_inCallNotificationSound));
+ mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ .build();
+ mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
+
+ mSettingsObserver = new SettingsObserver();
+ }
+
+ public void onSystemReady() {
+ mSystemReady = true;
+
+ mIsAutomotive = mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
+ mNotificationEffectsEnabledForAutomotive = mContext.getResources().getBoolean(
+ R.bool.config_enableServerNotificationEffectsForAutomotive);
+
+ mAudioManager = mContext.getSystemService(AudioManager.class);
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+
+ registerBroadcastListeners();
+ }
+
+ private void registerBroadcastListeners() {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ mTelephonyManager.listen(new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DEBUG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+ mCallState = state;
+ }
+ }, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
+
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver,
+ UserHandle.USER_ALL);
+ }
+
+ @VisibleForTesting
+ /**
+ * Determine whether this notification should attempt to make noise, vibrate, or flash the LED
+ * @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) |
+ * (polite_attenuated ? 8 : 0) | (polite_muted ? 16 : 0)
+ */
+ int buzzBeepBlinkLocked(NotificationRecord record, Signals signals) {
+ if (mIsAutomotive && !mNotificationEffectsEnabledForAutomotive) {
+ return 0;
+ }
+ boolean buzz = false;
+ boolean beep = false;
+ boolean blink = false;
+
+ final String key = record.getKey();
+
+ if (DEBUG) {
+ Log.d(TAG, "buzzBeepBlinkLocked " + record);
+ }
+
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean aboveThreshold =
+ mIsAutomotive
+ ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
+ : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
+ // Remember if this notification already owns the notification channels.
+ boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
+ boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
+ // These are set inside the conditional if the notification is allowed to make noise.
+ boolean hasValidVibrate = false;
+ boolean hasValidSound = false;
+ boolean sentAccessibilityEvent = false;
+
+ // If the notification will appear in the status bar, it should send an accessibility event
+ final boolean suppressedByDnd = record.isIntercepted()
+ && (record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
+ if (!record.isUpdate
+ && record.getImportance() > IMPORTANCE_MIN
+ && !suppressedByDnd
+ && isNotificationForCurrentUser(record, signals)) {
+ sendAccessibilityEvent(record);
+ sentAccessibilityEvent = true;
+ }
+
+ if (aboveThreshold && isNotificationForCurrentUser(record, signals)) {
+ if (mSystemReady && mAudioManager != null) {
+ Uri soundUri = record.getSound();
+ hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
+ VibrationEffect vibration = record.getVibration();
+ // Demote sound to vibration if vibration missing & phone in vibration mode.
+ if (vibration == null
+ && hasValidSound
+ && (mAudioManager.getRingerModeInternal()
+ == AudioManager.RINGER_MODE_VIBRATE)
+ && mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
+ boolean insistent = (record.getFlags() & Notification.FLAG_INSISTENT) != 0;
+ vibration = mVibratorHelper.createFallbackVibration(insistent);
+ }
+ hasValidVibrate = vibration != null;
+ boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
+ if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) {
+ if (!sentAccessibilityEvent) {
+ sendAccessibilityEvent(record);
+ sentAccessibilityEvent = true;
+ }
+ if (DEBUG) Slog.v(TAG, "Interrupting!");
+ boolean isInsistentUpdate = isInsistentUpdate(record);
+ if (hasValidSound) {
+ if (isInsistentUpdate) {
+ // don't reset insistent sound, it's jarring
+ beep = true;
+ } else {
+ if (isInCall()) {
+ playInCallNotification();
+ beep = true;
+ } else {
+ beep = playSound(record, soundUri);
+ }
+ if (beep) {
+ mSoundNotificationKey = key;
+ }
+ }
+ }
+
+ final boolean ringerModeSilent =
+ mAudioManager.getRingerModeInternal()
+ == AudioManager.RINGER_MODE_SILENT;
+ if (!isInCall() && hasValidVibrate && !ringerModeSilent) {
+ if (isInsistentUpdate) {
+ buzz = true;
+ } else {
+ buzz = playVibration(record, vibration, hasValidSound);
+ if (buzz) {
+ mVibrateNotificationKey = key;
+ }
+ }
+ }
+
+ // Try to start flash notification event whenever an audible and non-suppressed
+ // notification is received
+ mAccessibilityManager.startFlashNotificationEvent(mContext,
+ AccessibilityManager.FLASH_REASON_NOTIFICATION,
+ record.getSbn().getPackageName());
+
+ } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
+ hasValidSound = false;
+ }
+ }
+ }
+ // If a notification is updated to remove the actively playing sound or vibrate,
+ // cancel that feedback now
+ if (wasBeep && !hasValidSound) {
+ clearSoundLocked();
+ }
+ if (wasBuzz && !hasValidVibrate) {
+ clearVibrateLocked();
+ }
+
+ // light
+ // release the light
+ boolean wasShowLights = mLights.remove(key);
+ if (canShowLightsLocked(record, signals, aboveThreshold)) {
+ mLights.add(key);
+ updateLightsLocked();
+ if (mUseAttentionLight && mAttentionLight != null) {
+ mAttentionLight.pulse();
+ }
+ blink = true;
+ } else if (wasShowLights) {
+ updateLightsLocked();
+ }
+ final int buzzBeepBlink =
+ (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
+ if (buzzBeepBlink > 0) {
+ // Ignore summary updates because we don't display most of the information.
+ if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: summary");
+ }
+ } else if (record.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: bubble");
+ }
+ } else {
+ record.setInterruptive(true);
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is interruptive: alerted");
+ }
+ }
+ MetricsLogger.action(record.getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_ALERT)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .setSubtype(buzzBeepBlink));
+ EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+ }
+ record.setAudiblyAlerted(buzz || beep);
+
+ return buzzBeepBlink;
+ }
+
+ boolean isInsistentUpdate(final NotificationRecord record) {
+ return (Objects.equals(record.getKey(), mSoundNotificationKey)
+ || Objects.equals(record.getKey(), mVibrateNotificationKey))
+ && isCurrentlyInsistent();
+ }
+
+ boolean isCurrentlyInsistent() {
+ return isLoopingRingtoneNotification(mNMP.getNotificationByKey(mSoundNotificationKey))
+ || isLoopingRingtoneNotification(
+ mNMP.getNotificationByKey(mVibrateNotificationKey));
+ }
+
+ boolean shouldMuteNotificationLocked(final NotificationRecord record, final Signals signals) {
+ // Suppressed because it's a silent update
+ final Notification notification = record.getNotification();
+ if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
+ return true;
+ }
+
+ // Suppressed because a user manually unsnoozed something (or similar)
+ if (record.shouldPostSilently()) {
+ return true;
+ }
+
+ // muted by listener
+ final String disableEffects = disableNotificationEffects(record, signals.listenerHints);
+ if (disableEffects != null) {
+ ZenLog.traceDisableEffects(record, disableEffects);
+ return true;
+ }
+
+ // suppressed due to DND
+ if (record.isIntercepted()) {
+ return true;
+ }
+
+ // Suppressed because another notification in its group handles alerting
+ if (record.getSbn().isGroup()) {
+ if (notification.suppressAlertingDueToGrouping()) {
+ return true;
+ }
+ }
+
+ // Suppressed for being too recently noisy
+ final String pkg = record.getSbn().getPackageName();
+ if (mUsageStats.isAlertRateLimited(pkg)) {
+ Slog.e(TAG, "Muting recently noisy " + record.getKey());
+ return true;
+ }
+
+ // A different looping ringtone, such as an incoming call is playing
+ if (isCurrentlyInsistent() && !isInsistentUpdate(record)) {
+ return true;
+ }
+
+ // Suppressed since it's a non-interruptive update to a bubble-suppressed notification
+ final boolean isBubbleOrOverflowed = record.canBubble() && (record.isFlagBubbleRemoved()
+ || record.getNotification().isBubbleNotification());
+ if (record.isUpdate && !record.isInterruptive() && isBubbleOrOverflowed
+ && record.getNotification().getBubbleMetadata() != null) {
+ if (record.getNotification().getBubbleMetadata().isNotificationSuppressed()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
+ if (playingRecord != null) {
+ if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE
+ && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean playSound(final NotificationRecord record, Uri soundUri) {
+ boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
+ // play notifications if there is no user of exclusive audio focus
+ // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+ // VIBRATE ringer mode)
+ if (!mAudioManager.isAudioFocusExclusive()
+ && (mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (DEBUG) {
+ Slog.v(TAG, "Playing sound " + soundUri + " with attributes "
+ + record.getAudioAttributes());
+ }
+ player.playAsync(soundUri, record.getSbn().getUser(), looping,
+ record.getAudioAttributes());
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed playSound: " + e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return false;
+ }
+
+ private boolean playVibration(final NotificationRecord record, final VibrationEffect effect,
+ boolean delayVibForSound) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (delayVibForSound) {
+ new Thread(() -> {
+ // delay the vibration by the same amount as the notification sound
+ final int waitMs = mAudioManager.getFocusRampTimeMs(
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ record.getAudioAttributes());
+ if (DEBUG) {
+ Slog.v(TAG, "Delaying vibration for notification "
+ + record.getKey() + " by " + waitMs + "ms");
+ }
+ try {
+ Thread.sleep(waitMs);
+ } catch (InterruptedException e) { }
+ // Notifications might be canceled before it actually vibrates due to waitMs,
+ // so need to check that the notification is still valid for vibrate.
+ if (mNMP.getNotificationByKey(record.getKey()) != null) {
+ if (record.getKey().equals(mVibrateNotificationKey)) {
+ vibrate(record, effect, true);
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "No vibration for notification "
+ + record.getKey() + ": a new notification is "
+ + "vibrating, or effects were cleared while waiting");
+ }
+ }
+ } else {
+ Slog.w(TAG, "No vibration for canceled notification "
+ + record.getKey());
+ }
+ }).start();
+ } else {
+ vibrate(record, effect, false);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void vibrate(NotificationRecord record, VibrationEffect effect, boolean delayed) {
+ // We need to vibrate as "android" so we can breakthrough DND. VibratorManagerService
+ // doesn't have a concept of vibrating on an app's behalf, so add the app information
+ // to the reason so we can still debug from bugreports
+ String reason = "Notification (" + record.getSbn().getOpPkg() + " "
+ + record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
+ mVibratorHelper.vibrate(effect, record.getAudioAttributes(), reason);
+ }
+
+ void playInCallNotification() {
+ // TODO: Should we apply politeness to mInCallNotificationVolume ?
+ final ContentResolver cr = mContext.getContentResolver();
+ if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
+ && Settings.Secure.getIntForUser(cr,
+ Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1, cr.getUserId()) != 0) {
+ new Thread() {
+ @Override
+ public void run() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (mCallNotificationToken != null) {
+ player.stop(mCallNotificationToken);
+ }
+ mCallNotificationToken = new Binder();
+ player.play(mCallNotificationToken, mInCallNotificationUri,
+ mInCallNotificationAudioAttributes,
+ mInCallNotificationVolume, false);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed playInCallNotification: " + e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }.start();
+ }
+ }
+
+ void clearSoundLocked() {
+ mSoundNotificationKey = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ player.stopAsync();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed clearSoundLocked: " + e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void clearVibrateLocked() {
+ mVibrateNotificationKey = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mVibratorHelper.cancelVibration();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void clearLightsLocked() {
+ // light
+ mLights.clear();
+ updateLightsLocked();
+ }
+
+ public void clearEffectsLocked(String key) {
+ if (key.equals(mSoundNotificationKey)) {
+ clearSoundLocked();
+ }
+ if (key.equals(mVibrateNotificationKey)) {
+ clearVibrateLocked();
+ }
+ boolean removed = mLights.remove(key);
+ if (removed) {
+ updateLightsLocked();
+ }
+ }
+
+ public void clearAttentionEffects() {
+ clearSoundLocked();
+ clearVibrateLocked();
+ clearLightsLocked();
+ }
+
+ void updateLightsLocked() {
+ if (mNotificationLight == null) {
+ return;
+ }
+
+ // handle notification lights
+ NotificationRecord ledNotification = null;
+ while (ledNotification == null && !mLights.isEmpty()) {
+ final String owner = mLights.get(mLights.size() - 1);
+ ledNotification = mNMP.getNotificationByKey(owner);
+ if (ledNotification == null) {
+ Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
+ mLights.remove(owner);
+ }
+ }
+
+ // Don't flash while we are in a call or screen is on
+ if (ledNotification == null || isInCall() || mScreenOn) {
+ mNotificationLight.turnOff();
+ } else {
+ NotificationRecord.Light light = ledNotification.getLight();
+ if (light != null && mNotificationPulseEnabled) {
+ // pulse repeatedly
+ mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,
+ light.onMs, light.offMs);
+ }
+ }
+ }
+
+ boolean canShowLightsLocked(final NotificationRecord record, final Signals signals,
+ boolean aboveThreshold) {
+ // device lacks light
+ if (!mHasLight) {
+ return false;
+ }
+ // user turned lights off globally
+ if (!mNotificationPulseEnabled) {
+ return false;
+ }
+ // the notification/channel has no light
+ if (record.getLight() == null) {
+ return false;
+ }
+ // unimportant notification
+ if (!aboveThreshold) {
+ return false;
+ }
+ // suppressed due to DND
+ if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
+ return false;
+ }
+ // Suppressed because it's a silent update
+ final Notification notification = record.getNotification();
+ if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
+ return false;
+ }
+ // Suppressed because another notification in its group handles alerting
+ if (record.getSbn().isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+ return false;
+ }
+ // not if in call
+ if (isInCall()) {
+ return false;
+ }
+ // check current user
+ if (!isNotificationForCurrentUser(record, signals)) {
+ return false;
+ }
+ // Light, but only when the screen is off
+ return true;
+ }
+
+ private String disableNotificationEffects(NotificationRecord record, int listenerHints) {
+ if (mDisableNotificationEffects) {
+ return "booleanState";
+ }
+
+ if ((listenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
+ return "listenerHints";
+ }
+ if (record != null && record.getAudioAttributes() != null) {
+ if ((listenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+ if (record.getAudioAttributes().getUsage()
+ != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+ return "listenerNoti";
+ }
+ }
+ if ((listenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+ if (record.getAudioAttributes().getUsage()
+ == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+ return "listenerCall";
+ }
+ }
+ }
+ if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
+ return "callState";
+ }
+
+ return null;
+ }
+
+ public void updateDisableNotificationEffectsLocked(int status) {
+ mDisableNotificationEffects =
+ (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ //if (disableNotificationEffects(null) != null) {
+ if (mDisableNotificationEffects) {
+ // cancel whatever is going on
+ clearAttentionEffects();
+ }
+ }
+
+ private boolean isInCall() {
+ if (mInCallStateOffHook) {
+ return true;
+ }
+ int audioMode = mAudioManager.getMode();
+ if (audioMode == AudioManager.MODE_IN_CALL
+ || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String callStateToString(int state) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
+ case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
+ case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
+ default: return "CALL_STATE_UNKNOWN_" + state;
+ }
+ }
+
+ private boolean isNotificationForCurrentUser(final NotificationRecord record,
+ final Signals signals) {
+ final int currentUser;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ currentUser = ActivityManager.getCurrentUser();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return (record.getUserId() == UserHandle.USER_ALL || record.getUserId() == currentUser
+ || signals.isCurrentProfile);
+ }
+
+ void sendAccessibilityEvent(NotificationRecord record) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+
+ final Notification notification = record.getNotification();
+ final CharSequence packageName = record.getSbn().getPackageName();
+ final AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setPackageName(packageName);
+ event.setClassName(Notification.class.getName());
+ final int visibilityOverride = record.getPackageVisibilityOverride();
+ final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+ ? notification.visibility : visibilityOverride;
+ final int userId = record.getUser().getIdentifier();
+ final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+ if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+ // Emit the public version if we're on the lockscreen and this notification isn't
+ // publicly visible.
+ event.setParcelableData(notification.publicVersion);
+ } else {
+ event.setParcelableData(notification);
+ }
+ final CharSequence tickerText = notification.tickerText;
+ if (!TextUtils.isEmpty(tickerText)) {
+ event.getText().add(tickerText);
+ }
+
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
+ pw.println("\n Notification attention state:");
+ pw.print(prefix);
+ pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
+ pw.print(prefix);
+ pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
+ pw.print(prefix);
+ pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
+ pw.print(prefix);
+ pw.println(" mCallState=" + callStateToString(mCallState));
+ pw.print(prefix);
+ pw.println(" mSystemReady=" + mSystemReady);
+ pw.print(prefix);
+ pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
+
+ int N = mLights.size();
+ if (N > 0) {
+ pw.print(prefix);
+ pw.println(" Lights List:");
+ for (int i=0; i<N; i++) {
+ if (i == N - 1) {
+ pw.print(" > ");
+ } else {
+ pw.print(" ");
+ }
+ pw.println(mLights.get(i));
+ }
+ pw.println(" ");
+ }
+
+ }
+
+ // External signals set from NMS
+ public static class Signals {
+ private final boolean isCurrentProfile;
+ private final int listenerHints;
+
+ public Signals(boolean isCurrentProfile, int listenerHints) {
+ this.isCurrentProfile = isCurrentProfile;
+ this.listenerHints = listenerHints;
+ }
+ }
+
+ //====================== Observers =============================
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ // Keep track of screen on/off state, but do not turn off the notification light
+ // until user passes through the lock screen or views the notification.
+ mScreenOn = true;
+ updateLightsLocked();
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ mScreenOn = false;
+ mUserPresent = false;
+ updateLightsLocked();
+ } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
+ mInCallStateOffHook = TelephonyManager.EXTRA_STATE_OFFHOOK
+ .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
+ updateLightsLocked();
+ } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
+ mUserPresent = true;
+ // turn off LED when user passes through lock screen
+ if (mNotificationLight != null) {
+ mNotificationLight.turnOff();
+ }
+ }
+ }
+ };
+
+ private final class SettingsObserver extends ContentObserver {
+
+ private static final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE);
+ public SettingsObserver() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
+ boolean pulseEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0,
+ UserHandle.USER_CURRENT)
+ != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateLightsLocked();
+ }
+ }
+ }
+ }
+
+
+ //TODO: cleanup most (all?) of these
+ //======================= FOR TESTS =====================
+ @VisibleForTesting
+ void setIsAutomotive(boolean isAutomotive) {
+ mIsAutomotive = isAutomotive;
+ }
+
+ @VisibleForTesting
+ void setNotificationEffectsEnabledForAutomotive(boolean isEnabled) {
+ mNotificationEffectsEnabledForAutomotive = isEnabled;
+ }
+
+ @VisibleForTesting
+ void setSystemReady(boolean systemReady) {
+ mSystemReady = systemReady;
+ }
+
+ @VisibleForTesting
+ void setKeyguardManager(KeyguardManager keyguardManager) {
+ mKeyguardManager = keyguardManager;
+ }
+
+ @VisibleForTesting
+ void setAccessibilityManager(AccessibilityManager am) {
+ mAccessibilityManager = am;
+ }
+
+ @VisibleForTesting
+ VibratorHelper getVibratorHelper() {
+ return mVibratorHelper;
+ }
+
+ @VisibleForTesting
+ void setVibratorHelper(VibratorHelper helper) {
+ mVibratorHelper = helper;
+ }
+
+ @VisibleForTesting
+ void setScreenOn(boolean on) {
+ mScreenOn = on;
+ }
+
+ @VisibleForTesting
+ void setLights(LogicalLight light) {
+ mNotificationLight = light;
+ mAttentionLight = light;
+ mNotificationPulseEnabled = true;
+ mHasLight = true;
+ }
+
+ @VisibleForTesting
+ void setAudioManager(AudioManager audioManager) {
+ mAudioManager = audioManager;
+ }
+
+ @VisibleForTesting
+ void setInCallStateOffHook(boolean inCallStateOffHook) {
+ mInCallStateOffHook = inCallStateOffHook;
+ }
+
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerPrivate.java b/services/core/java/com/android/server/notification/NotificationManagerPrivate.java
new file mode 100644
index 0000000..2cc63eb
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationManagerPrivate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface that allows components (helpers) to access NotificationRecords
+ * without an explicit reference to NotificationManagerService.
+ */
+interface NotificationManagerPrivate {
+ @Nullable
+ NotificationRecord getNotificationByKey(String key);
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6a5b9d8..802dfb1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -287,6 +287,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.MetricsLogger;
@@ -315,6 +316,7 @@
import com.android.server.job.JobSchedulerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
+import com.android.server.notification.Flags;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.notification.toast.CustomToastRecord;
@@ -684,6 +686,7 @@
private boolean mIsAutomotive;
private boolean mNotificationEffectsEnabledForAutomotive;
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener;
+ protected NotificationAttentionHelper mAttentionHelper;
private int mWarnRemoteViewsSizeBytes;
private int mStripRemoteViewsSizeBytes;
@@ -1167,12 +1170,16 @@
@Override
public void onSetDisabled(int status) {
synchronized (mNotificationLock) {
- mDisableNotificationEffects =
- (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
- if (disableNotificationEffects(null) != null) {
- // cancel whatever's going on
- clearSoundLocked();
- clearVibrateLocked();
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.updateDisableNotificationEffectsLocked(status);
+ } else {
+ mDisableNotificationEffects =
+ (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ if (disableNotificationEffects(null) != null) {
+ // cancel whatever's going on
+ clearSoundLocked();
+ clearVibrateLocked();
+ }
}
}
}
@@ -1309,9 +1316,13 @@
public void clearEffects() {
synchronized (mNotificationLock) {
if (DBG) Slog.d(TAG, "clearEffects");
- clearSoundLocked();
- clearVibrateLocked();
- clearLightsLocked();
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.clearAttentionEffects();
+ } else {
+ clearSoundLocked();
+ clearVibrateLocked();
+ clearLightsLocked();
+ }
}
}
@@ -1534,7 +1545,12 @@
int changedFlags = data.getFlags() ^ flags;
if ((changedFlags & FLAG_SUPPRESS_NOTIFICATION) != 0) {
// Suppress notification flag changed, clear any effects
- clearEffectsLocked(key);
+ if (mFlagResolver.isEnabled(
+ NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.clearEffectsLocked(key);
+ } else {
+ clearEffectsLocked(key);
+ }
}
data.setFlags(flags);
// Shouldn't alert again just because of a flag change.
@@ -1626,6 +1642,12 @@
};
+ NotificationManagerPrivate mNotificationManagerPrivate = key -> {
+ synchronized (mNotificationLock) {
+ return mNotificationsByKey.get(key);
+ }
+ };
+
@VisibleForTesting
void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
// If the newly visible notification has smart suggestions
@@ -1873,19 +1895,28 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)) {
- // Keep track of screen on/off state, but do not turn off the notification light
- // until user passes through the lock screen or views the notification.
- mScreenOn = true;
- updateNotificationPulse();
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- mScreenOn = false;
- updateNotificationPulse();
- } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
- mInCallStateOffHook = TelephonyManager.EXTRA_STATE_OFFHOOK
+ if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ // Keep track of screen on/off state, but do not turn off the notification light
+ // until user passes through the lock screen or views the notification.
+ mScreenOn = true;
+ updateNotificationPulse();
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ mScreenOn = false;
+ updateNotificationPulse();
+ } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
+ mInCallStateOffHook = TelephonyManager.EXTRA_STATE_OFFHOOK
.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
- updateNotificationPulse();
- } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
+ updateNotificationPulse();
+ } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
+ // turn off LED when user passes through lock screen
+ if (mNotificationLight != null) {
+ mNotificationLight.turnOff();
+ }
+ }
+ }
+
+ if (action.equals(Intent.ACTION_USER_STOPPED)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
@@ -1898,11 +1929,6 @@
REASON_PROFILE_TURNED_OFF);
mSnoozeHelper.clearData(userHandle);
}
- } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
- // turn off LED when user passes through lock screen
- if (mNotificationLight != null) {
- mNotificationLight.turnOff();
- }
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
@@ -1976,8 +2002,10 @@
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
+ }
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
@@ -2000,13 +2028,15 @@
public void update(Uri uri) {
ContentResolver resolver = getContext().getContentResolver();
- if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
- boolean pulseEnabled = Settings.System.getIntForUser(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
+ if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
+ boolean pulseEnabled = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
!= 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateNotificationPulse();
+ }
}
}
if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
@@ -2491,14 +2521,22 @@
mToastRateLimiter = toastRateLimiter;
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager,
+ mAccessibilityManager, mPackageManagerClient, usageStats,
+ mNotificationManagerPrivate, mZenModeHelper, flagResolver);
+ }
+
// register for various Intents.
// If this is called within a test, make sure to unregister the intent receivers by
// calling onDestroy()
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
+ if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ }
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -2818,6 +2856,9 @@
}
registerNotificationPreferencesPullers();
new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker);
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.onSystemReady();
+ }
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
@@ -2826,9 +2867,6 @@
mAssistants.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
mHistoryManager.onBootPhaseAppsCanStart();
- if (expireBitmaps()) {
- NotificationBitmapJobService.scheduleJob(getContext());
- }
registerDeviceConfigChange();
migrateDefaultNAS();
maybeShowInitialReviewPermissionsNotification();
@@ -2837,6 +2875,10 @@
} else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) {
mPreferencesHelper.updateFixedImportance(mUm.getUsers());
mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers());
+ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ if (expireBitmaps()) {
+ NotificationBitmapJobService.scheduleJob(getContext());
+ }
}
}
@@ -6374,6 +6416,9 @@
pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
pw.println(" hideSilentStatusBar="
+ mPreferencesHelper.shouldHideSilentStatusIcons());
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.dump(pw, " ", filter);
+ }
}
pw.println(" mArchive=" + mArchive.toString());
mArchive.dumpImpl(pw, filter);
@@ -7631,7 +7676,11 @@
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null,
SystemClock.elapsedRealtime());
- updateLightsLocked();
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.updateLightsLocked();
+ } else {
+ updateLightsLocked();
+ }
if (isSnoozable(r)) {
if (mSnoozeCriterionId != null) {
mAssistants.notifyAssistantSnoozedLocked(r, mSnoozeCriterionId);
@@ -7760,7 +7809,11 @@
cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
mSendDelete, childrenFlagChecker, mReason,
mCancellationElapsedTimeMs);
- updateLightsLocked();
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.updateLightsLocked();
+ } else {
+ updateLightsLocked();
+ }
if (mShortcutHelper != null) {
mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
true /* isRemoved */,
@@ -8053,7 +8106,14 @@
int buzzBeepBlinkLoggingCode = 0;
if (!r.isHidden()) {
- buzzBeepBlinkLoggingCode = buzzBeepBlinkLocked(r);
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ buzzBeepBlinkLoggingCode = mAttentionHelper.buzzBeepBlinkLocked(r,
+ new NotificationAttentionHelper.Signals(
+ mUserProfiles.isCurrentProfile(r.getUserId()),
+ mListenerHints));
+ } else {
+ buzzBeepBlinkLoggingCode = buzzBeepBlinkLocked(r);
+ }
}
if (notification.getSmallIcon() != null) {
@@ -9033,7 +9093,13 @@
|| interruptiveChanged;
if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
- buzzBeepBlinkLocked(record);
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.buzzBeepBlinkLocked(record,
+ new NotificationAttentionHelper.Signals(
+ mUserProfiles.isCurrentProfile(record.getUserId()), mListenerHints));
+ } else {
+ buzzBeepBlinkLocked(record);
+ }
// Log alert after change in intercepted state to Zen Log as well
ZenLog.traceAlertOnUpdatedIntercept(record);
@@ -9407,18 +9473,22 @@
});
}
- // sound
- if (canceledKey.equals(mSoundNotificationKey)) {
- clearSoundLocked();
- }
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.clearEffectsLocked(canceledKey);
+ } else {
+ // sound
+ if (canceledKey.equals(mSoundNotificationKey)) {
+ clearSoundLocked();
+ }
- // vibrate
- if (canceledKey.equals(mVibrateNotificationKey)) {
- clearVibrateLocked();
- }
+ // vibrate
+ if (canceledKey.equals(mVibrateNotificationKey)) {
+ clearVibrateLocked();
+ }
- // light
- mLights.remove(canceledKey);
+ // light
+ mLights.remove(canceledKey);
+ }
}
// Record usage stats
@@ -9767,7 +9837,11 @@
cancellationElapsedTimeMs);
}
}
- updateLightsLocked();
+ if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mAttentionHelper.updateLightsLocked();
+ } else {
+ updateLightsLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index b015a72..d2e980b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Objects;
@@ -497,6 +498,7 @@
final boolean is_non_dismissible;
final int fsi_state;
final boolean is_locked;
+ final int age_in_minutes;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
@@ -541,6 +543,9 @@
hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
this.is_locked = p.r.isLocked();
+
+ this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
+ p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().when);
}
}
@@ -601,4 +606,13 @@
}
return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI;
}
+
+ /**
+ * @param postTimeMs time (in {@link System#currentTimeMillis} time) the notification was posted
+ * @param whenMs A timestamp related to this notification, in milliseconds since the epoch.
+ * @return difference in duration as an integer in minutes
+ */
+ static int getAgeInMinutes(long postTimeMs, long whenMs) {
+ return (int) Duration.ofMillis(postTimeMs - whenMs).toMinutes();
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9da0e98..fc0a776 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -77,7 +77,8 @@
notificationReported.is_non_dismissible,
notificationReported.post_duration_millis,
notificationReported.fsi_state,
- notificationReported.is_locked);
+ notificationReported.is_locked,
+ notificationReported.age_in_minutes);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 85c140c..e14f7c0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -203,7 +202,6 @@
Context.DEVICE_ID_DEFAULT, userId, TAG);
}
int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
- flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask;
if (userSet) {
mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, flagMask,
FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, userId);
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index 59b2bc1..7db2e8b 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -13,7 +13,7 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
@@ -33,7 +33,7 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a700d32..71562dc 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1022,6 +1022,8 @@
@VisibleForTesting
protected void setZenModeSetting(int zen) {
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+ ZenLog.traceSetZenMode(Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, -1),
+ "updated setting");
showZenUpgradeNotification(zen);
}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5b93244..50ed3b1 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -655,7 +655,7 @@
}
} catch (SignatureNotFoundException e) {
// Nothing
- } catch (SecurityException e) {
+ } catch (SignatureException | SecurityException e) {
Slog.e(TAG, "V4 signature error", e);
}
return null;
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ffa2af1..6baa889 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1024,7 +1024,7 @@
if ("android".equals(packageName) || "system".equals(packageName)) {
return androidApplication();
}
- if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+ if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
// Already generates the external package name
return generateApplicationInfoFromSettings(packageName,
flags, filterCallingUid, userId);
@@ -1518,7 +1518,6 @@
pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
pi.firstInstallTime = state.getFirstInstallTimeMillis();
pi.lastUpdateTime = ps.getLastUpdateTime();
- pi.isArchived = isArchived(state);
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = ps.getPackageName();
@@ -1701,7 +1700,9 @@
if (!listApex && ps.getPkg() != null && ps.getPkg().isApex()) {
continue;
}
- if (listArchivedOnly && !isArchived(ps.getUserStateOrDefault(userId))) {
+ PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+ if (listArchivedOnly && !userState.isInstalled()
+ && userState.getArchiveState() == null) {
continue;
}
if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
@@ -1747,13 +1748,6 @@
return new ParceledListSlice<>(list);
}
- // TODO(b/288142708) Check for userState.isInstalled() here once this bug is fixed.
- // If an app has isInstalled() == true - it should not be filtered above in any case, currently
- // it is.
- private static boolean isArchived(PackageUserStateInternal userState) {
- return userState.getArchiveState() != null;
- }
-
public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 7d59210..83f90a1 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -52,6 +53,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -119,8 +121,6 @@
*/
public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
boolean removedBySystem) {
- final boolean isArchived = false; // TODO(b/278553670) Pass true during archival.
-
final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
final boolean res;
@@ -250,6 +250,7 @@
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
+ final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
info.sendSystemPackageUpdatedBroadcasts();
PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
@@ -561,6 +562,17 @@
Slog.d(TAG, "Marking package:" + ps.getPackageName()
+ " uninstalled for user:" + nextUserId);
}
+
+ // Keep enabled and disabled components in case of DELETE_KEEP_DATA
+ ArraySet<String> enabledComponents = null;
+ ArraySet<String> disabledComponents = null;
+ if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
+ enabledComponents = new ArraySet<String>(
+ ps.readUserState(nextUserId).getEnabledComponents());
+ disabledComponents = new ArraySet<String>(
+ ps.readUserState(nextUserId).getDisabledComponents());
+ }
+
// Preserve ArchiveState if this is not a full uninstall
ArchiveState archiveState =
(flags & DELETE_KEEP_DATA) == 0
@@ -580,8 +592,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
- null /*enabledComponents*/,
- null /*disabledComponents*/,
+ enabledComponents,
+ disabledComponents,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2ee0706..e1e5e6d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -44,6 +44,7 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -111,6 +112,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
@@ -1142,15 +1144,18 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
+ final ArchivedPackageParcel archivedPackage;
try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
if (request.getPackageLite() == null || !request.isArchived()) {
// TODO: pass packageLite from install request instead of reparsing the package
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ archivedPackage = null;
} else {
// Archived install mode, no APK.
parsedPackage = pp.parsePackageFromPackageLite(request.getPackageLite(),
parseFlags);
+ archivedPackage = request.getPackageLite().getArchivedPackage();
}
} catch (PackageManagerException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
@@ -1833,7 +1838,7 @@
shouldCloseFreezerBeforeReturn = false;
request.setPrepareResult(replace, targetScanFlags, targetParseFlags,
- oldPackageState, parsedPackage,
+ oldPackageState, parsedPackage, archivedPackage,
replace /* clearCodeCache */, sysPkg, ps, disabledPs);
} finally {
request.setFreezer(freezer);
@@ -2164,6 +2169,9 @@
}
}
if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+ mPm.createArchiveStateIfNeeded(ps,
+ installRequest.getArchivedPackage(),
+ installRequest.getNewUsers());
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
mPm.updateInstantAppInstallerLocked(packageName);
}
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 3a6d423..a4ee3c8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -21,6 +21,9 @@
import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -29,6 +32,7 @@
import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.app.AppOpsManager;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInstaller;
@@ -55,6 +59,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.List;
final class InstallRequest {
@@ -77,6 +82,8 @@
/** parsed package to be scanned */
@Nullable
private ParsedPackage mParsedPackage;
+ @Nullable
+ private ArchivedPackageParcel mArchivedPackage;
private boolean mClearCodeCache;
private boolean mSystem;
@Nullable
@@ -144,6 +151,9 @@
@NonNull
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+ @NonNull
+ private ArrayList<String> mWarnings = new ArrayList<>();
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -189,6 +199,7 @@
}
mInstallArgs = null;
mParsedPackage = parsedPackage;
+ mArchivedPackage = null;
mParseFlags = parseFlags;
mScanFlags = scanFlags;
mScanResult = scanResult;
@@ -448,6 +459,9 @@
return mParsedPackage;
}
+ @Nullable
+ public ArchivedPackageParcel getArchivedPackage() { return mArchivedPackage; }
+
@ParsingPackageUtils.ParseFlags
public int getParseFlags() {
return mParseFlags;
@@ -650,6 +664,11 @@
return mUpdateBroadcastInstantUserIds;
}
+ @NonNull
+ public ArrayList<String> getWarnings() {
+ return mWarnings;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
@@ -753,7 +772,8 @@
public void setPrepareResult(boolean replace, int scanFlags,
int parseFlags, PackageState existingPackageState,
- ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
+ ParsedPackage packageToScan, ArchivedPackageParcel archivedPackage,
+ boolean clearCodeCache, boolean system,
PackageSetting originalPs, PackageSetting disabledPs) {
mReplace = replace;
mScanFlags = scanFlags;
@@ -761,6 +781,7 @@
mExistingPackageName =
existingPackageState != null ? existingPackageState.getPackageName() : null;
mParsedPackage = packageToScan;
+ mArchivedPackage = archivedPackage;
mClearCodeCache = clearCodeCache;
mSystem = system;
mOriginalPs = originalPs;
@@ -845,6 +866,10 @@
}
}
+ public void addWarning(@NonNull String warning) {
+ mWarnings.add(warning);
+ }
+
public void onPrepareStarted() {
if (mPackageMetrics != null) {
mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
@@ -894,22 +919,37 @@
}
public void onDexoptFinished(DexoptResult dexoptResult) {
- if (mPackageMetrics == null) {
- return;
- }
- mDexoptStatus = dexoptResult.getFinalStatus();
- if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
- return;
- }
- long durationMillis = 0;
- for (DexoptResult.PackageDexoptResult packageResult :
- dexoptResult.getPackageDexoptResults()) {
- for (DexoptResult.DexContainerFileDexoptResult fileResult :
- packageResult.getDexContainerFileDexoptResults()) {
- durationMillis += fileResult.getDex2oatWallTimeMillis();
+ // Only report external profile warnings when installing from adb. The goal is to warn app
+ // developers if they have provided bad external profiles, so it's not beneficial to report
+ // those warnings in the normal app install workflow.
+ if (isInstallFromAdb()) {
+ var externalProfileErrors = new LinkedHashSet<String>();
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ externalProfileErrors.addAll(fileResult.getExternalProfileErrors());
+ }
+ }
+ if (!externalProfileErrors.isEmpty()) {
+ addWarning("Error occurred during dexopt when processing external profiles:\n "
+ + String.join("\n ", externalProfileErrors));
}
}
- mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+
+ // Report dexopt metrics.
+ if (mPackageMetrics != null) {
+ mDexoptStatus = dexoptResult.getFinalStatus();
+ if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) {
+ long durationMillis = 0;
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ durationMillis += fileResult.getDex2oatWallTimeMillis();
+ }
+ }
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+ }
+ }
}
public void onInstallCompleted() {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 64cdca3..f59188e 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -31,12 +32,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ArchivedActivityParcel;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -54,8 +58,10 @@
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -96,6 +102,11 @@
this.mPm = mPm;
}
+ /** Returns whether a package is archived for a user. */
+ public static boolean isArchived(PackageUserState userState) {
+ return userState.getArchiveState() != null && !userState.isInstalled();
+ }
+
void requestArchive(
@NonNull String packageName,
@NonNull String callerPackageName,
@@ -138,7 +149,10 @@
mPm.mInstallerService.uninstall(
new VersionedPackage(packageName,
PackageManager.VERSION_CODE_HIGHEST),
- callerPackageName, DELETE_KEEP_DATA, intentSender, userId,
+ callerPackageName,
+ DELETE_ARCHIVE | DELETE_KEEP_DATA,
+ intentSender,
+ userId,
binderUid);
})
.exceptionally(
@@ -158,7 +172,8 @@
String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
verifyInstaller(responsibleInstallerPackage);
- List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId);
+ List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
+ userId);
final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>();
mPm.mHandler.post(() -> {
try {
@@ -172,13 +187,34 @@
return archiveState;
}
- private ArchiveState createArchiveStateInternal(String packageName, int userId,
+ static ArchiveState createArchiveState(@NonNull ArchivedPackageParcel archivedPackage,
+ int userId, String installerPackage) {
+ try {
+ var packageName = archivedPackage.packageName;
+ var mainActivities = archivedPackage.archivedActivities;
+ List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.length);
+ for (int i = 0, size = mainActivities.length; i < size; ++i) {
+ var mainActivity = mainActivities[i];
+ Path iconPath = storeIconForParcel(packageName, mainActivity, userId, i);
+ ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
+ mainActivity.title, iconPath, null);
+ archiveActivityInfos.add(activityInfo);
+ }
+
+ return new ArchiveState(archiveActivityInfos, installerPackage);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to create archive state", e);
+ return null;
+ }
+ }
+
+ ArchiveState createArchiveStateInternal(String packageName, int userId,
List<LauncherActivityInfo> mainActivities, String installerPackage)
throws IOException {
- List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>();
- for (int i = 0; i < mainActivities.size(); i++) {
+ List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.size());
+ for (int i = 0, size = mainActivities.size(); i < size; i++) {
LauncherActivityInfo mainActivity = mainActivities.get(i);
- Path iconPath = storeIcon(packageName, mainActivity, userId);
+ Path iconPath = storeIcon(packageName, mainActivity, userId, i);
ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
mainActivity.getLabel().toString(), iconPath, null);
archiveActivityInfos.add(activityInfo);
@@ -188,17 +224,30 @@
}
// TODO(b/298452477) Handle monochrome icons.
+ private static Path storeIconForParcel(String packageName, ArchivedActivityParcel mainActivity,
+ @UserIdInt int userId, int index) throws IOException {
+ if (mainActivity.iconBitmap == null) {
+ return null;
+ }
+ File iconsDir = createIconsDir(userId);
+ File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+ try (FileOutputStream out = new FileOutputStream(iconFile)) {
+ out.write(mainActivity.iconBitmap);
+ out.flush();
+ }
+ return iconFile.toPath();
+ }
+
@VisibleForTesting
Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
- @UserIdInt int userId)
- throws IOException {
+ @UserIdInt int userId, int index) throws IOException {
int iconResourceId = mainActivity.getActivityInfo().getIconResource();
if (iconResourceId == 0) {
// The app doesn't define an icon. No need to store anything.
return null;
}
File iconsDir = createIconsDir(userId);
- File iconFile = new File(iconsDir, packageName + "-" + mainActivity.getName() + ".png");
+ File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0));
try (FileOutputStream out = new FileOutputStream(iconFile)) {
// Note: Quality is ignored for PNGs.
@@ -228,6 +277,9 @@
*/
public boolean verifySupportsUnarchival(String installerPackage) {
// TODO(b/278553670) Check if installerPackage supports unarchival.
+ if (TextUtils.isEmpty(installerPackage)) {
+ return false;
+ }
return true;
}
@@ -265,11 +317,50 @@
mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
}
+ /**
+ * Returns the icon of an archived app. This is the icon of the main activity of the app.
+ *
+ * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
+ * launcher activities, only one of the icons is returned arbitrarily.
+ */
+ public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(user);
+
+ Computer snapshot = mPm.snapshotComputer();
+ int callingUid = Binder.getCallingUid();
+ int userId = user.getIdentifier();
+ PackageStateInternal ps;
+ try {
+ ps = getPackageState(packageName, snapshot, callingUid, userId);
+ snapshot.enforceCrossUserPermission(callingUid, userId, true, false,
+ "getArchivedAppIcon");
+ verifyArchived(ps, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+
+ List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault(
+ userId).getArchiveState().getActivityInfos();
+ if (activityInfos.size() == 0) {
+ return null;
+ }
+
+ // TODO(b/298452477) Handle monochrome icons.
+ // In the rare case the archived app defined more than two launcher activities, we choose
+ // the first one arbitrarily.
+ return decodeIcon(activityInfos.get(0));
+ }
+
+ @VisibleForTesting
+ Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
+ return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
+ }
+
private void verifyArchived(PackageStateInternal ps, int userId)
throws PackageManager.NameNotFoundException {
PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
- // TODO(b/288142708) Check for isInstalled false here too.
- if (userState.getArchiveState() == null) {
+ if (!isArchived(userState)) {
throw new PackageManager.NameNotFoundException(
TextUtils.formatSimple("Package %s is not currently archived.",
ps.getPackageName()));
@@ -310,16 +401,16 @@
/* initialExtras= */ null);
}
- private List<LauncherActivityInfo> getLauncherActivityInfos(PackageStateInternal ps,
+ List<LauncherActivityInfo> getLauncherActivityInfos(String packageName,
int userId) throws PackageManager.NameNotFoundException {
List<LauncherActivityInfo> mainActivities =
Binder.withCleanCallingIdentity(() -> getLauncherApps().getActivityList(
- ps.getPackageName(),
+ packageName,
new UserHandle(userId)));
if (mainActivities.isEmpty()) {
throw new PackageManager.NameNotFoundException(
TextUtils.formatSimple("The app %s does not have a main activity.",
- ps.getPackageName()));
+ packageName));
}
return mainActivities;
@@ -340,7 +431,7 @@
return DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS;
}
- private String getResponsibleInstallerPackage(PackageStateInternal ps) {
+ static String getResponsibleInstallerPackage(PackageStateInternal ps) {
return TextUtils.isEmpty(ps.getInstallSource().mUpdateOwnerPackageName)
? ps.getInstallSource().mInstallerPackageName
: ps.getInstallSource().mUpdateOwnerPackageName;
@@ -423,7 +514,7 @@
}
}
- private File createIconsDir(@UserIdInt int userId) throws IOException {
+ private static File createIconsDir(@UserIdInt int userId) throws IOException {
File iconsDir = getIconsDir(userId);
if (!iconsDir.isDirectory()) {
iconsDir.delete();
@@ -436,7 +527,7 @@
return iconsDir;
}
- private File getIconsDir(int userId) {
+ private static File getIconsDir(int userId) {
return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
}
@@ -462,4 +553,90 @@
drawable.draw(canvas);
return bitmap;
}
+
+ private static byte[] bytesFromBitmapFile(Path path) throws IOException {
+ if (path == null) {
+ return null;
+ }
+ // Technically we could just read the bytes, but we want to be sure we store the
+ // right format.
+ return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
+ }
+
+ private static byte[] bytesFromBitmap(Bitmap bitmap) throws IOException {
+ if (bitmap == null) {
+ return null;
+ }
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
+ bitmap.getByteCount())) {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+ return baos.toByteArray();
+ }
+ }
+
+ /**
+ * Creates serializable archived activities from existing ArchiveState.
+ */
+ static ArchivedActivityParcel[] createArchivedActivities(ArchiveState archiveState)
+ throws IOException {
+ var infos = archiveState.getActivityInfos();
+ if (infos == null || infos.isEmpty()) {
+ throw new IllegalArgumentException("No activities in archive state");
+ }
+
+ List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
+ for (int i = 0, size = infos.size(); i < size; ++i) {
+ var info = infos.get(i);
+ if (info == null) {
+ continue;
+ }
+ var archivedActivity = new ArchivedActivityParcel();
+ archivedActivity.title = info.getTitle();
+ archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap());
+ archivedActivity.monochromeIconBitmap = bytesFromBitmapFile(
+ info.getMonochromeIconBitmap());
+ activities.add(archivedActivity);
+ }
+
+ if (activities.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Failed to extract title and icon of main activities");
+ }
+
+ return activities.toArray(new ArchivedActivityParcel[activities.size()]);
+ }
+
+ /**
+ * Creates serializable archived activities from launcher activities.
+ */
+ static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos)
+ throws IOException {
+ if (infos == null || infos.isEmpty()) {
+ throw new IllegalArgumentException("No launcher activities");
+ }
+
+ List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
+ for (int i = 0, size = infos.size(); i < size; ++i) {
+ var info = infos.get(i);
+ if (info == null) {
+ continue;
+ }
+ var archivedActivity = new ArchivedActivityParcel();
+ archivedActivity.title = info.getLabel().toString();
+ archivedActivity.iconBitmap =
+ info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
+ drawableToBitmap(info.getIcon(/* density= */ 0)));
+ // TODO(b/298452477) Handle monochrome icons.
+ archivedActivity.monochromeIconBitmap = null;
+ activities.add(archivedActivity);
+ }
+
+ if (activities.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Failed to extract title and icon of main activities");
+ }
+
+ return activities.toArray(new ArchivedActivityParcel[activities.size()]);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2e9da09..f554773 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -40,6 +40,7 @@
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
@@ -137,6 +138,7 @@
import android.os.incremental.IncrementalManager;
import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
+import android.os.incremental.V4Signature;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
@@ -801,6 +803,7 @@
// entries like "lost+found".
if (file.isDirectory()) return false;
if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
+ if (file.getName().endsWith(V4Signature.EXT)) return false;
if (isAppMetadata(file)) return false;
if (DexMetadataHelper.isDexMetadataFile(file)) return false;
if (VerityUtils.isFsveritySignatureFile(file)) return false;
@@ -1165,11 +1168,6 @@
throw new IllegalArgumentException(
"Archived installation can only use Streaming System DataLoader.");
}
- if (!TextUtils.isEmpty(params.appPackageName) && !isArchivedInstallationAllowed(
- params.appPackageName)) {
- throw new IllegalArgumentException(
- "Archived installation of this package is not allowed.");
- }
}
}
@@ -1506,6 +1504,21 @@
}
@GuardedBy("mLock")
+ private void enableFsVerityToAddedApksWithIdsig() throws PackageManagerException {
+ try {
+ List<File> files = getAddedApksLocked();
+ for (var file : files) {
+ if (new File(file.getPath() + V4Signature.EXT).exists()) {
+ VerityUtils.setUpFsverity(file.getPath());
+ }
+ }
+ } catch (IOException e) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to enable fs-verity to verify with idsig: " + e);
+ }
+ }
+
+ @GuardedBy("mLock")
private List<ApkLite> getAddedApkLitesLocked() throws PackageManagerException {
if (!isArchivedInstallation()) {
List<File> files = getAddedApksLocked();
@@ -1548,6 +1561,10 @@
}
var archPkg = metadata.getArchivedPackage();
+ if (archPkg == null) {
+ throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Metadata does not contain ArchivedPackage: " + file);
+ }
if (archPkg.packageName == null || archPkg.signingDetails == null) {
throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
"ArchivedPackage does not contain required info: " + file);
@@ -3294,6 +3311,14 @@
}
}
+ // Needs to happen before the first v4 signature verification, which happens in
+ // getAddedApkLitesLocked.
+ if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ if (!isIncrementalInstallation()) {
+ enableFsVerityToAddedApksWithIdsig();
+ }
+ }
+
final List<ApkLite> addedFiles = getAddedApkLitesLocked();
if (addedFiles.isEmpty()
&& (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
@@ -3395,8 +3420,7 @@
"Archived installation of this package is not allowed.");
}
- if (!isInstalledByAdb(getInstallSource().mInitiatingPackageName)
- && !mPm.mInstallerService.mPackageArchiver.verifySupportsUnarchival(
+ if (!mPm.mInstallerService.mPackageArchiver.verifySupportsUnarchival(
getInstallSource().mInstallerPackageName)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
@@ -3657,6 +3681,16 @@
}
@GuardedBy("mLock")
+ private void maybeStageV4SignatureLocked(File origFile, File targetFile)
+ throws PackageManagerException {
+ final File originalSignature = new File(origFile.getPath() + V4Signature.EXT);
+ if (originalSignature.exists()) {
+ final File stagedSignature = new File(targetFile.getPath() + V4Signature.EXT);
+ stageFileLocked(originalSignature, stagedSignature);
+ }
+ }
+
+ @GuardedBy("mLock")
private void maybeStageDexMetadataLocked(File origFile, File targetFile)
throws PackageManagerException {
final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile);
@@ -3783,6 +3817,10 @@
// Stage APK's fs-verity signature if present.
maybeStageFsveritySignatureLocked(origFile, targetFile,
isFsVerityRequiredForApk(origFile, targetFile));
+ // Stage APK's v4 signature if present.
+ if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ maybeStageV4SignatureLocked(origFile, targetFile);
+ }
// Stage dex metadata (.dm) and corresponding fs-verity signature if present.
maybeStageDexMetadataLocked(origFile, targetFile);
// Stage checksums (.digests) if present.
@@ -3800,10 +3838,22 @@
}
@GuardedBy("mLock")
+ private void maybeInheritV4SignatureLocked(File origFile) {
+ // Inherit the v4 signature file if present.
+ final File v4SignatureFile = new File(origFile.getPath() + V4Signature.EXT);
+ if (v4SignatureFile.exists()) {
+ mResolvedInheritedFiles.add(v4SignatureFile);
+ }
+ }
+
+ @GuardedBy("mLock")
private void inheritFileLocked(File origFile) {
mResolvedInheritedFiles.add(origFile);
maybeInheritFsveritySignatureLocked(origFile);
+ if (android.security.Flags.extendVbChainToUpdatedApk()) {
+ maybeInheritV4SignatureLocked(origFile);
+ }
// Inherit the dex metadata if present.
final File dexMetadataFile =
@@ -5139,6 +5189,10 @@
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
+ ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(warnings)) {
+ fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
+ }
}
try {
final BroadcastOptions options = BroadcastOptions.makeBasic();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d23dcbc..33cb85c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
import static com.android.server.pm.DexOptHelper.useArtService;
@@ -116,11 +117,7 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.ApkLite;
-import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -228,6 +225,7 @@
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
@@ -1435,9 +1433,98 @@
break;
}
}
+ if (!request.getWarnings().isEmpty()) {
+ extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
+ }
return extras;
}
+ ArchivedPackageParcel getArchivedPackageInternal(@NonNull String packageName, int userId) {
+ Objects.requireNonNull(packageName);
+ int binderUid = Binder.getCallingUid();
+
+ Computer snapshot = snapshotComputer();
+ snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
+ "getArchivedPackage");
+
+ ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
+ archPkg.packageName = packageName;
+
+ ArchiveState archiveState;
+ synchronized (mLock) {
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ return null;
+ }
+ var psi = ps.getUserStateOrDefault(userId);
+ archiveState = psi.getArchiveState();
+ if (archiveState == null && !psi.isInstalled()) {
+ return null;
+ }
+
+ archPkg.signingDetails = ps.getSigningDetails();
+
+ long longVersionCode = ps.getVersionCode();
+ archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
+ archPkg.versionCode = (int) longVersionCode;
+
+ // TODO(b/297916136): extract target sdk version.
+ archPkg.targetSdkVersion = MIN_INSTALLABLE_TARGET_SDK;
+
+ // These get translated in flags important for user data management.
+ archPkg.defaultToDeviceProtectedStorage = String.valueOf(
+ ps.isDefaultToDeviceProtectedStorage());
+ archPkg.requestLegacyExternalStorage = String.valueOf(
+ ps.isRequestLegacyExternalStorage());
+ archPkg.userDataFragile = String.valueOf(ps.isUserDataFragile());
+ }
+
+ try {
+ if (archiveState != null) {
+ archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
+ archiveState);
+ } else {
+ var mainActivities =
+ mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName,
+ userId);
+ archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
+ mainActivities);
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Package does not have a main activity", e);
+ }
+
+ return archPkg;
+ }
+
+ void createArchiveStateIfNeeded(PackageSetting pkgSetting, ArchivedPackageParcel archivePackage,
+ int[] userIds) {
+ if (pkgSetting == null || archivePackage == null
+ || archivePackage.archivedActivities == null || userIds == null
+ || userIds.length == 0) {
+ return;
+ }
+
+ String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage(
+ pkgSetting);
+ // TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival.
+ if (TextUtils.isEmpty(responsibleInstallerPackage)) {
+ Slog.e(TAG, "Can't create archive state: responsible installer is empty");
+ return;
+ }
+ for (int userId : userIds) {
+ var archiveState = PackageArchiver.createArchiveState(archivePackage, userId,
+ responsibleInstallerPackage);
+ if (archiveState == null) {
+ continue;
+ }
+ pkgSetting
+ .modifyUserState(userId)
+ .setArchiveState(archiveState);
+ }
+ }
+
+
void scheduleWriteSettings() {
// We normally invalidate when we write settings, but in cases where we delay and
// coalesce settings writes, this strategy would have us invalidate the cache too late.
@@ -4594,9 +4681,12 @@
@UserIdInt int userId, @NonNull String recentCallingPackage,
@NonNull String debugInfo) {
synchronized (mLock) {
- final PackageUserStateInternal userState = mSettings.getPackageLPr(
- packageName).getUserStateOrDefault(userId);
- if (userState.isQuarantined()) {
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ // If the package doesn't exist, don't need to proceed to setPackageStoppedState.
+ if (pkgSetting == null) {
+ return;
+ }
+ if (pkgSetting.getUserStateOrDefault(userId).isQuarantined()) {
Slog.i(TAG,
"Component is quarantined+suspended but being used: "
+ packageName + " by " + recentCallingPackage + ", debugInfo: "
@@ -6301,35 +6391,13 @@
}
@Override
- public ArchivedPackageParcel getArchivedPackage(String apkPath) {
- ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
- new File(apkPath), ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
- if (result.isError()) {
- throw new IllegalArgumentException(result.getErrorMessage(), result.getException());
- }
- final ApkLite apk = result.getResult();
+ public ArchivedPackageParcel getArchivedPackage(@NonNull String packageName, int userId) {
+ return getArchivedPackageInternal(packageName, userId);
+ }
- ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
- archPkg.packageName = apk.getPackageName();
- archPkg.signingDetails = apk.getSigningDetails();
-
- archPkg.versionCodeMajor = apk.getVersionCodeMajor();
- archPkg.versionCode = apk.getVersionCode();
-
- archPkg.targetSdkVersion = apk.getTargetSdkVersion();
-
- // These get translated in flags important for user data management.
- archPkg.backupAllowed = String.valueOf(apk.isBackupAllowed());
- archPkg.defaultToDeviceProtectedStorage = String.valueOf(
- apk.isDefaultToDeviceProtectedStorage());
- archPkg.requestLegacyExternalStorage = String.valueOf(
- apk.isRequestLegacyExternalStorage());
- archPkg.userDataFragile = String.valueOf(apk.isUserDataFragile());
- archPkg.clearUserDataOnFailedRestoreAllowed = String.valueOf(
- apk.isClearUserDataOnFailedRestoreAllowed());
-
- return archPkg;
+ @Override
+ public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+ return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8d82085..7264e2e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -26,6 +26,7 @@
import static android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
import static android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
+
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -82,9 +83,9 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.PersistableBundle;
@@ -130,6 +131,7 @@
import libcore.io.IoUtils;
import libcore.io.Streams;
+import libcore.util.HexEncoding;
import java.io.BufferedReader;
import java.io.File;
@@ -248,8 +250,6 @@
return runStreamingInstall();
case "install-incremental":
return runIncrementalInstall();
- case "install-archived":
- return runArchivedInstall();
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
@@ -277,6 +277,10 @@
return runUninstall();
case "clear":
return runClear();
+ case "get-archived-package-metadata":
+ return runGetArchivedPackageMetadata();
+ case "install-archived":
+ return runArchivedInstall();
case "enable":
return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
case "disable":
@@ -1808,6 +1812,56 @@
return doRemoveSplits(sessionId, splitNames, true /*logSuccess*/);
}
+ private int runGetArchivedPackageMetadata() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+ final int translatedUserId = translateUserId(userId, UserHandle.USER_NULL,
+ "runGetArchivedPackageMetadata");
+
+ try {
+ var archivedPackage = mInterface.getArchivedPackage(packageName, translatedUserId);
+ if (archivedPackage == null) {
+ pw.write("Package not found " + packageName);
+ return -1;
+ }
+
+ Parcel parcel = Parcel.obtain();
+ byte[] bytes;
+ try {
+ parcel.writeParcelable(archivedPackage, 0);
+ bytes = parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
+
+ String encoded = HexEncoding.encodeToString(bytes);
+ pw.write(encoded);
+ } catch (Exception e) {
+ getErrPrintWriter().println("Failed to get archived package, reason: " + e);
+ pw.println("Failure [failed to get archived package], reason: " + e);
+ return -1;
+ }
+ return 0;
+ }
+
private int runInstallExisting() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
int userId = UserHandle.USER_CURRENT;
@@ -4146,28 +4200,24 @@
throw new IllegalArgumentException("Error: Can't open file: " + inPath);
}
- File tmpFile = null;
+ final String encoded;
final ParcelFileDescriptor fd = fdWithSize.first;
+ final int size = (int) (long) fdWithSize.second;
try (InputStream inStream = new AutoCloseInputStream(fd)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- File tmpStagingDir = Environment.getDataAppDirectory(null);
- tmpFile = new File(tmpStagingDir, "tmdl" + RANDOM.nextInt() + ".tmp");
-
- try (OutputStream outStream = new FileOutputStream(tmpFile)) {
- Streams.copy(inStream, outStream);
- }
-
- return mInterface.getArchivedPackage(tmpFile.getAbsolutePath());
- } finally {
- if (tmpFile != null) {
- tmpFile.delete();
- }
- Binder.restoreCallingIdentity(identity);
- }
+ byte[] bytes = new byte[size];
+ Streams.readFully(inStream, bytes);
+ encoded = new String(bytes);
} catch (IOException e) {
- throw new IllegalArgumentException("Error: Can't stage file: " + inPath, e);
+ throw new IllegalArgumentException("Error: Can't load archived package from: " + inPath,
+ e);
}
+
+ var result = Metadata.readArchivedPackageParcel(HexEncoding.decode(encoded));
+ if (result == null) {
+ throw new IllegalArgumentException(
+ "Error: Can't parse archived package from: " + inPath);
+ }
+ return result;
}
private void processArgForLocalFile(String arg, PackageInstaller.Session session,
@@ -4347,10 +4397,21 @@
session.commit(receiver.getIntentSender());
if (!session.isStaged()) {
final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
+ int status = result.getIntExtra(
+ PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ List<String> warnings =
+ result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS);
if (status == PackageInstaller.STATUS_SUCCESS) {
- if (logSuccess) {
+ if (!ArrayUtils.isEmpty(warnings)) {
+ // Don't start the output string with "Success" because that will make adb
+ // treat this as a success.
+ for (String warning : warnings) {
+ pw.println("Warning: " + warning);
+ }
+ // Treat warnings as failure to draw app developers' attention.
+ status = PackageInstaller.STATUS_FAILURE;
+ pw.println("Completed with warning(s)");
+ } else if (logSuccess) {
pw.println("Success");
}
} else {
@@ -4507,6 +4568,10 @@
PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, 0);
+ if (ai == null) {
+ Slog.e(TAG, "Failed to get ApplicationInfo for package name(" + pii.packageName + ").");
+ return null;
+ }
AssetManager am = new AssetManager();
am.addAssetPath(ai.publicSourceDir);
res = new Resources(am, null, null);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index fbe5a51..9e7f043 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -166,16 +166,7 @@
/** @hide */
@VisibleForTesting
public static Metadata forArchived(ArchivedPackageParcel archivedPackage) {
- Parcel parcel = Parcel.obtain();
- byte[] bytes;
- try {
- parcel.writeParcelable(archivedPackage, 0);
- bytes = parcel.marshall();
- } finally {
- parcel.recycle();
- }
-
- return new Metadata(ARCHIVED, bytes, null);
+ return new Metadata(ARCHIVED, writeArchivedPackageParcel(archivedPackage), null);
}
static Metadata forDataOnlyStreaming(String fileId) {
@@ -270,11 +261,14 @@
if (getMode() != ARCHIVED) {
throw new IllegalStateException("Not an archived package metadata.");
}
+ return readArchivedPackageParcel(this.mData);
+ }
+ static ArchivedPackageParcel readArchivedPackageParcel(byte[] bytes) {
Parcel parcel = Parcel.obtain();
ArchivedPackageParcel result;
try {
- parcel.unmarshall(this.mData, 0, this.mData.length);
+ parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
result = parcel.readParcelable(ArchivedPackageParcel.class.getClassLoader());
} finally {
@@ -282,6 +276,16 @@
}
return result;
}
+
+ static byte[] writeArchivedPackageParcel(ArchivedPackageParcel archivedPackage) {
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(archivedPackage, 0);
+ return parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
+ }
}
private static class DataLoader implements DataLoaderService.DataLoader {
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 7ad336c..24d2a26 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -310,18 +310,25 @@
if (!SecurityLog.isLoggingEnabled()) {
return;
}
- final PackageSetting ps = mInstallRequest.getScannedPackageSetting();
- if (ps == null) {
- return;
- }
- final String packageName = ps.getPackageName();
- final long versionCode = ps.getVersionCode();
- if (!mInstallRequest.isInstallReplace()) {
- SecurityLog.writeEvent(SecurityLog.TAG_PACKAGE_INSTALLED, packageName, versionCode,
- userId);
- } else {
- SecurityLog.writeEvent(SecurityLog.TAG_PACKAGE_UPDATED, packageName, versionCode,
- userId);
+ // TODO: Remove temp try-catch to avoid IllegalStateException. The reason is because
+ // the scan result is null for installExistingPackageAsUser(). Because it's installing
+ // a package that's already existing, there's no scanning or parsing involved
+ try {
+ final PackageSetting ps = mInstallRequest.getScannedPackageSetting();
+ if (ps == null) {
+ return;
+ }
+ final String packageName = ps.getPackageName();
+ final long versionCode = ps.getVersionCode();
+ if (!mInstallRequest.isInstallReplace()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_PACKAGE_INSTALLED, packageName, versionCode,
+ userId);
+ } else {
+ SecurityLog.writeEvent(SecurityLog.TAG_PACKAGE_UPDATED, packageName, versionCode,
+ userId);
+ }
+ } catch (IllegalStateException | NullPointerException e) {
+ // no-op
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 1884caf..2e60064 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -654,6 +654,16 @@
return (getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ public boolean isRequestLegacyExternalStorage() {
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+ != 0;
+ }
+
+ public boolean isUserDataFragile() {
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+ != 0;
+ }
+
public SigningDetails getSigningDetails() {
return signatures.mSigningDetails;
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 4eceb77..d16a812 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -54,6 +54,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
+import com.android.server.pm.PackageArchiver;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.pkg.AndroidPackage;
@@ -378,7 +379,6 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
-
if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0
&& state.isQuarantined()) {
ai.enabled = false;
@@ -402,6 +402,7 @@
ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
}
+ ai.isArchived = PackageArchiver.isArchived(state);
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index c44b8852..d6a7dc6 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -218,6 +218,7 @@
STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED);
}
private static final Set<String> NEARBY_DEVICES_PERMISSIONS = new ArraySet<>();
@@ -1490,12 +1491,9 @@
if (dir.isDirectory() && dir.canRead()) {
Collections.addAll(ret, dir.listFiles());
}
- // For IoT devices, we check the oem partition for default permissions for each app.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
+ dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
}
return ret.isEmpty() ? null : ret.toArray(new File[0]);
}
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 3296c1f..150ca9b 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -91,13 +91,14 @@
mHandler = context.getMainThreadHandler();
}
- void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
+ void startPackageOneTimeSession(@NonNull String packageName, int deviceId, long timeoutMillis,
long revokeAfterKilledDelayMillis) {
int uid;
try {
uid = mContext.getPackageManager().getPackageUid(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Unknown package name " + packageName, e);
+ Log.e(LOG_TAG,
+ "Unknown package name " + packageName + ", device ID " + deviceId, e);
return;
}
@@ -107,7 +108,7 @@
listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
return;
}
- listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+ listener = new PackageInactivityListener(uid, packageName, deviceId, timeoutMillis,
revokeAfterKilledDelayMillis);
mListeners.put(uid, listener);
}
@@ -159,6 +160,7 @@
private final int mUid;
private final @NonNull String mPackageName;
+ private final int mDeviceId;
private long mTimeout;
private long mRevokeAfterKilledDelay;
@@ -191,14 +193,15 @@
}
};
- private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
- long revokeAfterkilledDelay) {
+ private PackageInactivityListener(int uid, @NonNull String packageName, int deviceId,
+ long timeout, long revokeAfterkilledDelay) {
Log.i(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+ " killedDelay=" + revokeAfterkilledDelay);
mUid = uid;
mPackageName = packageName;
+ mDeviceId = deviceId;
mTimeout = timeout;
mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1
? DeviceConfig.getLong(
@@ -232,7 +235,8 @@
PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterKilledDelayMillis);
Log.v(LOG_TAG,
- "Updated params for " + mPackageName + ". timeout=" + mTimeout
+ "Updated params for " + mPackageName + ", device ID " + mDeviceId
+ + ". timeout=" + mTimeout
+ " killedDelay=" + mRevokeAfterKilledDelay);
updateUidState();
}
@@ -260,7 +264,7 @@
private void updateUidState(int state) {
Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
- + " state=" + state);
+ + " device ID=" + mDeviceId + ", state=" + state);
synchronized (mInnerLock) {
// Remove any pending inactivity callback
mHandler.removeCallbacksAndMessages(mToken);
@@ -283,7 +287,7 @@
if (DEBUG) {
Log.d(LOG_TAG, "No longer gone after delayed revocation. "
+ "Rechecking for " + mPackageName + " (" + mUid
- + ").");
+ + "). device ID " + mDeviceId);
}
updateUidState(currentState);
}, mToken, mRevokeAfterKilledDelay);
@@ -292,7 +296,7 @@
if (mTimerStart == TIMER_INACTIVE) {
if (DEBUG) {
Log.d(LOG_TAG, "Start the timer for "
- + mPackageName + " (" + mUid + ").");
+ + mPackageName + " (" + mUid + "). device ID " + mDeviceId);
}
mTimerStart = System.currentTimeMillis();
setAlarmLocked();
@@ -329,7 +333,8 @@
}
if (DEBUG) {
- Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Scheduling alarm for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
long revokeTime = mTimerStart + mTimeout;
if (revokeTime > System.currentTimeMillis()) {
@@ -349,7 +354,8 @@
private void cancelAlarmLocked() {
if (mIsAlarmSet) {
if (DEBUG) {
- Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Canceling alarm for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
mAlarmManager.cancel(this);
mIsAlarmSet = false;
@@ -366,17 +372,17 @@
}
if (DEBUG) {
Log.d(LOG_TAG, "onPackageInactiveLocked stack trace for "
- + mPackageName + " (" + mUid + ").", new RuntimeException());
+ + mPackageName + " (" + mUid + "). device ID " + mDeviceId,
+ new RuntimeException());
}
mIsFinished = true;
cancelAlarmLocked();
mHandler.post(
() -> {
Log.i(LOG_TAG, "One time session expired for "
- + mPackageName + " (" + mUid + ").");
-
+ + mPackageName + " (" + mUid + "). deviceID " + mDeviceId);
mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
- mPackageName);
+ mPackageName, mDeviceId);
});
try {
mIActivityManager.unregisterUidObserver(mObserver);
@@ -391,7 +397,8 @@
@Override
public void onAlarm() {
if (DEBUG) {
- Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ").");
+ Log.d(LOG_TAG, "Alarm received for " + mPackageName + " (" + mUid + ")."
+ + " device ID " + mDeviceId);
}
synchronized (mInnerLock) {
if (!mIsAlarmSet) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 44eb28f..9610d05 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -404,15 +404,15 @@
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
@Override
- public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
- long timeoutMillis, long revokeAfterKilledDelayMillis) {
+ public void startOneTimePermissionSession(String packageName, int deviceId,
+ @UserIdInt int userId, long timeoutMillis, long revokeAfterKilledDelayMillis) {
startOneTimePermissionSession_enforcePermission();
Objects.requireNonNull(packageName);
final long token = Binder.clearCallingIdentity();
try {
getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
- timeoutMillis, revokeAfterKilledDelayMillis);
+ deviceId, timeoutMillis, revokeAfterKilledDelayMillis);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 12795c6..a76a7ce0 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -393,29 +393,27 @@
}
public @NonNull PackageUserStateImpl setDisabledComponents(@Nullable ArraySet<String> value) {
- if (value == null) {
- return this;
- }
if (mDisabledComponentsWatched == null) {
mDisabledComponentsWatched = new WatchedArraySet<>();
mDisabledComponentsWatched.registerObserver(mSnapshot);
}
mDisabledComponentsWatched.clear();
- mDisabledComponentsWatched.addAll(value);
+ if (value != null) {
+ mDisabledComponentsWatched.addAll(value);
+ }
onChanged();
return this;
}
public @NonNull PackageUserStateImpl setEnabledComponents(@Nullable ArraySet<String> value) {
- if (value == null) {
- return this;
- }
if (mEnabledComponentsWatched == null) {
mEnabledComponentsWatched = new WatchedArraySet<>();
mEnabledComponentsWatched.registerObserver(mSnapshot);
}
mEnabledComponentsWatched.clear();
- mEnabledComponentsWatched.addAll(value);
+ if (value != null) {
+ mEnabledComponentsWatched.addAll(value);
+ }
onChanged();
return this;
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 812e228..f14941b 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -28,6 +28,7 @@
import static android.os.Build.VERSION_CODES.DONUT;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
import android.annotation.AnyRes;
@@ -509,24 +510,33 @@
/* Set the global "on SD card" flag */
.setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+ var archivedPackage = lite.getArchivedPackage();
+ if (archivedPackage == null) {
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "archivePackage is missing");
+ }
+
// parseBaseAppBasicFlags
pkg
// Default true
- .setBackupAllowed(lite.isBackupAllowed())
+ .setBackupAllowed(true)
.setClearUserDataAllowed(true)
- .setClearUserDataOnFailedRestoreAllowed(
- lite.isClearUserDataOnFailedRestoreAllowed())
+ .setClearUserDataOnFailedRestoreAllowed(true)
.setAllowNativeHeapPointerTagging(true)
.setEnabled(true)
.setExtractNativeLibrariesRequested(true)
// targetSdkVersion gated
.setAllowAudioPlaybackCapture(targetSdk >= Build.VERSION_CODES.Q)
.setHardwareAccelerated(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- .setRequestLegacyExternalStorage(lite.isRequestLegacyExternalStorage())
+ .setRequestLegacyExternalStorage(
+ XmlUtils.convertValueToBoolean(archivedPackage.requestLegacyExternalStorage,
+ targetSdk < Build.VERSION_CODES.Q))
.setCleartextTrafficAllowed(targetSdk < Build.VERSION_CODES.P)
// Default false
- .setDefaultToDeviceProtectedStorage(lite.isDefaultToDeviceProtectedStorage())
- .setUserDataFragile(lite.isUserDataFragile())
+ .setDefaultToDeviceProtectedStorage(XmlUtils.convertValueToBoolean(
+ archivedPackage.defaultToDeviceProtectedStorage, false))
+ .setUserDataFragile(
+ XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false))
// Ints
.setCategory(ApplicationInfo.CATEGORY_UNDEFINED)
// Floats Default 0f
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4d38239..dc75a98 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -438,7 +438,6 @@
boolean mPreloadedRecentApps;
final Object mServiceAcquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
- SearchManager mSearchManager;
AccessibilityManager mAccessibilityManager;
AccessibilityManagerInternal mAccessibilityManagerInternal;
BurnInProtectionHelper mBurnInProtectionHelper;
@@ -1984,6 +1983,7 @@
public void run() {
if (mPendingHomeKeyEvent != null) {
handleShortPressOnHome(mPendingHomeKeyEvent);
+ mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
}
}
@@ -2028,7 +2028,7 @@
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
|| mPictureInPictureVisible) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
- mPendingHomeKeyEvent = event;
+ mPendingHomeKeyEvent = KeyEvent.obtain(event);
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return true;
@@ -2036,7 +2036,11 @@
}
// Post to main thread to avoid blocking input pipeline.
- mHandler.post(() -> handleShortPressOnHome(event));
+ final KeyEvent shortPressEvent = KeyEvent.obtain(event);
+ mHandler.post(() -> {
+ handleShortPressOnHome(shortPressEvent);
+ shortPressEvent.recycle();
+ });
return true;
}
@@ -2063,9 +2067,14 @@
if (repeatCount == 0) {
mHomePressed = true;
if (mPendingHomeKeyEvent != null) {
+ mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- mHandler.post(() -> handleDoubleTapOnHome(event));
+ final KeyEvent doublePressEvent = KeyEvent.obtain(event);
+ mHandler.post(() -> {
+ handleDoubleTapOnHome(doublePressEvent);
+ doublePressEvent.recycle();
+ });
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -2075,7 +2084,11 @@
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
- mHandler.post(() -> handleLongPressOnHome(event));
+ final KeyEvent longPressEvent = KeyEvent.obtain(event);
+ mHandler.post(() -> {
+ handleLongPressOnHome(longPressEvent);
+ longPressEvent.recycle();
+ });
}
}
return true;
@@ -2217,7 +2230,6 @@
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
- mSearchManager = mContext.getSystemService(SearchManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -3974,15 +3986,15 @@
*/
private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
boolean notifyOccluded) {
+ int redoLayout = 0;
if (notifyOccluded) {
- final int redoLayout = applyKeyguardOcclusionChange();
- if (redoLayout != 0) return redoLayout;
+ redoLayout = applyKeyguardOcclusionChange();
}
if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis());
}
- return 0;
+ return redoLayout;
}
// There are several different flavors of "assistant" that can be launched from
@@ -4009,8 +4021,9 @@
args.putLong(Intent.EXTRA_TIME, eventTime);
args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
- if (mSearchManager != null) {
- mSearchManager.launchAssist(args);
+ SearchManager searchManager = mContext.getSystemService(SearchManager.class);
+ if (searchManager != null) {
+ searchManager.launchAssist(args);
} else {
// Fallback to status bar if search manager doesn't exist (e.g. on wear).
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -4773,7 +4786,7 @@
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
- if (down && mStylusButtonsEnabled) {
+ if (mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING
index 673456f..29d52ff 100644
--- a/services/core/java/com/android/server/security/TEST_MAPPING
+++ b/services/core/java/com/android/server/security/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSecurityTestCases",
"options": [
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index c526016..a5c0fb3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -1454,6 +1454,7 @@
boolean hasDesiredDemuxCap = request.desiredFilterTypes
!= DemuxFilterMainType.UNDEFINED;
int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+ int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
for (DemuxResource dr : getDemuxResources().values()) {
if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
if (!dr.isInUse()) {
@@ -1476,12 +1477,18 @@
currentLowestPriority = priority;
isRequestFromSameProcess = (requestClient.getProcessId()
== (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+ // reset the smallest caps when lower priority resource is found
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
shouldUpdate = true;
- }
- // update smallest caps
- if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
- smallestNumOfSupportedCaps = numOfSupportedCaps;
- shouldUpdate = true;
+ } else {
+ // This is the case when the priority is the same as previously found
+ // one. Update smallest caps when priority.
+ if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+ shouldUpdate = true;
+ }
}
if (shouldUpdate) {
inUseLowestPriorityDrHandle = dr.getHandle();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3c2d0fc..4c525e9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -256,6 +256,15 @@
|| event != CLOSE_WRITE // includes the MOVED_TO case
|| wallpaper.imageWallpaperPending;
+ if (isMigration) {
+ // When separate lock screen engine is supported, migration will be handled by
+ // WallpaperDestinationChangeHandler.
+ return;
+ }
+ if (!(sysWallpaperChanged || lockWallpaperChanged)) {
+ return;
+ }
+
if (DEBUG) {
Slog.v(TAG, "Wallpaper file change: evt=" + event
+ " path=" + path
@@ -270,15 +279,6 @@
+ " needsUpdate=" + needsUpdate);
}
- if (isMigration) {
- // When separate lock screen engine is supported, migration will be handled by
- // WallpaperDestinationChangeHandler.
- return;
- }
- if (!(sysWallpaperChanged || lockWallpaperChanged)) {
- return;
- }
-
int notifyColorsWhich = 0;
synchronized (mLock) {
notifyCallbacksLocked(wallpaper);
@@ -3494,15 +3494,24 @@
}
}
+ /**
+ * Determines if the given component name is the default component. Note: a null name can be
+ * used to represent the default component.
+ * @param name The component name to check.
+ * @return True if the component name matches the default wallpaper component.
+ */
+ private boolean isDefaultComponent(ComponentName name) {
+ return name == null || name.equals(mDefaultWallpaperComponent);
+ }
+
private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
if (wallpaper.connection != null) {
- if (wallpaper.wallpaperComponent == null) {
- if (componentName == null) {
- if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
- // Still using default wallpaper.
- return true;
- }
- } else if (wallpaper.wallpaperComponent.equals(componentName)) {
+ final ComponentName wallpaperName = wallpaper.wallpaperComponent;
+ if (isDefaultComponent(componentName) && isDefaultComponent(wallpaperName)) {
+ if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
+ // Still using default wallpaper.
+ return true;
+ } else if (wallpaperName != null && wallpaperName.equals(componentName)) {
// Changing to same wallpaper.
if (DEBUG) Slog.v(TAG, "same wallpaper");
return true;
@@ -3519,6 +3528,9 @@
// Has the component changed?
if (!force && changingToSame(componentName, wallpaper)) {
try {
+ if (DEBUG_LIVE) {
+ Slog.v(TAG, "Changing to the same component, ignoring");
+ }
if (reply != null) reply.sendResult(null);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to send callback", e);
@@ -3737,7 +3749,11 @@
mContext.getMainThreadHandler().removeCallbacks(
wallpaper.connection.mTryToRebindRunnable);
- mContext.unbindService(wallpaper.connection);
+ try {
+ mContext.unbindService(wallpaper.connection);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Error unbinding wallpaper when detaching", e);
+ }
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) {
mLastWallpaper = null;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 4c9ec9d..3c8e630 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1018,8 +1018,7 @@
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(
- r.app.getThread(), r.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
transaction.addCallback(EnterPipRequestedItem.obtain(r.token));
mService.getLifecycleManager().scheduleTransaction(transaction);
return true;
@@ -1040,8 +1039,7 @@
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(
- r.app.getThread(), r.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
transaction.addCallback(PipStateTransactionItem.obtain(r.token, pipState));
mService.getLifecycleManager().scheduleTransaction(transaction);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dca2b6f..a0c7870 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -565,8 +565,8 @@
boolean forceNewConfig; // force re-create with new config next time
boolean supportsEnterPipOnTaskSwitch; // This flag is set by the system to indicate that the
// activity can enter picture in picture while pausing (only when switching to another task)
+ // The PiP params used when deferring the entering of picture-in-picture.
PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
- // The PiP params used when deferring the entering of picture-in-picture.
boolean shouldDockBigOverlays;
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last launch of this activity
@@ -580,7 +580,7 @@
IBinder mRequestedLaunchingTaskFragmentToken;
// Tracking splash screen status from previous activity
- boolean mSplashScreenStyleSolidColor = false;
+ boolean mAllowIconSplashScreen = true;
boolean mPauseSchedulePendingForPip = false;
@@ -1442,7 +1442,7 @@
+ "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
config);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
MoveToDisplayItem.obtain(token, displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1459,7 +1459,7 @@
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
+ "config: %s", this, config);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
ActivityConfigurationChangeItem.obtain(token, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1480,7 +1480,7 @@
ProtoLog.v(WM_DEBUG_STATES, "Sending position change to %s, onTop: %b",
this, onTop);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
TopResumedActivityChangeItem.obtain(token, onTop));
} catch (RemoteException e) {
// If process died, whatever.
@@ -1732,6 +1732,16 @@
mAnimatingActivityRegistry = registry;
}
+ boolean canAutoEnterPip() {
+ // beforeStopping=false since the actual pip-ing will take place after startPausing()
+ final boolean activityCanPip = checkEnterPictureInPictureState(
+ "startActivityUnchecked", false /* beforeStopping */);
+
+ // check if this activity is about to auto-enter pip
+ return activityCanPip && pictureInPictureArgs != null
+ && pictureInPictureArgs.isAutoEnterEnabled();
+ }
+
/**
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
* {@link #getTask()} is set before this is called.
@@ -2398,8 +2408,7 @@
@VisibleForTesting
boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
- boolean activityCreated, boolean isSimple,
- boolean activityAllDrawn) {
+ boolean activityCreated, boolean allowIcon, boolean activityAllDrawn) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
if (!okToDisplay()) {
@@ -2434,8 +2443,8 @@
final int typeParameter = StartingSurfaceController
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
- type, packageName, mUserId);
+ allowTaskSnapshot, activityCreated, allowIcon, useLegacy,
+ activityAllDrawn, type, packageName, mUserId);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
@@ -2727,7 +2736,7 @@
}
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
TransferSplashScreenViewStateItem.obtain(token, parcelable,
windowAnimationLeash));
scheduleTransferSplashScreenTimeout();
@@ -2878,7 +2887,7 @@
final boolean animate;
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
- || mTransitionController.inCollectingTransition(startingWindow)) {
+ || mTransitionController.isCollecting(this)) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
@@ -3894,7 +3903,7 @@
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
DestroyActivityItem.obtain(token, finishing, configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process has crashed, our death
@@ -4800,9 +4809,9 @@
if (isState(RESUMED) && attachedToProcess()) {
try {
- final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ final ArrayList<ResultInfo> list = new ArrayList<>();
list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
ActivityResultItem.obtain(token, list));
return;
} catch (Exception e) {
@@ -4813,7 +4822,7 @@
// Schedule sending results now for Media Projection setup.
if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
STOPPING, STOPPED)) {
- final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
+ final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
// Build result to be returned immediately.
transaction.addCallback(ActivityResultItem.obtain(
token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data))));
@@ -4908,7 +4917,7 @@
// Making sure the client state is RESUMED after transaction completed and doing
// so only if activity is currently RESUMED. Otherwise, client may have extra
// life-cycle calls to RESUMED (and PAUSED later).
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
NewIntentItem.obtain(token, ar, mState == RESUMED));
unsent = false;
} catch (RemoteException e) {
@@ -6143,7 +6152,7 @@
EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this),
shortComponentName, "userLeaving=false", "make-active");
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */, mAutoEnteringPip));
} catch (Exception e) {
@@ -6156,7 +6165,7 @@
setState(STARTED, "makeActiveIfNeeded");
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
StartActivityItem.obtain(token, takeOptions()));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
@@ -6454,7 +6463,7 @@
}
EventLogTags.writeWmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
StopActivityItem.obtain(token, configChangeFlags));
mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
@@ -6737,7 +6746,7 @@
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
- mSplashScreenStyleSolidColor = true;
+ mAllowIconSplashScreen = false;
if (mStartingWindow != null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
@@ -6786,7 +6795,7 @@
void onStartingWindowDrawn() {
boolean wasTaskVisible = false;
if (task != null) {
- mSplashScreenStyleSolidColor = true;
+ mAllowIconSplashScreen = false;
wasTaskVisible = !setTaskHasBeenVisible();
}
@@ -7309,52 +7318,69 @@
}
return false;
}
- private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
+
+ /**
+ * Checks whether an icon splash screen can be used in the starting window based on the
+ * preference in the {@code options} and this activity's theme, giving higher priority to the
+ * {@code options}'s preference.
+ *
+ * When no preference is specified, a default behaviour is defined:
+ * - if the activity is started from the home or shell app, an icon can be used
+ * - if the activity is started from SystemUI, an icon should not be used
+ * - if there is a launching activity, use its preference
+ * - if none of the above is met, only use an icon when the activity is started for the first
+ * time from a System app
+ *
+ * The returned value is sent to WmShell, which will make the final decision on what splash
+ * screen type will be used.
+ *
+ * @return true if an icon can be used in the splash screen
+ * false when an icon should not be used in the splash screen
+ */
+ private boolean canUseIconSplashScreen(ActivityRecord sourceRecord,
boolean startActivity, ActivityOptions options, int resolvedTheme) {
if (sourceRecord == null && !startActivity) {
- // Use simple style if this activity is not top activity. This could happen when adding
- // a splash screen window to the warm start activity which is re-create because top is
- // finishing.
+ // Shouldn't use an icon if this activity is not top activity. This could happen when
+ // adding a splash screen window to the warm start activity which is re-create because
+ // top is finishing.
final ActivityRecord above = task.getActivityAbove(this);
if (above != null) {
- return true;
+ return false;
}
}
// setSplashScreenStyle decide in priority of windowSplashScreenBehavior.
- if (options != null) {
- final int optionsStyle = options.getSplashScreenStyle();
- if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
- return true;
- } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
- || isIconStylePreferred(resolvedTheme)) {
- return false;
- }
- // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
- // not specified in the ActivityOptions.
- if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
- || launchedFromUid == Process.SHELL_UID) {
- return false;
- } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
- return true;
- }
- } else if (isIconStylePreferred(resolvedTheme)) {
+ final int optionsStyle = options != null ? options.getSplashScreenStyle() :
+ SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
+ if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
return false;
- }
- if (sourceRecord == null) {
- sourceRecord = searchCandidateLaunchingActivity();
+ } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
+ || isIconStylePreferred(resolvedTheme)) {
+ return true;
}
- if (sourceRecord != null && !sourceRecord.isActivityTypeHome()) {
- return sourceRecord.mSplashScreenStyleSolidColor;
- }
+ // Choose the default behavior when neither the ActivityRecord nor the activity theme have
+ // specified a splash screen style.
- // If this activity was launched from Launcher or System for first start, never use a
- // solid color splash screen.
- // Need to check sourceRecord before in case this activity is launched from service.
- return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
- || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
- || launchedFromUid == Process.SHELL_UID);
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
+ return true;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return false;
+ } else {
+ // Need to check sourceRecord in case this activity is launched from a service or a
+ // trampoline activity.
+ if (sourceRecord == null) {
+ sourceRecord = searchCandidateLaunchingActivity();
+ }
+
+ if (sourceRecord != null) {
+ return sourceRecord.mAllowIconSplashScreen;
+ }
+
+ // Use an icon if the activity was launched from System for the first start.
+ // Otherwise, can't use an icon splash screen.
+ return mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM && startActivity;
+ }
}
private int getSplashscreenTheme(ActivityOptions options) {
@@ -7417,7 +7443,7 @@
final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
- mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
+ mAllowIconSplashScreen = canUseIconSplashScreen(sourceRecord, startActivity,
startOptions, resolvedTheme);
final boolean activityCreated =
@@ -7429,7 +7455,7 @@
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
prev, newTask || newSingleActivity, taskSwitch, processRunning,
- allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);
+ allowTaskSnapshot(), activityCreated, mAllowIconSplashScreen, allDrawn);
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
Slog.d(TAG, "Scheduled starting window for " + this);
}
@@ -9180,7 +9206,13 @@
getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED
? newParentConfig.windowConfiguration.getWindowingMode()
: getRequestedOverrideWindowingMode();
- if (getWindowingMode() != projectedWindowingMode) {
+ if (getWindowingMode() != projectedWindowingMode
+ // Do not collect a pip activity about to enter pinned mode
+ // as a part of WindowOrganizerController#finishTransition().
+ // If not checked the activity might be collected for the wrong transition,
+ // such as a TRANSIT_OPEN transition requested right after TRANSIT_PIP.
+ && !(mWaitForEnteringPinnedMode
+ && mTransitionController.inFinishingTransition(this))) {
mTransitionController.collect(this);
}
}
@@ -9869,7 +9901,6 @@
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
forceNewConfig = false;
- startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
pendingResults, pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
@@ -9882,15 +9913,16 @@
} else {
lifecycleItem = PauseActivityItem.obtain(token);
}
- final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
+ final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ startRelaunching();
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
} catch (RemoteException e) {
- ProtoLog.i(WM_DEBUG_STATES, "Relaunch failed %s", e);
+ Slog.w(TAG, "Failed to relaunch " + this + ": " + e);
}
if (andResume) {
@@ -9976,7 +10008,7 @@
// The process will be killed until the activity reports stopped with saved state (see
// {@link ActivityTaskManagerService.activityStopped}).
try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
StopActivityItem.obtain(token, 0 /* configChanges */));
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown during restart " + this, e);
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index a5b1132..25c42b4 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -33,9 +33,7 @@
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
@@ -240,11 +238,6 @@
getInterceptorInfo(null /* clearOptionsAnimation */);
for (int i = 0; i < callbacks.size(); i++) {
- final int orderId = callbacks.keyAt(i);
- if (!shouldInterceptActivityLaunch(orderId, interceptorInfo)) {
- continue;
- }
-
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
final ActivityInterceptResult interceptResult = callback.onInterceptActivityLaunch(
interceptorInfo);
@@ -543,11 +536,6 @@
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(
r::clearOptionsAnimationForSiblings);
for (int i = 0; i < callbacks.size(); i++) {
- final int orderId = callbacks.keyAt(i);
- if (!shouldNotifyOnActivityLaunch(orderId, info)) {
- continue;
- }
-
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
callback.onActivityLaunched(taskInfo, r.info, info);
}
@@ -565,21 +553,4 @@
.build();
}
- private boolean shouldInterceptActivityLaunch(
- @ActivityInterceptorCallback.OrderedId int orderId,
- @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
- if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
- return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
- }
- return true;
- }
-
- private boolean shouldNotifyOnActivityLaunch(
- @ActivityInterceptorCallback.OrderedId int orderId,
- @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
- if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
- return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
- }
- return true;
- }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6999c6a..237bc92 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -313,6 +313,8 @@
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
+ private static final String ENABLE_PIP2_IMPLEMENTATION =
+ "persist.wm.debug.enable_pip2_implementation";
static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -1841,8 +1843,12 @@
RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()");
-
- return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
/**
@@ -3613,6 +3619,28 @@
}
}
+ /**
+ * Prepare to enter PiP mode after {@link TransitionController#requestStartTransition}.
+ *
+ * @param r activity auto entering pip
+ * @return true if the activity is about to auto-enter pip or is already in pip mode.
+ */
+ boolean prepareAutoEnterPictureAndPictureMode(ActivityRecord r) {
+ // If the activity is already in picture in picture mode, then just return early
+ if (r.inPinnedWindowingMode()) {
+ return true;
+ }
+
+ if (r.canAutoEnterPip() && getTransitionController().getCollectingTransition() != null) {
+ // This will be used later to construct TransitionRequestInfo for Shell to resolve.
+ // It will also be passed into a direct moveActivityToPinnedRootTask() call via
+ // startTransition()
+ getTransitionController().getCollectingTransition().setPipActivity(r);
+ return true;
+ }
+ return false;
+ }
+
boolean enterPictureInPictureMode(@NonNull ActivityRecord r,
@NonNull PictureInPictureParams params, boolean fromClient) {
return enterPictureInPictureMode(r, params, fromClient, false /* isAutoEnter */);
@@ -6392,6 +6420,11 @@
if (!restarting && hasVisibleActivities) {
deferWindowLayout();
try {
+ final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topTask != null
+ && topTask.topRunningActivity(true /* focusableOnly */) == null) {
+ topTask.adjustFocusToNextFocusableTask("handleAppDied");
+ }
if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) {
// If there was nothing to resume, and we are not already restarting
// this process, but there is a visible activity that is hosted by the
@@ -7183,4 +7216,8 @@
ActivityTaskManagerService.this.unregisterTaskStackListener(listener);
}
}
+
+ static boolean isPip2ExperimentEnabled() {
+ return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e523119..901975b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -795,17 +795,22 @@
return false;
}
- // Try pausing the existing resumed activity in the same TaskFragment if any.
- final TaskFragment taskFragment = r.getTaskFragment();
- if (taskFragment != null && taskFragment.getResumedActivity() != null) {
- if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
+ final Task task = r.getTask();
+ if (andResume) {
+ // Try pausing the existing resumed activity in the Task if any.
+ if (task.pauseActivityIfNeeded(r, "realStart")) {
return false;
}
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null && taskFragment.getResumedActivity() != null) {
+ if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r,
+ "realStart")) {
+ return false;
+ }
+ }
}
- final Task task = r.getTask();
final Task rootTask = task.getRootTask();
-
beginDeferResume();
// The LaunchActivityItem also contains process configuration, so the configuration change
// from WindowProcessController#setProcess can be deferred. The major reason is that if
@@ -923,7 +928,7 @@
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(
- proc.getThread(), r.token);
+ proc.getThread());
final boolean isTransitionForward = r.isTransitionForward();
final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 7430f0f..ef31837 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -18,11 +18,10 @@
import android.annotation.NonNull;
import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
-import android.app.servertransaction.ActivityLifecycleItem;
import android.os.Binder;
-import android.os.IBinder;
import android.os.RemoteException;
/**
@@ -36,13 +35,13 @@
// TODO(lifecycler): Use object pools for transactions and transaction items.
/**
- * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
+ * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
* @param transaction A sequence of client transaction items.
* @throws RemoteException
*
* @see ClientTransaction
*/
- void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+ void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
transaction.schedule();
if (!(client instanceof Binder)) {
@@ -54,75 +53,22 @@
}
/**
- * Schedule a single lifecycle request or callback to client activity.
+ * Schedules a single transaction item, either a callback or a lifecycle request, delivery to
+ * client application.
* @param client Target client.
- * @param activityToken Target activity token.
- * @param stateRequest A request to move target activity to a desired lifecycle state.
- * @throws RemoteException
- *
- * @see ClientTransactionItem
- */
- void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
- @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
- final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
- stateRequest);
- scheduleTransaction(clientTransaction);
- }
-
- /**
- * Schedule a single callback delivery to client activity.
- * @param client Target client.
- * @param activityToken Target activity token.
- * @param callback A request to deliver a callback.
- * @throws RemoteException
- *
- * @see ClientTransactionItem
- */
- void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
- @NonNull ClientTransactionItem callback) throws RemoteException {
- final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
- callback);
- scheduleTransaction(clientTransaction);
- }
-
- /**
- * Schedule a single callback delivery to client application.
- * @param client Target client.
- * @param callback A request to deliver a callback.
+ * @param transactionItem A transaction item to deliver a message.
* @throws RemoteException
*
* @see ClientTransactionItem
*/
void scheduleTransaction(@NonNull IApplicationThread client,
- @NonNull ClientTransactionItem callback) throws RemoteException {
- final ClientTransaction clientTransaction = transactionWithCallback(client,
- null /* activityToken */, callback);
+ @NonNull ClientTransactionItem transactionItem) throws RemoteException {
+ final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
+ if (transactionItem instanceof ActivityLifecycleItem) {
+ clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
+ } else {
+ clientTransaction.addCallback(transactionItem);
+ }
scheduleTransaction(clientTransaction);
}
-
- /**
- * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
- *
- * @see ClientTransaction
- * @see ClientTransactionItem
- */
- private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
- @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
- clientTransaction.setLifecycleStateRequest(stateRequest);
- return clientTransaction;
- }
-
- /**
- * @return A new instance of {@link ClientTransaction} with a single callback invocation.
- *
- * @see ClientTransaction
- * @see ClientTransactionItem
- */
- private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
- IBinder activityToken, @NonNull ClientTransactionItem callback) {
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
- clientTransaction.addCallback(callback);
- return clientTransaction;
- }
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 7cd07d6..06448d0 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -33,7 +33,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.DeviceConfig;
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;
import android.view.Display;
@@ -48,11 +47,6 @@
final class ContentRecorder implements WindowContainerListener {
/**
- * The key for accessing the device config that controls if task recording is supported.
- */
- @VisibleForTesting static final String KEY_RECORD_TASK_FEATURE = "record_task_content";
-
- /**
* The display content this class is handling recording for.
*/
@NonNull
@@ -83,6 +77,11 @@
@Nullable private Rect mLastRecordedBounds = null;
/**
+ * The last size of the surface mirrored out to.
+ */
+ @Nullable private Point mLastConsumingSurfaceSize = new Point(0, 0);
+
+ /**
* The last configuration orientation.
*/
@Configuration.Orientation
@@ -141,60 +140,64 @@
*/
void onConfigurationChanged(@Configuration.Orientation int lastOrientation) {
// Update surface for MediaProjection, if this DisplayContent is being used for recording.
- if (isCurrentlyRecording() && mLastRecordedBounds != null) {
- // Recording has already begun, but update recording since the display is now on.
- if (mRecordedWindowContainer == null) {
+ if (!isCurrentlyRecording() || mLastRecordedBounds == null) {
+ return;
+ }
+
+ // Recording has already begun, but update recording since the display is now on.
+ if (mRecordedWindowContainer == null) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unexpectedly null window container; unable to update "
+ + "recording for display %d",
+ mDisplayContent.getDisplayId());
+ return;
+ }
+
+ // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
+ // inaccurate.
+ if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+ final Task capturedTask = mRecordedWindowContainer.asTask();
+ if (capturedTask.inPinnedWindowingMode()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unexpectedly null window container; unable to update "
- + "recording for display %d",
+ "Content Recording: Display %d was already recording, but "
+ + "pause capture since the task is in PIP",
mDisplayContent.getDisplayId());
+ pauseRecording();
return;
}
+ }
- // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
- // inaccurate.
- if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
- final Task capturedTask = mRecordedWindowContainer.asTask();
- if (capturedTask.inPinnedWindowingMode()) {
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d was already recording, but "
- + "pause capture since the task is in PIP",
- mDisplayContent.getDisplayId());
- pauseRecording();
- return;
- }
- }
-
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d was already recording, so apply "
- + "transformations if necessary",
- mDisplayContent.getDisplayId());
- // Retrieve the size of the region to record, and continue with the update
- // if the bounds or orientation has changed.
- final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
- @Configuration.Orientation int recordedContentOrientation =
- mRecordedWindowContainer.getConfiguration().orientation;
- if (!mLastRecordedBounds.equals(recordedContentBounds)
- || lastOrientation != recordedContentOrientation) {
- Point surfaceSize = fetchSurfaceSizeIfPresent();
- if (surfaceSize != null) {
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Going ahead with updating recording for display "
- + "%d to new bounds %s and/or orientation %d.",
- mDisplayContent.getDisplayId(), recordedContentBounds,
- recordedContentOrientation);
- updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
- recordedContentBounds, surfaceSize);
- } else {
- // If the surface removed, do nothing. We will handle this via onDisplayChanged
- // (the display will be off if the surface is removed).
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to update recording for display %d to new "
- + "bounds %s and/or orientation %d, since the surface is not "
- + "available.",
- mDisplayContent.getDisplayId(), recordedContentBounds,
- recordedContentOrientation);
- }
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Display %d was already recording, so apply "
+ + "transformations if necessary",
+ mDisplayContent.getDisplayId());
+ // Retrieve the size of the region to record, and continue with the update
+ // if the bounds or orientation has changed.
+ final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
+ @Configuration.Orientation int recordedContentOrientation =
+ mRecordedWindowContainer.getConfiguration().orientation;
+ final Point surfaceSize = fetchSurfaceSizeIfPresent();
+ if (!mLastRecordedBounds.equals(recordedContentBounds)
+ || lastOrientation != recordedContentOrientation
+ || !mLastConsumingSurfaceSize.equals(surfaceSize)) {
+ if (surfaceSize != null) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Going ahead with updating recording for display "
+ + "%d to new bounds %s and/or orientation %d and/or surface "
+ + "size %s",
+ mDisplayContent.getDisplayId(), recordedContentBounds,
+ recordedContentOrientation, surfaceSize);
+ updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
+ recordedContentBounds, surfaceSize);
+ } else {
+ // If the surface removed, do nothing. We will handle this via onDisplayChanged
+ // (the display will be off if the surface is removed).
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unable to update recording for display %d to new "
+ + "bounds %s and/or orientation %d and/or surface size %s, "
+ + "since the surface is not available.",
+ mDisplayContent.getDisplayId(), recordedContentBounds,
+ recordedContentOrientation, surfaceSize);
}
}
}
@@ -402,14 +405,6 @@
// TODO(206461622) Migrate to using the RootDisplayArea
return dc;
case RECORD_CONTENT_TASK:
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_RECORD_TASK_FEATURE, false)) {
- handleStartRecordingFailed();
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to record task since feature is disabled %d",
- mDisplayContent.getDisplayId());
- return null;
- }
// Given the WindowToken of the region to record, retrieve the associated
// SurfaceControl.
if (tokenToRecord == null) {
@@ -498,10 +493,13 @@
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x "
- + "%d for display %d",
+ "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka "
+ + "recorded content size) %d x %d for display %d; display has size %d x "
+ + "%d; surface has size %d x %d",
shiftedX, shiftedY, scale, recordedContentBounds.width(),
- recordedContentBounds.height(), mDisplayContent.getDisplayId());
+ recordedContentBounds.height(), mDisplayContent.getDisplayId(),
+ mDisplayContent.getConfiguration().screenWidthDp,
+ mDisplayContent.getConfiguration().screenHeightDp, surfaceSize.x, surfaceSize.y);
transaction
// Crop the area to capture to exclude the 'extra' wallpaper that is used
@@ -515,6 +513,8 @@
// the content will no longer be centered in the output surface.
.setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */);
mLastRecordedBounds = new Rect(recordedContentBounds);
+ mLastConsumingSurfaceSize.x = surfaceSize.x;
+ mLastConsumingSurfaceSize.y = surfaceSize.y;
// Request to notify the client about the resize.
mMediaProjectionManager.notifyActiveProjectionCapturedContentResized(
mLastRecordedBounds.width(), mLastRecordedBounds.height());
@@ -523,6 +523,7 @@
/**
* Returns a non-null {@link Point} if the surface is present, or null otherwise
*/
+ @Nullable
private Point fetchSurfaceSizeIfPresent() {
// Retrieve the default size of the surface the app provided to
// MediaProjection#createVirtualDisplay. Note the app is the consumer of the surface,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 996d666..ca42400 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2160,6 +2160,16 @@
}
/**
+ * @see DisplayWindowPolicyController#getCustomHomeComponent() ()
+ */
+ @Nullable ComponentName getCustomHomeComponent() {
+ if (!supportsSystemDecorations() || mDwpcHelper == null) {
+ return null;
+ }
+ return mDwpcHelper.getCustomHomeComponent();
+ }
+
+ /**
* Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked}
* (if it returned {@code true}) to actually finish the rotation.
*
@@ -6040,8 +6050,9 @@
mOffTokenAcquirer.release(mDisplayId);
}
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Display %d state is now (%d), so update recording?",
- mDisplayId, displayState);
+ "Content Recording: Display %d state was (%d), is now (%d), so update "
+ + "recording?",
+ mDisplayId, lastDisplayState, displayState);
if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start recording.
// If state is off due to surface being removed, then stop recording.
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index c4ed0dd..534cdc2 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -227,7 +227,7 @@
"Refreshing activity for camera compatibility treatment, "
+ "activityRecord=%s", activity);
final ClientTransaction transaction = ClientTransaction.obtain(
- activity.app.getThread(), activity.token);
+ activity.app.getThread());
transaction.addCallback(RefreshCallbackItem.obtain(activity.token,
cycleThroughStop ? ON_STOP : ON_PAUSE));
transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(activity.token,
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
index 6b33746..e0d69b0 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Process;
@@ -200,6 +201,17 @@
return mDisplayWindowPolicyController.isEnteringPipAllowed(uid);
}
+ /**
+ * @see DisplayWindowPolicyController#getCustomHomeComponent
+ */
+ @Nullable
+ public ComponentName getCustomHomeComponent() {
+ if (mDisplayWindowPolicyController == null) {
+ return null;
+ }
+ return mDisplayWindowPolicyController.getCustomHomeComponent();
+ }
+
void dump(String prefix, PrintWriter pw) {
if (mDisplayWindowPolicyController != null) {
pw.println();
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 8519e4d..0f1a105 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -21,6 +21,7 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
@@ -32,6 +33,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.MY_UID;
+
import static java.util.concurrent.CompletableFuture.completedFuture;
import android.animation.Animator;
@@ -46,6 +48,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -183,13 +186,8 @@
// Crop the input surface to the display size.
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
- // Make trusted overlay to not block any touches while D&D ongoing and allowing
- // touches to pass through to windows underneath. This allows user to interact with the
- // UI to navigate while dragging.
- h.setTrustedOverlay(mTransaction, mInputSurface, true);
mTransaction.show(mInputSurface)
.setInputWindowInfo(mInputSurface, h)
- .setTrustedOverlay(mInputSurface, true)
.setLayer(mInputSurface, Integer.MAX_VALUE)
.setCrop(mInputSurface, mTmpClipRect);
@@ -379,6 +377,11 @@
mDragWindowHandle.ownerUid = MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
+ // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
+ // touches to pass through to windows underneath. This allows user to interact with the
+ // UI to navigate while dragging.
+ mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
+
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index c21930d..39622c1 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -74,7 +74,7 @@
mWindowHandle.ownerPid = WindowManagerService.MY_PID;
mWindowHandle.ownerUid = WindowManagerService.MY_UID;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
mInputSurface = mService.makeSurfaceBuilder(
mService.mRoot.getDisplayContent(displayId).getSession())
@@ -129,14 +129,12 @@
void show(SurfaceControl.Transaction t, WindowContainer w) {
t.show(mInputSurface);
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
}
void show(SurfaceControl.Transaction t, int layer) {
t.show(mInputSurface);
- mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, layer);
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f77da62..825d38b 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,10 +40,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
+
import static java.lang.Integer.MAX_VALUE;
import android.annotation.Nullable;
@@ -730,7 +732,7 @@
new InputWindowHandle(null /* inputApplicationHandle */, displayId));
inputWindowHandle.setName(name);
inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
- inputWindowHandle.setTrustedOverlay(t, sc, true);
+ inputWindowHandle.setTrustedOverlay(true);
populateOverlayInputInfo(inputWindowHandle);
setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 90d81bd..64b7a60 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -195,11 +195,6 @@
mChanged = true;
}
- void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
- boolean trustedOverlay) {
- mHandle.setTrustedOverlay(t, sc, trustedOverlay);
- }
-
void setOwnerPid(int pid) {
if (mHandle.ownerPid == pid) {
return;
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 579fd14..45cf10b 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -29,7 +29,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.FeatureFlags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -91,6 +90,13 @@
"enable_app_compat_user_aspect_ratio_fullscreen";
private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true;
+ // Whether the letterbox wallpaper style is enabled by default
+ private static final String KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER =
+ "enable_letterbox_background_wallpaper";
+
+ // TODO(b/290048978): Enable wallpaper as default letterbox background.
+ private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = false;
+
/**
* Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
* set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -179,9 +185,6 @@
@NonNull
private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
- @NonNull
- private final FeatureFlags mFeatureFlags;
-
// Aspect ratio of letterbox for fixed orientation, values <=
// MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
private float mFixedOrientationLetterboxAspectRatio;
@@ -298,8 +301,7 @@
// Flags dynamically updated with {@link android.provider.DeviceConfig}.
@NonNull private final SynchedDeviceConfig mDeviceConfig;
- LetterboxConfiguration(@NonNull final Context systemUiContext,
- @NonNull FeatureFlags featureFags) {
+ LetterboxConfiguration(@NonNull final Context systemUiContext) {
this(systemUiContext, new LetterboxConfigurationPersister(
() -> readLetterboxHorizontalReachabilityPositionFromConfig(
systemUiContext, /* forBookMode */ false),
@@ -308,16 +310,14 @@
() -> readLetterboxHorizontalReachabilityPositionFromConfig(
systemUiContext, /* forBookMode */ true),
() -> readLetterboxVerticalReachabilityPositionFromConfig(
- systemUiContext, /* forTabletopMode */ true)),
- featureFags);
+ systemUiContext, /* forTabletopMode */ true)));
}
@VisibleForTesting
LetterboxConfiguration(@NonNull final Context systemUiContext,
- @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister,
- @NonNull FeatureFlags featureFags) {
+ @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister) {
mContext = systemUiContext;
- mFeatureFlags = featureFags;
+
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
R.dimen.config_fixedOrientationLetterboxAspectRatio);
mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
@@ -385,6 +385,8 @@
DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS,
mContext.getResources().getBoolean(
R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
+ .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER,
+ DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, /* enabled */ true)
.addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN,
DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN,
mContext.getResources().getBoolean(
@@ -542,7 +544,8 @@
}
/**
- * Resets letterbox background type value depending on the built time and runtime flags.
+ * Resets letterbox background type value depending on the
+ * {@link #KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER} built time and runtime flags.
*
* <p>If enabled, the letterbox background type value is set toZ
* {@link #LETTERBOX_BACKGROUND_WALLPAPER}. When disabled the letterbox background type value
@@ -552,11 +555,12 @@
mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET;
}
- // Returns LETTERBOX_BACKGROUND_WALLPAPER if the flag is enabled or the value in
- // com.android.internal.R.integer.config_letterboxBackgroundType if the flag is disabled.
+ // Returns KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER if the DeviceConfig flag is enabled
+ // or the value in com.android.internal.R.integer.config_letterboxBackgroundType if the flag
+ // is disabled.
@LetterboxBackgroundType
private int getDefaultLetterboxBackgroundType() {
- return mFeatureFlags.letterboxBackgroundWallpaperFlag()
+ return mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER)
? LETTERBOX_BACKGROUND_WALLPAPER : mLetterboxBackgroundType;
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 01786be..735cbc4 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -33,6 +33,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -661,6 +662,10 @@
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
if (shouldApplyUserFullscreenOverride()) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_USER)
+ + " by user aspect ratio settings.");
return SCREEN_ORIENTATION_USER;
}
@@ -668,6 +673,15 @@
// orientation.
candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
+ if (shouldApplyUserMinAspectRatioOverride() && (!isFixedOrientation(candidate)
+ || candidate == SCREEN_ORIENTATION_LOCKED)) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
+ + " by user aspect ratio settings.");
+ return SCREEN_ORIENTATION_PORTRAIT;
+ }
+
if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) {
return candidate;
}
@@ -1126,11 +1140,25 @@
return computeAspectRatio(bounds);
}
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ boolean shouldEnableUserAspectRatioSettings() {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return !FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
+ && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+ * current app.
+ */
boolean shouldApplyUserMinAspectRatioOverride() {
- if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
- || !mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- || mActivityRecord.mDisplayContent == null
- || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ if (!shouldEnableUserAspectRatioSettings()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index be90588..ee05e35 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -117,6 +118,11 @@
return;
}
if (targetActivity.attachedToProcess()) {
+ if (targetActivity.app.getCurrentProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
+ Slog.v(TAG, "Skip preload recents for cached proc " + targetActivity.app);
+ // The process may be frozen that cannot receive binder call.
+ return;
+ }
// The activity may be relaunched if it cannot handle the current configuration
// changes. The activity will be paused state if it is relaunched, otherwise it
// keeps the original stopped state.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2fdfec0..2a33918 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1513,10 +1513,30 @@
throw new IllegalArgumentException(
"resolveSecondaryHomeActivity: Should not be default task container");
}
- // Resolve activities in the same package as currently selected primary home activity.
+
Intent homeIntent = mService.getHomeIntent();
ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
- if (aInfo != null) {
+ boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null;
+
+ if (android.companion.virtual.flags.Flags.vdmCustomHome()) {
+ // Resolve the externally set home activity for this display, if any. If it is unset or
+ // we fail to resolve it, fallback to the default secondary home activity.
+ final ComponentName customHomeComponent =
+ taskDisplayArea.getDisplayContent() != null
+ ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
+ : null;
+ if (customHomeComponent != null) {
+ homeIntent.setComponent(customHomeComponent);
+ ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
+ if (customHomeActivityInfo != null) {
+ aInfo = customHomeActivityInfo;
+ lookForSecondaryHomeActivityInPrimaryHomePackage = false;
+ }
+ }
+ }
+
+ if (lookForSecondaryHomeActivityInPrimaryHomePackage) {
+ // Resolve activities in the same package as currently selected primary home activity.
if (ResolverActivity.class.getName().equals(aInfo.name)) {
// Always fallback to secondary home component if default home is not set.
aInfo = null;
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 2d281c4..07ffa69e 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -108,4 +108,13 @@
boolean hasImeSurface() {
return false;
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit
+ + " removeAfterTransaction= " + mRemoveAfterTransaction
+ + "}";
+ }
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a55c232..a0517be 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -19,12 +19,12 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -102,7 +102,7 @@
static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
- boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
+ boolean allowIcon, boolean useLegacy, boolean activityDrawn, int startingWindowType,
String packageName, int userId) {
int parameter = 0;
if (newTask) {
@@ -120,8 +120,8 @@
if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
}
- if (isSolidColor) {
- parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+ if (allowIcon) {
+ parameter |= TYPE_PARAMETER_ALLOW_ICON;
}
if (useLegacy) {
parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 21526e7..261fc2e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1262,6 +1262,37 @@
return null;
}
+ boolean pauseActivityIfNeeded(@Nullable ActivityRecord resuming, @NonNull String reason) {
+ if (!isLeafTask()) {
+ return false;
+ }
+
+ final int[] someActivityPaused = {0};
+ // Check if the direct child resumed activity in the leaf task needed to be paused if
+ // the leaf task is not a leaf task fragment.
+ if (!isLeafTaskFragment()) {
+ final ActivityRecord top = topRunningActivity();
+ final ActivityRecord resumedActivity = getResumedActivity();
+ if (resumedActivity != null && top.getTaskFragment() != this) {
+ // Pausing the resumed activity because it is occluded by other task fragment.
+ if (startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }
+
+ forAllLeafTaskFragments((taskFrag) -> {
+ final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+ if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
+ if (taskFrag.startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }, true /* traverseTopToBottom */);
+
+ return someActivityPaused[0] > 0;
+ }
+
void updateTaskMovement(boolean toTop, boolean toBottom, int position) {
EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
position);
@@ -3463,6 +3494,7 @@
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.isUserFullscreenOverrideEnabled = top != null
&& top.mLetterboxUiController.shouldApplyUserFullscreenOverride();
+ info.isTopActivityTransparent = top != null && !top.fillsParent();
info.isFromLetterboxDoubleTap = top != null && top.mLetterboxUiController.isFromDoubleTap();
if (info.isLetterboxDoubleTapEnabled) {
info.topActivityLetterboxWidth = top.getBounds().width();
@@ -3478,9 +3510,9 @@
}
}
// User Aspect Ratio Settings is enabled if the app is not in SCM
- info.topActivityEligibleForUserAspectRatioButton =
- mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- && top != null && !info.topActivityInSizeCompat;
+ info.topActivityEligibleForUserAspectRatioButton = top != null
+ && !info.topActivityInSizeCompat
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
@@ -5102,7 +5134,6 @@
void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
- final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
Task rTask = r.getTask();
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
final boolean isOrhasTask = rTask == this || hasChild(rTask);
@@ -5166,8 +5197,10 @@
// supporting picture-in-picture while pausing only if the starting activity
// would not be considered an overlay on top of the current activity
// (eg. not fullscreen, or the assistant)
- enableEnterPipOnTaskSwitch(pipCandidate,
- null /* toFrontTask */, r, options);
+ if (!ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
+ enableEnterPipOnTaskSwitch(pipCandidate, null /* toFrontTask */, r, options);
+ }
}
boolean doShow = true;
if (newTask) {
@@ -5240,7 +5273,7 @@
* enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
* {@param toFrontActivity} should be set.
*/
- private static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
+ static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
@Nullable Task toFrontTask, @Nullable ActivityRecord toFrontActivity,
@Nullable ActivityOptions opts) {
if (pipCandidate == null) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9af12ad..ae794a8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1271,27 +1271,9 @@
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
forAllLeafTasks(leafTask -> {
- // Check if the direct child resumed activity in the leaf task needed to be paused if
- // the leaf task is not a leaf task fragment.
- if (!leafTask.isLeafTaskFragment()) {
- final ActivityRecord top = topRunningActivity();
- final ActivityRecord resumedActivity = leafTask.getResumedActivity();
- if (resumedActivity != null && top.getTaskFragment() != leafTask) {
- // Pausing the resumed activity because it is occluded by other task fragment.
- if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
+ if (leafTask.pauseActivityIfNeeded(resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
}
-
- leafTask.forAllLeafTaskFragments((taskFrag) -> {
- final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
- if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
- if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
- }, true /* traverseTopToBottom */);
}, true /* traverseTopToBottom */);
return someActivityPaused[0] > 0;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 57f44cb..5d95bc7 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1456,8 +1456,8 @@
}
try {
- final ClientTransaction transaction =
- ClientTransaction.obtain(next.app.getThread(), next.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(
+ next.app.getThread());
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -1643,7 +1643,17 @@
// next activity.
final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
"shouldAutoPipWhilePausing", userLeaving);
- if (userLeaving && resumingOccludesParent && lastResumedCanPip
+
+ if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ // If a new task is being launched, then mark the existing top activity as
+ // supporting picture-in-picture while pausing only if the starting activity
+ // would not be considered an overlay on top of the current activity
+ // (eg. not fullscreen, or the assistant)
+ Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
+ resuming, resuming.getOptions());
+ }
+ if (prev.supportsEnterPipOnTaskSwitch && userLeaving
+ && resumingOccludesParent && lastResumedCanPip
&& prev.pictureInPictureArgs.isAutoEnterEnabled()) {
shouldAutoPip = true;
} else if (!lastResumedCanPip) {
@@ -1656,7 +1666,12 @@
}
if (prev.attachedToProcess()) {
- if (shouldAutoPip) {
+ if (shouldAutoPip && ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+ prev.mPauseSchedulePendingForPip = true;
+ boolean willAutoPip = mAtmService.prepareAutoEnterPictureAndPictureMode(prev);
+ ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, requesting PIP mode "
+ + "via requestStartTransition(): %s, willAutoPip: %b", prev, willAutoPip);
+ } else if (shouldAutoPip) {
prev.mPauseSchedulePendingForPip = true;
boolean didAutoPip = mAtmService.enterPictureInPictureMode(
prev, prev.pictureInPictureArgs, false /* fromClient */);
@@ -1726,7 +1741,7 @@
prev.shortComponentName, "userLeaving=" + userLeaving, reason);
mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.token, PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
+ PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately, autoEnteringPip));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 843e6d1..bbafa25 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -174,6 +174,8 @@
private final Token mToken;
private IApplicationThread mRemoteAnimApp;
+ private @Nullable ActivityRecord mPipActivity;
+
/** Only use for clean-up after binder death! */
private SurfaceControl.Transaction mStartTransaction = null;
private SurfaceControl.Transaction mFinishTransaction = null;
@@ -510,6 +512,21 @@
}
/**
+ * Set the pip-able activity participating in this transition.
+ * @param pipActivity activity about to enter pip
+ */
+ void setPipActivity(@Nullable ActivityRecord pipActivity) {
+ mPipActivity = pipActivity;
+ }
+
+ /**
+ * @return pip-able activity participating in this transition.
+ */
+ @Nullable ActivityRecord getPipActivity() {
+ return mPipActivity;
+ }
+
+ /**
* Only set flag to the parent tasks and activity itself.
*/
private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
@@ -1597,13 +1614,6 @@
}
}
- // Take task snapshots before the animation so that we can capture IME before it gets
- // transferred. If transition is transient, IME won't be moved during the transition and
- // the tasks are still live, so we take the snapshot at the end of the transition instead.
- if (mTransientLaunches == null) {
- mController.mSnapshotController.onTransactionReady(mType, mTargets);
- }
-
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
for (int i = 0; i < mTargetDisplays.size(); ++i) {
@@ -1654,6 +1664,16 @@
reportStartReasonsToLogger();
+ // Take snapshots for closing tasks/activities before the animation finished but after
+ // dispatching onTransitionReady, so IME (if there is) can be captured together and the
+ // time spent on snapshot won't delay the start of animation. Note that if this transition
+ // is transient (mTransientLaunches != null), the snapshot will be captured at the end of
+ // the transition, because IME won't move be moved during the transition and the tasks are
+ // still live.
+ if (mTransientLaunches == null) {
+ mController.mSnapshotController.onTransactionReady(mType, mTargets);
+ }
+
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 0d78701..7d2933a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -705,13 +705,21 @@
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
- ActivityManager.RunningTaskInfo info = null;
+ ActivityManager.RunningTaskInfo startTaskInfo = null;
+ ActivityManager.RunningTaskInfo pipTaskInfo = null;
if (startTask != null) {
- info = new ActivityManager.RunningTaskInfo();
- startTask.fillTaskInfo(info);
+ startTaskInfo = startTask.getTaskInfo();
}
- final TransitionRequestInfo request = new TransitionRequestInfo(
- transition.mType, info, remoteTransition, displayChange, transition.getFlags());
+
+ // set the pip task in the request if provided
+ if (mCollectingTransition.getPipActivity() != null) {
+ pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo();
+ }
+
+ final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
+ startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
+ transition.getFlags());
+
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
mTransitionPlayer.requestStartTransition(transition.getToken(), request);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 805e7ff..9f1bccb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -954,4 +954,11 @@
/** Returns the SurfaceControl accessibility services should use for accessibility overlays. */
public abstract SurfaceControl getA11yOverlayLayer(int displayId);
+
+ /**
+ * Device has a software navigation bar (separate from the status bar) on specific display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
+ */
+ public abstract boolean hasNavigationBar(int displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e720446..9a1920d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
import static android.window.WindowProviderService.isWindowProviderService;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -333,7 +334,6 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
-import com.android.window.flags.FeatureFlagsImpl;
import dalvik.annotation.optimization.NeverCompile;
@@ -1175,8 +1175,7 @@
mLetterboxConfiguration = new LetterboxConfiguration(
// Using SysUI context to have access to Material colors extracted from Wallpaper.
- ActivityThread.currentActivityThread().getSystemUiContext(),
- new FeatureFlagsImpl());
+ ActivityThread.currentActivityThread().getSystemUiContext());
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2494,6 +2493,14 @@
outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
}
+ // TODO (b/298562855): Remove this after identifying the reason why the frame is empty.
+ if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) {
+ Slog.w(TAG, "Empty frame of " + win
+ + " configChanged=" + configChanged
+ + " frame=" + win.getFrame().toShortString()
+ + " attrs=" + attrs);
+ }
+
ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
win, focusMayChange);
@@ -8408,6 +8415,11 @@
}
@Override
+ public boolean hasNavigationBar(int displayId) {
+ return WindowManagerService.this.hasNavigationBar(displayId);
+ }
+
+ @Override
public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
synchronized (mGlobalLock) {
mImeTargetChangeListener = listener;
@@ -8869,6 +8881,11 @@
h.inputConfig |= InputConfig.NOT_FOCUSABLE;
}
+ // Check private trusted overlay flag to set trustedOverlay field of input window handle.
+ if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
+ h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
+ }
+
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
h.ownerUid = callingUid;
h.ownerPid = callingPid;
@@ -8888,8 +8905,6 @@
}
final SurfaceControl.Transaction t = mTransactionFactory.get();
- // Check private trusted overlay flag to set trustedOverlay field of input window handle.
- h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0);
t.setInputWindowInfo(surface, h);
t.apply();
t.close();
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bfe0553..74a0bafd3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -1279,6 +1279,7 @@
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx();
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 822082b..b12cc0b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,7 +28,6 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -99,6 +98,7 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -1112,9 +1112,7 @@
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
- if (!USE_SURFACE_TRUSTED_OVERLAY) {
- mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay());
- }
+ mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1195,12 +1193,12 @@
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
- private boolean isWindowTrustedOverlay() {
+ boolean shouldWindowHandleBeTrusted(Session s) {
return InputMonitor.isTrustedOverlay(mAttrs.type)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
- && mSession.mCanAddInternalSystemWindow)
+ && s.mCanAddInternalSystemWindow)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
- && mSession.mCanCreateSystemApplicationOverlay);
+ && s.mCanCreateSystemApplicationOverlay);
}
int getTouchOcclusionMode() {
@@ -5194,9 +5192,6 @@
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
- }
}
super.prepareSurfaces();
}
@@ -5949,13 +5944,7 @@
}
boolean isTrustedOverlay() {
- if (USE_SURFACE_TRUSTED_OVERLAY) {
- WindowState parentWindow = getParentWindow();
- return isWindowTrustedOverlay() || (parentWindow != null
- && parentWindow.isWindowTrustedOverlay());
- } else {
- return mInputWindowHandle.isTrustedOverlay();
- }
+ return mInputWindowHandle.isTrustedOverlay();
}
public boolean receiveFocusFromTapOutside() {
diff --git a/services/core/jni/TEST_MAPPING b/services/core/jni/TEST_MAPPING
index ea44d06..eb9db70 100644
--- a/services/core/jni/TEST_MAPPING
+++ b/services/core/jni/TEST_MAPPING
@@ -6,7 +6,7 @@
],
"name": "CtsVibratorTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index d22e02e..d0a9c45 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -50,6 +50,13 @@
maxOccurs="1"/>
<xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
maxOccurs="1"/>
+
+ <xs:element name="hdrBrightnessConfig" type="hdrBrightnessConfig"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+
<xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1"/>
<xs:element type="autoBrightness" name="autoBrightness" minOccurs="0"
maxOccurs="1"/>
@@ -89,6 +96,16 @@
<xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampDecreaseMaxMillis">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Maximum time in milliseconds that a brightness increase animation
+ can take in idle mode. -->
+ <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampIncreaseMaxIdleMillis">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Maximum time in milliseconds that a brightness decrease animation
+ can take in idle mode. -->
+ <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampDecreaseMaxIdleMillis">
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="sensorDetails" name="lightSensor">
<xs:annotation name="final"/>
</xs:element>
@@ -238,6 +255,31 @@
</xs:all>
</xs:complexType>
+ <!-- brightness config for HDR content -->
+ <xs:complexType name="hdrBrightnessConfig">
+ <!-- lux level from light sensor to screen brightness recommended max value map. -->
+ <xs:element name="brightnessMap" type="nonNegativeFloatToFloatMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Debounce for brightness increase in millis -->
+ <xs:element name="brightnessIncreaseDebounceMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Debounce for brightness decrease in millis -->
+ <xs:element name="brightnessDecreaseDebounceMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Animation time for brightness increase in millis -->
+ <xs:element name="brightnessIncreaseDurationMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Animation time for brightness decrease in millis -->
+ <xs:element name="brightnessDecreaseDurationMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+
<!-- Maps to PowerManager.THERMAL_STATUS_* values. -->
<xs:simpleType name="thermalStatus">
<xs:restriction base="xs:string">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6364c1f..949b1f2 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -101,6 +101,7 @@
method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
+ method @Nullable public final com.android.server.display.config.HdrBrightnessConfig getHdrBrightnessConfig();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public com.android.server.display.config.LuxThrottling getLuxThrottling();
@@ -110,9 +111,11 @@
method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
+ method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxIdleMillis();
method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxMillis();
method public final java.math.BigDecimal getScreenBrightnessRampFastDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
+ method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxIdleMillis();
method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxMillis();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecreaseIdle();
@@ -130,6 +133,7 @@
method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
+ method public final void setHdrBrightnessConfig(@Nullable com.android.server.display.config.HdrBrightnessConfig);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
@@ -139,9 +143,11 @@
method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
+ method public final void setScreenBrightnessRampDecreaseMaxIdleMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampDecreaseMaxMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampFastDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal);
+ method public final void setScreenBrightnessRampIncreaseMaxIdleMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampIncreaseMaxMillis(java.math.BigInteger);
method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
method public final void setScreenBrightnessRampSlowDecreaseIdle(java.math.BigDecimal);
@@ -168,6 +174,20 @@
method public final void setTimeWindowSecs_all(@NonNull java.math.BigInteger);
}
+ public class HdrBrightnessConfig {
+ ctor public HdrBrightnessConfig();
+ method public final java.math.BigInteger getBrightnessDecreaseDebounceMillis();
+ method public final java.math.BigInteger getBrightnessDecreaseDurationMillis();
+ method public final java.math.BigInteger getBrightnessIncreaseDebounceMillis();
+ method public final java.math.BigInteger getBrightnessIncreaseDurationMillis();
+ method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getBrightnessMap();
+ method public final void setBrightnessDecreaseDebounceMillis(java.math.BigInteger);
+ method public final void setBrightnessDecreaseDurationMillis(java.math.BigInteger);
+ method public final void setBrightnessIncreaseDebounceMillis(java.math.BigInteger);
+ method public final void setBrightnessIncreaseDurationMillis(java.math.BigInteger);
+ method public final void setBrightnessMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ }
+
public class HighBrightnessMode {
ctor public HighBrightnessMode();
method @NonNull public final boolean getAllowInLowPowerMode_all();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index d8c684f..a4adf58 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -35,11 +35,13 @@
import android.credentials.CreateCredentialRequest;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.ICredentialManager;
+import android.credentials.IGetCandidateCredentialsCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.ISetEnabledProvidersCallback;
@@ -96,6 +98,9 @@
private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
"enable_credential_manager";
+ private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
+ "enable_credential_description_api";
+
private final Context mContext;
/** Cache of system service list per user id. */
@@ -298,9 +303,9 @@
ComponentName compName = ComponentName.unflattenFromString(serviceName);
if (compName == null) {
Slog.w(
- TAG,
- "Primary provider component name unflatten from string error: "
- + serviceName);
+ TAG,
+ "Primary provider component name unflatten from string error: "
+ + serviceName);
continue;
}
services.add(compName);
@@ -321,7 +326,14 @@
}
public static boolean isCredentialDescriptionApiEnabled() {
- return true;
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+ false);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
@SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
@@ -461,6 +473,59 @@
final class CredentialManagerServiceStub extends ICredentialManager.Stub {
@Override
+ public ICancellationSignal getCandidateCredentials(
+ GetCredentialRequest request,
+ IGetCandidateCredentialsCallback callback,
+ final String callingPackage) {
+ Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+ + callingPackage);
+ ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+
+ // New request session, scoped for this request only.
+ final GetCandidateRequestSession session =
+ new GetCandidateRequestSession(
+ getContext(),
+ mSessionManager,
+ mLock,
+ userId,
+ callingUid,
+ callback,
+ request,
+ constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
+ getEnabledProvidersForUser(userId),
+ CancellationSignal.fromTransport(cancelTransport)
+ );
+ addSessionLocked(userId, session);
+
+ List<ProviderSession> providerSessions =
+ initiateProviderSessions(
+ session,
+ request.getCredentialOptions().stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
+
+ if (providerSessions.isEmpty()) {
+ try {
+ callback.onError(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials available on this device.");
+ } catch (RemoteException e) {
+ Slog.i(
+ TAG,
+ "Issue invoking onError on IGetCredentialCallback "
+ + "callback: "
+ + e.getMessage());
+ }
+ }
+
+ invokeProviderSessions(providerSessions);
+ return cancelTransport;
+ }
+
+ @Override
public ICancellationSignal executeGetCredential(
GetCredentialRequest request,
IGetCredentialCallback callback,
@@ -714,7 +779,7 @@
@Override
public void setEnabledProviders(
- List<String> primaryProviders, List<String> providers, int userId,
+ List<String> primaryProviders, List<String> providers, int userId,
ISetEnabledProvidersCallback callback) {
final int callingUid = Binder.getCallingUid();
if (!hasWriteSecureSettingsPermission()) {
@@ -839,9 +904,9 @@
ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
ApiStatus.SUCCESS, callingUid);
return CredentialProviderInfoFactory
- .getCredentialProviderServices(
- mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
- getPrimaryProvidersForUserId(mContext, userId));
+ .getCredentialProviderServices(
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
+ getPrimaryProvidersForUserId(mContext, userId));
}
@@ -871,13 +936,14 @@
private Set<ComponentName> getEnabledProvidersForUser(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(
- Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, false,
- "getEnabledProvidersForUser", null);
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getEnabledProvidersForUser", null);
Set<ComponentName> enabledProviders = new HashSet<>();
String directValue = Settings.Secure.getStringForUser(
- mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE, resolvedUserId);
+ mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
+ resolvedUserId);
if (!TextUtils.isEmpty(directValue)) {
String[] components = directValue.split(":");
@@ -954,7 +1020,7 @@
Slog.i(TAG, "registerCredentialDescription with callingPackage: " + callingPackage);
if (!isCredentialDescriptionApiEnabled()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Feature not supported");
}
enforceCallingPackage(callingPackage, Binder.getCallingUid());
@@ -974,7 +1040,7 @@
if (!isCredentialDescriptionApiEnabled()) {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("Feature not supported");
}
enforceCallingPackage(callingPackage, Binder.getCallingUid());
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
new file mode 100644
index 0000000..6d9b7e8
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
+import android.credentials.GetCandidateCredentialsResponse;
+import android.credentials.GetCredentialRequest;
+import android.credentials.IGetCandidateCredentialsCallback;
+import android.credentials.ui.GetCredentialProviderData;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.os.CancellationSignal;
+import android.os.RemoteException;
+import android.service.credentials.CallingAppInfo;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Central session for a single getCandidateCredentials request. This class listens to the
+ * responses from providers, and updates the provider(s) state.
+ */
+public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
+ IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
+ implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+ private static final String TAG = "GetCandidateRequestSession";
+
+ public GetCandidateRequestSession(
+ Context context, SessionLifetime sessionCallback,
+ Object lock, int userId, int callingUid,
+ IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
+ CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
+ CancellationSignal cancellationSignal) {
+ super(context, sessionCallback, lock, userId, callingUid, request, callback,
+ RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
+ cancellationSignal, 0L);
+ }
+
+ /**
+ * Creates a new provider session, and adds it list of providers that are contributing to
+ * this session.
+ *
+ * @return the provider session created within this request session, for the given provider
+ * info.
+ */
+ @Override
+ @Nullable
+ public ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
+ RemoteCredentialService remoteCredentialService) {
+ ProviderGetSession providerGetCandidateSessions = ProviderGetSession
+ .createNewSession(mContext, mUserId, providerInfo,
+ this, remoteCredentialService);
+ if (providerGetCandidateSessions != null) {
+ Slog.d(TAG, "In startProviderSession - provider session created and "
+ + "being added for: " + providerInfo.getComponentName());
+ mProviders.put(providerGetCandidateSessions.getComponentName().flattenToString(),
+ providerGetCandidateSessions);
+ }
+ return providerGetCandidateSessions;
+ }
+
+ /**
+ * Even though there is no UI involved, this is called when all providers are ready
+ * in our current flow. Eventually can completely separate UI and non UI flows.
+ */
+ @Override
+ protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
+ if (providerDataList == null || providerDataList.isEmpty()) {
+ respondToClientWithErrorAndFinish(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials found");
+ return;
+ }
+
+ List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+ for (ProviderData providerData : providerDataList) {
+ candidateProviderDataList.add((GetCredentialProviderData) (providerData));
+ }
+ respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
+ candidateProviderDataList));
+ }
+
+ @Override
+ protected void invokeClientCallbackSuccess(GetCandidateCredentialsResponse response)
+ throws RemoteException {
+ mClientCallback.onResponse(response);
+ }
+
+ @Override
+ protected void invokeClientCallbackError(String errorType, String errorMsg)
+ throws RemoteException {
+ mClientCallback.onError(errorType, errorMsg);
+ }
+
+ @Override
+ public void onFinalErrorReceived(ComponentName componentName, String errorType,
+ String message) {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onUiCancellation(boolean isUserCancellation) {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onUiSelectorInvocationFailure() {
+ // Not applicable for session without UI
+ }
+
+ @Override
+ public void onProviderStatusChanged(ProviderSession.Status status,
+ ComponentName componentName, ProviderSession.CredentialsSource source) {
+ Slog.d(TAG, "in onStatusChanged with status: " + status + ", and source: " + source);
+
+ // For any other status, we check if all providers are done and then invoke UI if needed
+ if (!isAnyProviderPending()) {
+ // If all provider responses have been received, we can either need the UI,
+ // or we need to respond with error. The only other case is the entry being
+ // selected after the UI has been invoked which has a separate code path.
+ if (isUiInvocationNeeded()) {
+ Slog.d(TAG, "in onProviderStatusChanged - isUiInvocationNeeded");
+ getProviderDataAndInitiateUi();
+ } else {
+ respondToClientWithErrorAndFinish(
+ GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+ "No credentials available");
+ }
+ }
+ }
+
+ @Override
+ public void onFinalResponseReceived(ComponentName componentName,
+ GetCandidateCredentialsResponse response) {
+ // Not applicable for session without UI
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 3c1432a..3eb6718 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -119,6 +119,42 @@
return null;
}
+ /** Creates a new provider session to be used by the request session. */
+ @Nullable
+ public static ProviderGetSession createNewSession(
+ Context context,
+ @UserIdInt int userId,
+ CredentialProviderInfo providerInfo,
+ GetCandidateRequestSession getRequestSession,
+ RemoteCredentialService remoteCredentialService) {
+ android.credentials.GetCredentialRequest filteredRequest =
+ filterOptions(providerInfo.getCapabilities(),
+ getRequestSession.mClientRequest,
+ providerInfo);
+ if (filteredRequest != null) {
+ Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
+ new HashMap<>();
+ return new ProviderGetSession(
+ context,
+ providerInfo,
+ getRequestSession,
+ userId,
+ remoteCredentialService,
+ constructQueryPhaseRequest(
+ filteredRequest, getRequestSession.mClientAppInfo,
+ getRequestSession.mClientRequest.alwaysSendAppInfoToProvider(),
+ beginGetOptionToCredentialOptionMap),
+ filteredRequest,
+ getRequestSession.mClientAppInfo,
+ beginGetOptionToCredentialOptionMap,
+ getRequestSession.mHybridService
+ );
+ }
+ Slog.i(TAG, "Unable to create provider session for: "
+ + providerInfo.getComponentName());
+ return null;
+ }
+
private static BeginGetCredentialRequest constructQueryPhaseRequest(
android.credentials.GetCredentialRequest filteredRequest,
CallingAppInfo callingAppInfo,
@@ -192,7 +228,7 @@
public ProviderGetSession(Context context,
CredentialProviderInfo info,
- ProviderInternalCallback<GetCredentialResponse> callbacks,
+ ProviderInternalCallback callbacks,
int userId, RemoteCredentialService remoteCredentialService,
BeginGetCredentialRequest beginGetRequest,
android.credentials.GetCredentialRequest completeGetRequest,
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
index 72bba11..fccd1ec 100644
--- a/services/devicepolicy/TEST_MAPPING
+++ b/services/devicepolicy/TEST_MAPPING
@@ -7,7 +7,7 @@
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 21b1291..d0ead14 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1472,11 +1472,10 @@
synchronized (mLock) {
clear();
new DevicePoliciesReaderWriter().readFromFileLocked();
- reapplyAllPoliciesLocked();
}
}
- private <V> void reapplyAllPoliciesLocked() {
+ <V> void reapplyAllPoliciesLocked() {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
// Policy definition and value will always be of the same type
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 50dc061..f604932 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3347,6 +3347,11 @@
mOwners.systemReady();
applyManagedSubscriptionsPolicyIfRequired();
break;
+ case SystemService.PHASE_SYSTEM_SERVICES_READY:
+ synchronized (getLockObject()) {
+ mDevicePolicyEngine.reapplyAllPoliciesLocked();
+ }
+ break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
@@ -15836,6 +15841,83 @@
}
/**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA},
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE} and {@link
+ * DevicePolicyManager#POLICY_SUSPEND_PACKAGES}.
+ */
+ private Set<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestrictionInternal(
+ int userId, @NonNull String restriction) {
+ Objects.requireNonNull(restriction);
+ Set<android.app.admin.EnforcingAdmin> admins = new HashSet<>();
+ // For POLICY_SUSPEND_PACKAGES return PO or DO to keep the behavior same as
+ // before the bug fix for b/192245204.
+ if (DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(
+ restriction)) {
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ if (profileOwner != null) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ profileOwner, userId);
+ admins.add(admin.getParcelableAdmin());
+ return admins;
+ }
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null && deviceOwner.first == userId) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ deviceOwner.second, deviceOwner.first);
+ admins.add(admin.getParcelableAdmin());
+ return admins;
+ }
+ } else {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ PolicyDefinition<Boolean> policyDefinition = getPolicyDefinitionForRestriction(
+ restriction);
+ Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+ if (value != null && value) {
+ Map<EnforcingAdmin, PolicyValue<Boolean>> globalPolicies =
+ mDevicePolicyEngine.getGlobalPoliciesSetByAdmins(policyDefinition);
+ for (EnforcingAdmin admin : globalPolicies.keySet()) {
+ if (globalPolicies.get(admin) != null
+ && Boolean.TRUE.equals(globalPolicies.get(admin).getValue())) {
+ admins.add(admin.getParcelableAdmin());
+ }
+ }
+
+ Map<EnforcingAdmin, PolicyValue<Boolean>> localPolicies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ policyDefinition, userId);
+ for (EnforcingAdmin admin : localPolicies.keySet()) {
+ if (localPolicies.get(admin) != null
+ && Boolean.TRUE.equals(localPolicies.get(admin).getValue())) {
+ admins.add(admin.getParcelableAdmin());
+ }
+ }
+ return admins;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+ return admins;
+ }
+
+ private static PolicyDefinition<Boolean> getPolicyDefinitionForRestriction(
+ @NonNull String restriction) {
+ Objects.requireNonNull(restriction);
+ if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
+ return PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_CAMERA);
+ } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ return PolicyDefinition.SCREEN_CAPTURE_DISABLED;
+ } else {
+ return PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction);
+ }
+ }
+
+
+ /**
* Excludes restrictions imposed by UserManager.
*/
private List<UserManager.EnforcingUser> getDevicePolicySources(
@@ -15873,6 +15955,13 @@
return getEnforcingAdminAndUserDetailsInternal(userId, restriction);
}
+ @Override
+ public List<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestriction(
+ int userId, String restriction) {
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ return new ArrayList<>(getEnforcingAdminsForRestrictionInternal(userId, restriction));
+ }
+
/**
* @param restriction The restriction enforced by admin. It could be any user restriction or
* policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
@@ -15928,16 +16017,20 @@
}
@Override
- public PackagePolicy getCredentialManagerPolicy() {
+ public PackagePolicy getCredentialManagerPolicy(int userId) {
if (!mHasFeature) {
return null;
}
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
canWriteCredentialManagerPolicy(caller) || canQueryAdminPolicy(caller));
+ if (userId != caller.getUserId()) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
+ }
synchronized (getLockObject()) {
- ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(userId);
return (admin != null) ? admin.mCredentialManagerPolicy : null;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 5243d14..0066422 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -228,7 +228,8 @@
return new android.app.admin.EnforcingAdmin(
mPackageName,
authority,
- UserHandle.of(mUserId));
+ UserHandle.of(mUserId),
+ mComponentName);
}
/**
diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS
new file mode 100644
index 0000000..b2dcd0c
--- /dev/null
+++ b/services/foldables/devicestateprovider/OWNERS
@@ -0,0 +1,6 @@
+akulian@google.com
+kennethford@google.com
+jiamingliu@google.com
+kchyn@google.com
+nickchameyev@google.com
+nicomazz@google.com
\ No newline at end of file
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index 4af880d..4c9403c 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -12,7 +12,7 @@
"name": "CtsPackageManagerIncrementalStatsHostTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
@@ -55,7 +55,7 @@
"name": "CtsPackageManagerIncrementalStatsHostTestCases",
"options": [
{
- "include-annotation": "android.platform.test.annotations.LargeTest"
+ "include-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index b2733d4..240585c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,6 +16,7 @@
package com.android.server.permission.access.permission
+import android.permission.PermissionManager
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -164,9 +165,18 @@
deviceId: String,
userId: Int,
permissionName: String
- ): Int =
- state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
+ ): Int {
+ val flags = state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+ ?.getWithDefault(permissionName, 0) ?: 0
+ if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+ Slog.i(
+ LOG_TAG, "getPermissionFlags: appId=$appId, userId=$userId," +
+ " deviceId=$deviceId, permissionName=$permissionName," +
+ " flags=${PermissionFlags.toString(flags)}"
+ )
+ }
+ return flags
+ }
fun MutateStateScope.setPermissionFlags(
appId: Int,
@@ -202,6 +212,13 @@
val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
MutableIndexedReferenceMap()
}
+ if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
+ Slog.i(
+ LOG_TAG, "setPermissionFlags(): appId=$appId, userId=$userId," +
+ " deviceId=$deviceId, permissionName=$permissionName," +
+ " newFlags=${PermissionFlags.toString(newFlags)}"
+ )
+ }
val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
permissionFlags.putWithDefault(permissionName, newFlags, 0)
if (permissionFlags.isEmpty()) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 8279600..cee2524 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -985,7 +985,7 @@
) {
val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
AppIdAppOpPolicy
- val appOpName = checkNotNull(AppOpsManager.permissionToOp(permissionName))
+ val appOpName = AppOpsManager.permissionToOp(permissionName)!!
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
@@ -1753,10 +1753,19 @@
}
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"
+ )
+
onPermissionsChangeListeners.addListener(listener)
}
override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
+ "removeOnPermissionsChangeListener"
+ )
+
onPermissionsChangeListeners.removeListener(listener)
}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index 3ab2547..3cf57a3 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -1445,6 +1445,192 @@
.isEqualTo(expectedNewFlags)
}
+ @Test
+ fun testOnPackageAdded_runtimeExistingImplicitPermissions_sourceFlagsNotInherited() {
+ val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = oldImplicitPermissionFlags,
+ isNewInstallAndNewPermission = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.APP_OP_REVOKED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, existing and runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_nonRuntimeNewImplicitPermissions_sourceFlagsNotInherited() {
+ testInheritImplicitPermissionStates(
+ implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, new and non-runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_runtimeNewImplicitPermissions_sourceFlagsInherited() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageAdded_grantingNewFromRevokeImplicitPermissions_onlySourceFlagsInherited() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
+ sourceRuntimeFlags = sourceRuntimeFlags,
+ isAnySourcePermissionNonRuntime = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a permission that is" +
+ " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
+ " from source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to
+ * remove the IMPLICIT flag so that they will be granted when they are no longer implicit.
+ * (instead of revoking it)
+ */
+ @Test
+ fun testOnPackageAdded_mediaImplicitPermissions_getsImplicitFlagRemoved() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
+ sourceRuntimeFlags = sourceRuntimeFlags
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_ACCESS_MEDIA_LOCATION)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
+ assertWithMessage(
+ "After onPackageAdded() is called for a package that requests a media permission that" +
+ " is implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission and have the IMPLICIT flag removed. Hence the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testInheritImplicitPermissionStates(
+ implicitPermissionName: String = PERMISSION_NAME_0,
+ implicitPermissionFlags: Int = 0,
+ implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
+ sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
+ isAnySourcePermissionNonRuntime: Boolean = true,
+ isNewInstallAndNewPermission: Boolean = true
+ ) {
+ val implicitPermission = mockParsedPermission(
+ implicitPermissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = implicitPermissionProtectionLevel,
+ )
+ // For source from non-runtime in order to grant by implicit
+ val sourcePermission1 = mockParsedPermission(
+ PERMISSION_NAME_1,
+ PACKAGE_NAME_0,
+ protectionLevel = if (isAnySourcePermissionNonRuntime) {
+ PermissionInfo.PROTECTION_NORMAL
+ } else {
+ PermissionInfo.PROTECTION_DANGEROUS
+ }
+ )
+ // For inheriting runtime flags
+ val sourcePermission2 = mockParsedPermission(
+ PERMISSION_NAME_2,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
+ )
+ )
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(
+ implicitPermissionName,
+ PERMISSION_NAME_1,
+ PERMISSION_NAME_2
+ ),
+ implicitPermissions = setOf(implicitPermissionName)
+ )
+ )
+ oldState.mutateExternalState().setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(implicitPermissionName, MutableIndexedListSet<String>().apply {
+ add(PERMISSION_NAME_1)
+ add(PERMISSION_NAME_2)
+ })
+ }
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPermission(implicitPermission)
+ addPermission(sourcePermission1)
+ addPermission(sourcePermission2)
+ if (!isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(APP_ID_1, USER_ID_0, implicitPermissionName, implicitPermissionFlags)
+ }
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_2, sourceRuntimeFlags)
+
+ mutateState {
+ if (isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(
+ APP_ID_1,
+ USER_ID_0,
+ implicitPermissionName,
+ implicitPermissionFlags,
+ newState
+ )
+ }
+ with(appIdPermissionPolicy) {
+ onPackageAdded(installedPackageState)
+ }
+ }
+ }
+
/**
* Setup simple package states for testing evaluatePermissionState().
* permissionOwnerPackageState is definer of permissionName with APP_ID_0.
@@ -1734,6 +1920,7 @@
private const val PERMISSION_NAME_0 = "permissionName0"
private const val PERMISSION_NAME_1 = "permissionName1"
+ private const val PERMISSION_NAME_2 = "permissionName2"
private const val PERMISSION_READ_EXTERNAL_STORAGE =
Manifest.permission.READ_EXTERNAL_STORAGE
private const val PERMISSION_POST_NOTIFICATIONS =
@@ -1742,6 +1929,8 @@
Manifest.permission.BLUETOOTH_CONNECT
private const val PERMISSION_ACCESS_BACKGROUND_LOCATION =
Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ private const val PERMISSION_ACCESS_MEDIA_LOCATION =
+ Manifest.permission.ACCESS_MEDIA_LOCATION
private const val USER_ID_0 = 0
}
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index e28028f9..0275c7d 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -29,6 +29,7 @@
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "flag-junit",
"frameworks-base-testutils",
"junit",
"junit-params",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 82d00a6..6ef150c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -44,6 +44,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
import org.junit.Before;
@@ -102,12 +103,6 @@
assertEquals(mDisplayDeviceConfig.getName(), "Example Display");
assertEquals(mDisplayDeviceConfig.getAmbientHorizonLong(), 5000);
assertEquals(mDisplayDeviceConfig.getAmbientHorizonShort(), 50);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampFastDecrease(), 0.01f, ZERO_DELTA);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampFastIncrease(), 0.02f, ZERO_DELTA);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, ZERO_DELTA);
- assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, ZERO_DELTA);
assertEquals(mDisplayDeviceConfig.getBrightnessDefault(), 0.5f, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
@@ -234,7 +229,8 @@
@Test
public void testInvalidLuxThrottling() throws Exception {
setupDisplayDeviceConfigFromDisplayConfigFile(
- getContent(getInvalidLuxThrottling(), getValidProxSensor()));
+ getContent(getInvalidLuxThrottling(), getValidProxSensor(),
+ /* includeIdleMode= */ true));
Map<DisplayDeviceConfig.BrightnessLimitMapType, Map<Float, Float>> luxThrottlingData =
mDisplayDeviceConfig.getLuxThrottlingData();
@@ -257,6 +253,10 @@
// We should fall back to the config resource
verifyConfigValuesFromConfigResource();
+ assertEquals(3000, mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce());
+ assertEquals(4000, mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce());
+ assertEquals(3000, mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounceIdle());
+ assertEquals(4000, mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounceIdle());
}
@Test
@@ -437,7 +437,8 @@
@Test
public void testProximitySensorWithEmptyValuesFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(
- getContent(getValidLuxThrottling(), getProxSensorWithEmptyValues()));
+ getContent(getValidLuxThrottling(), getProxSensorWithEmptyValues(),
+ /* includeIdleMode= */ true));
assertNull(mDisplayDeviceConfig.getProximitySensor());
}
@@ -477,6 +478,23 @@
mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), ZERO_DELTA);
}
+ @Test
+ public void testHdrBrightnessDataFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ HdrBrightnessData data = mDisplayDeviceConfig.getHdrBrightnessData();
+
+ assertNotNull(data);
+ assertEquals(2, data.mMaxBrightnessLimits.size());
+ assertEquals(13000, data.mBrightnessDecreaseDebounceMillis);
+ assertEquals(10000, data.mBrightnessDecreaseDurationMillis);
+ assertEquals(1000, data.mBrightnessIncreaseDebounceMillis);
+ assertEquals(11000, data.mBrightnessIncreaseDurationMillis);
+
+ assertEquals(0.3f, data.mMaxBrightnessLimits.get(500f), SMALL_DELTA);
+ assertEquals(0.6f, data.mMaxBrightnessLimits.get(1200f), SMALL_DELTA);
+ }
+
private void verifyConfigValuesFromConfigResource() {
assertNull(mDisplayDeviceConfig.getName());
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -559,6 +577,39 @@
assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounceIdle(), 1500);
}
+ @Test
+ public void testBrightnessRamps() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis(), 5000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis(), 4000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastDecrease(), 0.01f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastIncrease(), 0.02f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle(), 0.05f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.06f, ZERO_DELTA);
+ }
+
+ @Test
+ public void testBrightnessRamps_IdleFallsBackToConfigInteractive() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis(), 3000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis(), 2000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastDecrease(), 0.01f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastIncrease(), 0.02f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle(), 0.03f, ZERO_DELTA);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.04f, ZERO_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -694,11 +745,122 @@
+ "</proxSensor>\n";
}
- private String getContent() {
- return getContent(getValidLuxThrottling(), getValidProxSensor());
+ private String getHdrBrightnessConfig() {
+ return "<hdrBrightnessConfig>\n"
+ + " <brightnessMap>\n"
+ + " <point>\n"
+ + " <first>500</first>\n"
+ + " <second>0.3</second>\n"
+ + " </point>\n"
+ + " <point>\n"
+ + " <first>1200</first>\n"
+ + " <second>0.6</second>\n"
+ + " </point>\n"
+ + " </brightnessMap>\n"
+ + " <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>\n"
+ + " <brightnessIncreaseDurationMillis>11000</brightnessIncreaseDurationMillis>\n"
+ + " <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>\n"
+ + " <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>\n"
+ + "</hdrBrightnessConfig>";
}
- private String getContent(String brightnessCapConfig, String proxSensor) {
+ private String getRampSpeedsIdle() {
+ return "<brighteningLightDebounceIdleMillis>"
+ + "2500"
+ + "</brighteningLightDebounceIdleMillis>\n"
+ + "<darkeningLightDebounceIdleMillis>"
+ + "1500"
+ + "</darkeningLightDebounceIdleMillis>\n";
+ }
+
+ private String getThresholdsIdle() {
+ return "<ambientBrightnessChangeThresholdsIdle>\n"
+ + "<brighteningThresholds>\n"
+ + "<minimum>20</minimum>\n"
+ + "<brightnessThresholdPoints>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0</threshold><percentage>21</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>500</threshold><percentage>22</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>600</threshold><percentage>23</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "</brightnessThresholdPoints>\n"
+ + "</brighteningThresholds>\n"
+ + "<darkeningThresholds>\n"
+ + "<minimum>40</minimum>\n"
+ + "<brightnessThresholdPoints>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0</threshold><percentage>23</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>700</threshold><percentage>24</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>800</threshold><percentage>25</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "</brightnessThresholdPoints>\n"
+ + "</darkeningThresholds>\n"
+ + "</ambientBrightnessChangeThresholdsIdle>\n"
+ + "<displayBrightnessChangeThresholdsIdle>\n"
+ + "<brighteningThresholds>\n"
+ + "<minimum>0.2</minimum>\n"
+ + "<brightnessThresholdPoints>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0</threshold><percentage>17</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0.12</threshold><percentage>18</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0.22</threshold><percentage>19</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "</brightnessThresholdPoints>\n"
+ + "</brighteningThresholds>\n"
+ + "<darkeningThresholds>\n"
+ + "<minimum>0.4</minimum>\n"
+ + "<brightnessThresholdPoints>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0</threshold><percentage>19</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0.13</threshold><percentage>20</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "<brightnessThresholdPoint>\n"
+ + "<threshold>0.23</threshold><percentage>21</percentage>\n"
+ + "</brightnessThresholdPoint>\n"
+ + "</brightnessThresholdPoints>\n"
+ + "</darkeningThresholds>\n"
+ + "</displayBrightnessChangeThresholdsIdle>\n";
+ }
+
+ private String getScreenBrightnessRampSlowIdle() {
+ return "<screenBrightnessRampSlowDecreaseIdle>"
+ + "0.05"
+ + "</screenBrightnessRampSlowDecreaseIdle>\n"
+ + "<screenBrightnessRampSlowIncreaseIdle>"
+ + "0.06"
+ + "</screenBrightnessRampSlowIncreaseIdle>\n";
+ }
+
+ private String getScreenBrightnessRampCapsIdle() {
+ return "<screenBrightnessRampIncreaseMaxIdleMillis>"
+ + "4000"
+ + "</screenBrightnessRampIncreaseMaxIdleMillis>\n"
+ + "<screenBrightnessRampDecreaseMaxIdleMillis>"
+ + "5000"
+ + "</screenBrightnessRampDecreaseMaxIdleMillis>\n";
+
+ }
+ private String getContent() {
+ return getContent(getValidLuxThrottling(), getValidProxSensor(),
+ /* includeIdleMode= */ true);
+ }
+
+ private String getContent(String brightnessCapConfig, String proxSensor,
+ boolean includeIdleMode) {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<displayConfiguration>\n"
+ "<name>Example Display</name>\n"
@@ -741,12 +903,7 @@
+ "<autoBrightness>\n"
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
- + "<brighteningLightDebounceIdleMillis>"
- + "2500"
- + "</brighteningLightDebounceIdleMillis>\n"
- + "<darkeningLightDebounceIdleMillis>"
- + "1500"
- + "</darkeningLightDebounceIdleMillis>\n"
+ + (includeIdleMode ? getRampSpeedsIdle() : "")
+ "<displayBrightnessMapping>\n"
+ "<displayBrightnessPoint>\n"
+ "<lux>50</lux>\n"
@@ -784,6 +941,7 @@
+ "</point>\n"
+ "</sdrHdrRatioMap>\n"
+ "</highBrightnessMode>\n"
+ + getHdrBrightnessConfig()
+ brightnessCapConfig
+ "<lightSensor>\n"
+ "<type>test_light_sensor</type>\n"
@@ -861,76 +1019,19 @@
+ "</brightnessThresholdPoints>\n"
+ "</darkeningThresholds>\n"
+ "</displayBrightnessChangeThresholds>\n"
- + "<ambientBrightnessChangeThresholdsIdle>\n"
- + "<brighteningThresholds>\n"
- + "<minimum>20</minimum>\n"
- + "<brightnessThresholdPoints>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0</threshold><percentage>21</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>500</threshold><percentage>22</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>600</threshold><percentage>23</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "</brightnessThresholdPoints>\n"
- + "</brighteningThresholds>\n"
- + "<darkeningThresholds>\n"
- + "<minimum>40</minimum>\n"
- + "<brightnessThresholdPoints>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0</threshold><percentage>23</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>700</threshold><percentage>24</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>800</threshold><percentage>25</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "</brightnessThresholdPoints>\n"
- + "</darkeningThresholds>\n"
- + "</ambientBrightnessChangeThresholdsIdle>\n"
- + "<displayBrightnessChangeThresholdsIdle>\n"
- + "<brighteningThresholds>\n"
- + "<minimum>0.2</minimum>\n"
- + "<brightnessThresholdPoints>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0</threshold><percentage>17</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0.12</threshold><percentage>18</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0.22</threshold><percentage>19</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "</brightnessThresholdPoints>\n"
- + "</brighteningThresholds>\n"
- + "<darkeningThresholds>\n"
- + "<minimum>0.4</minimum>\n"
- + "<brightnessThresholdPoints>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0</threshold><percentage>19</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0.13</threshold><percentage>20</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "<brightnessThresholdPoint>\n"
- + "<threshold>0.23</threshold><percentage>21</percentage>\n"
- + "</brightnessThresholdPoint>\n"
- + "</brightnessThresholdPoints>\n"
- + "</darkeningThresholds>\n"
- + "</displayBrightnessChangeThresholdsIdle>\n"
- + "<screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease> "
- + "<screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease> "
- + "<screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>"
- + "<screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>"
+ + (includeIdleMode ? getThresholdsIdle() : "")
+ + "<screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>\n"
+ + "<screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease>\n"
+ + "<screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>\n"
+ + "<screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>\n"
+ + (includeIdleMode ? getScreenBrightnessRampSlowIdle() : "")
+ "<screenBrightnessRampIncreaseMaxMillis>"
+ "2000"
- + "</screenBrightnessRampIncreaseMaxMillis>"
+ + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ "<screenBrightnessRampDecreaseMaxMillis>"
+ "3000"
- + "</screenBrightnessRampDecreaseMaxMillis>"
+ + "</screenBrightnessRampDecreaseMaxMillis>\n"
+ + (includeIdleMode ? getScreenBrightnessRampCapsIdle() : "")
+ "<ambientLightHorizonLong>5000</ambientLightHorizonLong>\n"
+ "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n"
+ "<screenBrightnessRampIncreaseMaxMillis>"
@@ -1163,6 +1264,13 @@
when(mResources.getString(com.android.internal.R.string.config_displayLightSensorType))
.thenReturn("test_light_sensor");
+ when(mResources.getInteger(
+ R.integer.config_autoBrightnessBrighteningLightDebounce))
+ .thenReturn(3000);
+ when(mResources.getInteger(
+ R.integer.config_autoBrightnessDarkeningLightDebounce))
+ .thenReturn(4000);
+
mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index e7dc48e..a56b59a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -43,6 +43,7 @@
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -52,6 +53,10 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.FloatProperty;
@@ -67,7 +72,10 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.feature.flags.Flags;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
@@ -97,6 +105,7 @@
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -104,12 +113,16 @@
private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE = 0.5f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE = 0.6f;
+ private static final long BRIGHTNESS_RAMP_INCREASE_MAX = 1000;
+ private static final long BRIGHTNESS_RAMP_DECREASE_MAX = 2000;
+ private static final long BRIGHTNESS_RAMP_INCREASE_MAX_IDLE = 3000;
+ private static final long BRIGHTNESS_RAMP_DECREASE_MAX_IDLE = 4000;
+
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
-
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@Mock
@@ -126,6 +139,8 @@
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Mock
private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlagsMock;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
@@ -141,6 +156,9 @@
.spyStatic(BatteryStatsService.class)
.build();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mClock = new OffsettableClock.Stopped();
@@ -331,9 +349,9 @@
when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness);
listener.onBrightnessChanged(leadBrightness);
advanceTime(1); // Send messages, run updatePowerState
- verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat());
+ verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false));
verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
- anyFloat());
+ anyFloat(), eq(false));
clearInvocations(mHolder.animator, followerDpc.animator);
@@ -347,9 +365,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -379,9 +397,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -409,9 +427,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -441,9 +459,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -480,11 +498,11 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
// One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow
verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux);
verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -511,10 +529,10 @@
// The second time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux);
verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
}
@Test
@@ -548,7 +566,7 @@
followerListener.onBrightnessChanged(initialFollowerBrightness);
advanceTime(1);
verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(followerDpc.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -569,11 +587,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -584,7 +602,7 @@
mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc);
advanceTime(1);
verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
when(followerDpc.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -602,10 +620,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
- verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat());
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(),
+ anyBoolean());
verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -648,9 +667,9 @@
secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
advanceTime(1);
verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(followerHolder.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -673,11 +692,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -688,13 +707,87 @@
mHolder.dpc.stop();
advanceTime(1);
verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
}
@Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ }
+
+ @Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -745,7 +838,7 @@
verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
.getAutomaticScreenBrightness();
- verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
}
@Test
@@ -783,7 +876,7 @@
verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
.getAutomaticScreenBrightness();
- verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
}
@Test
@@ -1026,7 +1119,8 @@
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
// One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged
- verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat());
+ verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(),
+ eq(false));
}
@Test
@@ -1135,7 +1229,7 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
brightness = 0.05f;
@@ -1147,7 +1241,7 @@
// The second time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false));
brightness = 0.9f;
when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
@@ -1157,22 +1251,162 @@
advanceTime(1); // Run updatePowerState
// The third time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false));
}
@Test
- public void testPowerStateStopsOnDpcStop() {
- // Set up
+ public void testRampRateForHdrContent_HdrClamperOff() {
+ float hdrBrightness = 0.8f;
+ float clampedBrightness = 0.6f;
+ float transitionRate = 1.5f;
+
DisplayPowerRequest dpr = new DisplayPowerRequest();
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
+ when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
+
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ }
+
+ @Test
+ public void testRampRateForHdrContent_HdrClamperOn() {
+ float clampedBrightness = 0.6f;
+ float transitionRate = 1.5f;
+ DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
+ when(flags.isHdrClamperEnabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
+ when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(),
+ eq(transitionRate), eq(true));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeInteractiveThenIdle() {
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
advanceTime(1);
- // Stop dpc
- mHolder.dpc.stop();
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true);
advanceTime(1);
- // Ensure dps has stopped
- verify(mHolder.displayPowerState, times(1)).stop();
+ // A second time, when switching to idle mode.
+ verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
+ DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
+ when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
+ advanceTime(1);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true);
+ advanceTime(1);
+
+ // A second time, when switching to idle mode.
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
+ BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeIdle() {
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+ // Run updatePowerState
+ advanceTime(1);
+ // Once on setup
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(true);
+
+ // A second time when switching to idle mode.
+ verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeIdle_DifferentValues() {
+ DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
+ when(flags.isAdaptiveTone1Enabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true, flags);
+
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
+ advanceTime(1);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(true);
+
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
+ BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
/**
@@ -1238,6 +1472,7 @@
});
when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
.thenReturn(new int[0]);
+
when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
.thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
when(displayDeviceConfigMock.getBrightnessRampFastIncrease())
@@ -1250,6 +1485,15 @@
.thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE);
when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle())
.thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE);
+
+ when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis())
+ .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX);
+ when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxMillis())
+ .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX);
+ when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxIdleMillis())
+ .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE);
+ when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxIdleMillis())
+ .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
@@ -1259,6 +1503,12 @@
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId, boolean isEnabled) {
+ return createDisplayPowerController(displayId, uniqueId, isEnabled,
+ mock(DisplayManagerFlags.class));
+ }
+
+ private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
+ String uniqueId, boolean isEnabled, DisplayManagerFlags flags) {
final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
final AutomaticBrightnessController automaticBrightnessController =
@@ -1270,12 +1520,14 @@
final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
mock(ScreenOffBrightnessSensorController.class);
final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
+ final HdrClamper hdrClamper = mock(HdrClamper.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
TestInjector injector = spy(new TestInjector(displayPowerState, animator,
automaticBrightnessController, wakelockController, brightnessMappingStrategy,
- hysteresisLevels, screenOffBrightnessSensorController, hbmController));
+ hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
+ flags));
final LogicalDisplay display = mock(LogicalDisplay.class);
final DisplayDevice device = mock(DisplayDevice.class);
@@ -1288,13 +1540,14 @@
final DisplayPowerController2 dpc = new DisplayPowerController2(
mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
- mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata, /* bootCompleted= */ false);
+ mBrightnessTrackerMock, brightnessSetting, () -> {
+ },
+ hbmMetadata, /* bootCompleted= */ false, flags);
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, wakelockController,
- screenOffBrightnessSensorController, hbmController, hbmMetadata,
- brightnessMappingStrategy, injector);
+ screenOffBrightnessSensorController, hbmController, hdrClamper, hbmMetadata,
+ brightnessMappingStrategy, injector, config);
}
/**
@@ -1311,9 +1564,12 @@
public final WakelockController wakelockController;
public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
public final HighBrightnessModeController hbmController;
+
+ public final HdrClamper hdrClamper;
public final HighBrightnessModeMetadata hbmMetadata;
public final BrightnessMappingStrategy brightnessMappingStrategy;
public final DisplayPowerController2.Injector injector;
+ public final DisplayDeviceConfig config;
DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display,
DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
@@ -1322,9 +1578,11 @@
WakelockController wakelockController,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeController hbmController,
+ HdrClamper hdrClamper,
HighBrightnessModeMetadata hbmMetadata,
BrightnessMappingStrategy brightnessMappingStrategy,
- DisplayPowerController2.Injector injector) {
+ DisplayPowerController2.Injector injector,
+ DisplayDeviceConfig config) {
this.dpc = dpc;
this.display = display;
this.displayPowerState = displayPowerState;
@@ -1334,9 +1592,11 @@
this.wakelockController = wakelockController;
this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
this.hbmController = hbmController;
+ this.hdrClamper = hdrClamper;
this.hbmMetadata = hbmMetadata;
this.brightnessMappingStrategy = brightnessMappingStrategy;
this.injector = injector;
+ this.config = config;
}
}
@@ -1350,13 +1610,19 @@
private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
private final HighBrightnessModeController mHighBrightnessModeController;
+ private final HdrClamper mHdrClamper;
+
+ private final DisplayManagerFlags mFlags;
+
TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
AutomaticBrightnessController automaticBrightnessController,
WakelockController wakelockController,
BrightnessMappingStrategy brightnessMappingStrategy,
HysteresisLevels hysteresisLevels,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
- HighBrightnessModeController highBrightnessModeController) {
+ HighBrightnessModeController highBrightnessModeController,
+ HdrClamper hdrClamper,
+ DisplayManagerFlags flags) {
mDisplayPowerState = dps;
mAnimator = animator;
mAutomaticBrightnessController = automaticBrightnessController;
@@ -1365,6 +1631,8 @@
mHysteresisLevels = hysteresisLevels;
mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
mHighBrightnessModeController = highBrightnessModeController;
+ mHdrClamper = hdrClamper;
+ mFlags = flags;
}
@Override
@@ -1471,6 +1739,15 @@
}
@Override
+ BrightnessRangeController getBrightnessRangeController(
+ HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+ DisplayDeviceConfig displayDeviceConfig, Handler handler,
+ DisplayManagerFlags flags) {
+ return new BrightnessRangeController(hbmController, modeChangeCallback,
+ displayDeviceConfig, mHdrClamper, mFlags);
+ }
+
+ @Override
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
SensorManager sensorManager, Resources resources) {
return mDisplayWhiteBalanceControllerMock;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e0c0ae2..0572117 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -43,6 +43,7 @@
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -52,6 +53,10 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.FloatProperty;
@@ -69,6 +74,8 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.feature.flags.Flags;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
@@ -97,6 +104,7 @@
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -104,6 +112,11 @@
private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE = 0.5f;
private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE = 0.6f;
+ private static final long BRIGHTNESS_RAMP_INCREASE_MAX = 1000;
+ private static final long BRIGHTNESS_RAMP_DECREASE_MAX = 2000;
+ private static final long BRIGHTNESS_RAMP_INCREASE_MAX_IDLE = 3000;
+ private static final long BRIGHTNESS_RAMP_DECREASE_MAX_IDLE = 4000;
+
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
@@ -126,6 +139,9 @@
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Mock
private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlagsMock;
+
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
@@ -144,6 +160,9 @@
@Rule
public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mClock = new OffsettableClock.Stopped();
@@ -333,9 +352,9 @@
listener.onBrightnessChanged(leadBrightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -351,9 +370,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -384,9 +403,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -415,9 +434,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -448,9 +467,9 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -488,11 +507,11 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
// One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow
verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux);
verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -519,10 +538,10 @@
// The second time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux);
verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
}
@Test
@@ -557,7 +576,7 @@
followerListener.onBrightnessChanged(initialFollowerBrightness);
advanceTime(1);
verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(followerDpc.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -578,11 +597,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -593,7 +612,7 @@
mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc);
advanceTime(1);
verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
when(followerDpc.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -611,10 +630,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
- verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat());
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(),
+ anyBoolean());
verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
@@ -658,9 +678,9 @@
secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
advanceTime(1);
verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(followerHolder.displayPowerState.getScreenBrightness())
.thenReturn(initialFollowerBrightness);
@@ -683,11 +703,11 @@
listener.onBrightnessChanged(brightness);
advanceTime(1); // Send messages, run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -698,9 +718,9 @@
mHolder.dpc.stop();
advanceTime(1);
verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
}
@@ -753,7 +773,7 @@
verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
.getAutomaticScreenBrightness();
- verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
}
@Test
@@ -790,7 +810,7 @@
verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
.getAutomaticScreenBrightness();
- verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
}
@Test
@@ -1035,7 +1055,8 @@
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
// One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged
- verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat());
+ verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(),
+ eq(false));
}
@Test
@@ -1144,7 +1165,7 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
brightness = 0.05f;
@@ -1156,7 +1177,7 @@
// The second time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false));
brightness = 0.9f;
when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
@@ -1166,22 +1187,182 @@
advanceTime(1); // Run updatePowerState
// The third time, the animation rate should be slow
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
- eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE));
+ eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false));
}
@Test
- public void testPowerStateStopsOnDpcStop() {
- // Set up
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ }
+
+ @Test
+ public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ final float sdrBrightness = 0.1f;
+ final float hdrBrightness = 0.3f;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+ when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+ when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+ clearInvocations(mHolder.animator);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+ eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeInteractiveThenIdle() {
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
advanceTime(1);
- // Stop dpc
- mHolder.dpc.stop();
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ // switch to idle
+ mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true);
advanceTime(1);
- // Ensure dps has stopped
- verify(mHolder.displayPowerState, times(1)).stop();
+ verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
+ when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
+ advanceTime(1);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ // switch to idle
+ mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true);
+ advanceTime(1);
+
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
+ BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeIdle() {
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
+ advanceTime(1);
+
+ // once on setup
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(true);
+
+ // second time when switching to idle screen brightness mode
+ verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
+ BRIGHTNESS_RAMP_DECREASE_MAX);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
+ public void testRampMaxTimeIdle_DifferentValues() {
+ when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+
+ // Send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState
+ advanceTime(1);
+
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mHolder.config, /* isEnabled= */ true);
+
+ // switch to idle mode
+ mHolder.dpc.setAutomaticScreenBrightnessMode(true);
+
+ verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
+ BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
private void advanceTime(long timeMs) {
@@ -1239,6 +1420,7 @@
});
when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
.thenReturn(new int[0]);
+
when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
.thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
when(displayDeviceConfigMock.getBrightnessRampFastIncrease())
@@ -1251,6 +1433,15 @@
.thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE);
when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle())
.thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE);
+
+ when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis())
+ .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX);
+ when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxMillis())
+ .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX);
+ when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxIdleMillis())
+ .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE);
+ when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxIdleMillis())
+ .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
@@ -1289,11 +1480,11 @@
mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
- hbmMetadata, /* bootCompleted= */ false);
+ hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock);
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, screenOffBrightnessSensorController,
- hbmController, hbmMetadata, brightnessMappingStrategy, injector);
+ hbmController, hbmMetadata, brightnessMappingStrategy, injector, config);
}
/**
@@ -1312,6 +1503,7 @@
public final HighBrightnessModeMetadata hbmMetadata;
public final BrightnessMappingStrategy brightnessMappingStrategy;
public final DisplayPowerController.Injector injector;
+ public final DisplayDeviceConfig config;
DisplayPowerControllerHolder(DisplayPowerController dpc, LogicalDisplay display,
DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
@@ -1321,7 +1513,8 @@
HighBrightnessModeController hbmController,
HighBrightnessModeMetadata hbmMetadata,
BrightnessMappingStrategy brightnessMappingStrategy,
- DisplayPowerController.Injector injector) {
+ DisplayPowerController.Injector injector,
+ DisplayDeviceConfig config) {
this.dpc = dpc;
this.display = display;
this.displayPowerState = displayPowerState;
@@ -1333,6 +1526,7 @@
this.hbmMetadata = hbmMetadata;
this.brightnessMappingStrategy = brightnessMappingStrategy;
this.injector = injector;
+ this.config = config;
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java
deleted file mode 100644
index 167a412..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
-import static org.mockito.Mockito.times;
-
-import android.os.Handler;
-import android.os.test.TestLooper;
-import android.view.Display;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-
-@SmallTest
-public class DisplayPowerStateTest {
- private static final int DISPLAY_ID = 123;
-
- private DisplayPowerState mDisplayPowerState;
- private TestLooper mTestLooper;
- @Mock
- private DisplayBlanker mDisplayBlankerMock;
- @Mock
- private ColorFade mColorFadeMock;
-
- @Rule
- public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Before
- public void setUp() {
- mTestLooper = new TestLooper();
- mDisplayPowerState = new DisplayPowerState(
- mDisplayBlankerMock, mColorFadeMock, DISPLAY_ID, Display.STATE_ON,
- new Handler(mTestLooper.getLooper()));
- }
-
- @Test
- public void testColorFadeStopsOnDpsStop() {
- mDisplayPowerState.stop();
- verify(mColorFadeMock, times(1)).stop();
- }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index c0128ae..1c43418 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -28,7 +28,9 @@
import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
+import android.content.Context;
import android.graphics.Point;
+import android.os.IBinder;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -40,7 +42,6 @@
import com.android.server.display.layout.Layout;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.InputStream;
@@ -56,6 +57,9 @@
private LogicalDisplay mLogicalDisplay;
private DisplayDevice mDisplayDevice;
+ private DisplayAdapter mDisplayAdapter;
+ private Context mContext;
+ private IBinder mDisplayToken;
private DisplayDeviceRepository mDeviceRepo;
private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo();
@@ -64,6 +68,9 @@
// Share classloader to allow package private access.
System.setProperty("dexmaker.share_classloader", "true");
mDisplayDevice = mock(DisplayDevice.class);
+ mDisplayAdapter = mock(DisplayAdapter.class);
+ mContext = mock(Context.class);
+ mDisplayToken = mock(IBinder.class);
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
mDisplayDeviceInfo.copyFrom(new DisplayDeviceInfo());
@@ -131,33 +138,43 @@
assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
}
- // TODO: b/288880734 - fix test after display tests migration
@Test
- @Ignore
public void testDisplayInputFlags() {
+ DisplayDevice displayDevice = new DisplayDevice(mDisplayAdapter, mDisplayToken,
+ "unique_display_id", mContext) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return false;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return mDisplayDeviceInfo;
+ }
+ };
SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
- mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ mLogicalDisplay.configureDisplayLocked(t, displayDevice, false);
verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
reset(t);
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
- mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ mLogicalDisplay.configureDisplayLocked(t, displayDevice, false);
verify(t).setDisplayFlags(any(), eq(0));
reset(t);
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
- mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ mLogicalDisplay.configureDisplayLocked(t, displayDevice, false);
verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
reset(t);
mLogicalDisplay.setEnabledLocked(false);
- mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ mLogicalDisplay.configureDisplayLocked(t, displayDevice, false);
verify(t).setDisplayFlags(any(), eq(0));
reset(t);
mLogicalDisplay.setEnabledLocked(true);
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
- mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ mLogicalDisplay.configureDisplayLocked(t, displayDevice, false);
verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
reset(t);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
new file mode 100644
index 0000000..810a8b2
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.FloatProperty;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class RampAnimatorTest {
+
+ private static final float FLOAT_TOLERANCE = 0.0000001f;
+
+ private RampAnimator<TestObject> mRampAnimator;
+
+ private final TestObject mTestObject = new TestObject();
+
+ private final FloatProperty<TestObject> mTestProperty = new FloatProperty<>("mValue") {
+ @Override
+ public void setValue(TestObject object, float value) {
+ object.mValue = value;
+ }
+
+ @Override
+ public Float get(TestObject object) {
+ return object.mValue;
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mRampAnimator = new RampAnimator<>(mTestObject, mTestProperty, () -> 0);
+ }
+
+ @Test
+ public void testInitialValueUsedInLastAnimationStep() {
+ mRampAnimator.setAnimationTarget(0.67f, 0.1f, false);
+
+ assertEquals(0.67f, mTestObject.mValue, 0);
+ }
+
+ @Test
+ public void testAnimationStep_respectTimeLimits() {
+ // animation is limited to 2s
+ mRampAnimator.setAnimationTimeLimits(2_000, 2_000);
+ // initial brightness value, applied immediately, in HLG = 0.8716434
+ mRampAnimator.setAnimationTarget(0.5f, 0.1f, false);
+ // expected brightness, in HLG = 0.9057269
+ // delta = 0.0340835, duration = 3.40835s > 2s
+ // new rate = delta/2 = 0.01704175 u/s
+ mRampAnimator.setAnimationTarget(0.6f, 0.01f, false);
+ // animation step = 1s, new HGL = 0.88868515
+ mRampAnimator.performNextAnimationStep(1_000_000_000);
+ // converted back to Linear
+ assertEquals(0.54761934f, mTestObject.mValue, FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testAnimationStep_ignoreTimeLimits() {
+ // animation is limited to 2s
+ mRampAnimator.setAnimationTimeLimits(2_000, 2_000);
+ // initial brightness value, applied immediately, in HLG = 0.8716434
+ mRampAnimator.setAnimationTarget(0.5f, 0.1f, false);
+ // rate = 0.01f, time limits are ignored
+ mRampAnimator.setAnimationTarget(0.6f, 0.01f, true);
+ // animation step = 1s, new HGL = 0.8816434
+ mRampAnimator.performNextAnimationStep(1_000_000_000);
+ // converted back to Linear
+ assertEquals(0.52739114f, mTestObject.mValue, FLOAT_TOLERANCE);
+ }
+
+ private static class TestObject {
+ private float mValue;
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
new file mode 100644
index 0000000..37d966d
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.display.config.HdrBrightnessData;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Map;
+
+@SmallTest
+public class HdrClamperTest {
+
+ public static final float FLOAT_TOLERANCE = 0.0001f;
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockListener;
+
+ OffsettableClock mClock = new OffsettableClock.Stopped();
+
+ private final TestHandler mTestHandler = new TestHandler(null, mClock);
+
+
+ private HdrClamper mHdrClamper;
+
+
+ @Before
+ public void setUp() {
+ mHdrClamper = new HdrClamper(mMockListener, mTestHandler);
+ configureClamper();
+ }
+
+ @Test
+ public void testClamper_AmbientLuxChangesAboveLimit() {
+ mHdrClamper.onAmbientLuxChange(500);
+
+ assertFalse(mTestHandler.hasMessagesOrCallbacks());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testClamper_AmbientLuxChangesBelowLimit() {
+ mHdrClamper.onAmbientLuxChange(499);
+
+ assertTrue(mTestHandler.hasMessagesOrCallbacks());
+ TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+ assertEquals(2000, msgInfo.sendTime);
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+
+ mClock.fastForward(2000);
+ mTestHandler.timeAdvance();
+ assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testClamper_AmbientLuxChangesBelowLimit_ThenFastAboveLimit() {
+ mHdrClamper.onAmbientLuxChange(499);
+ mHdrClamper.onAmbientLuxChange(500);
+
+ assertFalse(mTestHandler.hasMessagesOrCallbacks());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() {
+ mHdrClamper.onAmbientLuxChange(499);
+ mClock.fastForward(2000);
+ mTestHandler.timeAdvance();
+
+ mHdrClamper.onAmbientLuxChange(500);
+
+ assertTrue(mTestHandler.hasMessagesOrCallbacks());
+ TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+ assertEquals(3000, msgInfo.sendTime); // 2000 + 1000
+
+ mClock.fastForward(1000);
+ mTestHandler.timeAdvance();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ }
+
+ private void configureClamper() {
+ HdrBrightnessData data = new HdrBrightnessData(
+ Map.of(500f, 0.6f),
+ /* brightnessIncreaseDebounceMillis= */ 1000,
+ /* brightnessIncreaseDurationMillis= */ 1500,
+ /* brightnessDecreaseDebounceMillis= */ 2000,
+ /* brightnessDecreaseDurationMillis= */2500
+ );
+ mHdrClamper.resetHdrConfig(data);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 525bfd7..5b1508b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -16,7 +16,6 @@
package com.android.server;
import static androidx.test.InstrumentationRegistry.getContext;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -42,7 +41,6 @@
import static com.android.server.DeviceIdleController.STATE_SENSING;
import static com.android.server.DeviceIdleController.lightStateToString;
import static com.android.server.DeviceIdleController.stateToString;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -2418,7 +2416,7 @@
}
@Test
- public void testModeManager_NoModeManagerLocalService_AddListenerNotCalled() {
+ public void testModeManager_NoModeManagerLocalService_AddQuickDozeListenerNotCalled() {
mConstants.USE_MODE_MANAGER = true;
doReturn(null)
.when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -2430,6 +2428,33 @@
}
@Test
+ public void testModeManager_NoModeManagerLocalService_AddOffBodyListenerNotCalled() {
+ mConstants.USE_MODE_MANAGER = true;
+ doReturn(null)
+ .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+ eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+ }
+
+ @Test
+ public void testModeManager_USEMODEMANAGERIsFalse_AddListenerNotCalled() {
+ mConstants.USE_MODE_MANAGER = false;
+ doReturn(new Object())
+ .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+ eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+ verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+ eq(WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER), any(),
+ eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer));
+ }
+
+ @Test
public void testModeManager_NoBatterySaver_QuickDoze() {
mConstants.USE_MODE_MANAGER = true;
PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
@@ -2469,6 +2494,102 @@
assertTrue(mDeviceIdleController.isQuickDozeEnabled());
}
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverDisabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ false).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeRequestedBatterySaverDisabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ false).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
+ @Test
+ public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+ mConstants.USE_MODE_MANAGER = true;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+ true).build();
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(powerSaveState);
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+
+ mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+ mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+ assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
index 8e17b3a..dcd5317 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
@@ -17,26 +17,33 @@
package com.android.server.backup.utils;
import static org.junit.Assert.assertTrue;
-
+import static org.testng.AssertJUnit.assertFalse;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupManagerMonitor;
import android.os.Bundle;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-
import java.io.File;
+import java.io.FileWriter;
public class BackupManagerMonitorDumpsysUtilsTest {
- private File mTempFile;
+ private long mRetentionPeriod;
+ private File mTempBMMEventsFile;
+ private File mTempSetUpDateFile;
+
+ private long mSizeLimit;
private TestBackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
@Before
public void setUp() throws Exception {
- mTempFile = tmp.newFile("testbmmevents.txt");
+ mRetentionPeriod = 30 * 60 * 1000;
+ mSizeLimit = 25 * 1024 * 1000;
+ mTempBMMEventsFile = tmp.newFile("testbmmevents.txt");
+ mTempSetUpDateFile = tmp.newFile("testSetUpDate.txt");
mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
}
@@ -46,7 +53,7 @@
throws Exception {
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(null);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
}
@@ -57,7 +64,7 @@
event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
}
@Test
@@ -67,18 +74,236 @@
event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_eventWithCategoryAndId_eventIsWrittenToFile()
+ throws Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_firstEvent_recordSetUpTimestamp()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length()==0);
+ assertTrue(mTempSetUpDateFile.length()==0);
+
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ assertTrue(mTempSetUpDateFile.length()!=0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_notFirstEvent_doNotChangeSetUpTimestamp()
+ throws Exception {
+ Bundle event1 = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event1);
+ String setUpTimestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ Bundle event2 = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event2);
+ String setUpTimestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ assertTrue(setUpTimestampBefore.equals(setUpTimestampAfter));
+ }
+
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_fileOverSizeLimit_doNotRecordEvents()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ long fileSizeBefore = mTempBMMEventsFile.length();
+
+ mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(0);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ long fileSizeAfter = mTempBMMEventsFile.length();
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile));
+ assertTrue(fileSizeBefore == fileSizeAfter);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_fileUnderSizeLimit_recordEvents()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ Bundle event = createRestoreBMMEvent();
+
+ mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(25 * 1024 * 1000);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile));
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void deleteExpiredBackupManagerMonitorEvent_eventsAreExpired_deleteEventsAndReturnTrue()
+ throws Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ // Re-initialise the test BackupManagerMonitorDumpsysUtils to
+ // clear the cached value of isAfterRetentionPeriod
+ mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
+
+ // set a retention period of 0 second
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0);
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents());
+ assertFalse(mTempBMMEventsFile.exists());
+ }
+
+ @Test
+ public void deleteExpiredBackupManagerMonitorEvent_eventsAreNotExpired_returnFalse() throws
+ Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertTrue(mTempBMMEventsFile.length() != 0);
+
+ // set a retention period of 30 minutes
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents());
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_afterRetentionPeriod_returnTrue() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ // set a retention period of 0 second
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0);
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_beforeRetentionPeriod_returnFalse() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ // set a retention period of 30 minutes
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_noSetupDate_returnFalse() throws
+ Exception {
+ assertTrue(mTempSetUpDateFile.length() == 0);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsAfterThanDate2_returnTrue() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 - 1;
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 0));
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsAfterNMillisecFromDate2_returnTrue() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 + 10;
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 10));
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsLessThanNMillisecFromDate2_returnFalse() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 + 10;
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 11));
+ }
+
+ @Test
+ public void recordSetUpTimestamp_timestampNotSetBefore_setTimestamp() throws
+ Exception {
+ assertTrue(mTempSetUpDateFile.length() == 0);
+
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ }
+
+ @Test
+ public void recordSetUpTimestamp_timestampSetBefore_doNothing() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ String timestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ String timestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+ assertTrue(timestampAfter.equals(timestampBefore));
+ }
+
+ private Bundle createRestoreBMMEvent() {
+ Bundle event = new Bundle();
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE,
+ BackupAnnotations.OperationType.RESTORE);
+ return event;
}
private class TestBackupManagerMonitorDumpsysUtils
extends BackupManagerMonitorDumpsysUtils {
+
+ private long testRetentionPeriod;
+ private long testSizeLimit;
+
TestBackupManagerMonitorDumpsysUtils() {
super();
+ this.testRetentionPeriod = mRetentionPeriod;
+ this.testSizeLimit = mSizeLimit;
+ }
+
+ public void setTestRetentionPeriod(long testRetentionPeriod) {
+ this.testRetentionPeriod = testRetentionPeriod;
+ }
+ public void setTestSizeLimit(long testSizeLimit) {
+ this.testSizeLimit = testSizeLimit;
}
@Override
public File getBMMEventsFile() {
- return mTempFile;
+ return mTempBMMEventsFile;
}
+
+ @Override
+ File getSetUpDateFile() {
+ return mTempSetUpDateFile;
+ }
+
+ @Override
+ long getRetentionPeriodInMillisec() {
+ return testRetentionPeriod;
+ }
+
+ @Override
+ long getBMMEventsFileSizeLimit(){
+ return testSizeLimit;
+ }
+
+
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
index 3af2932..604a68d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
@@ -340,8 +340,9 @@
@Test
public void putMonitoringExtraLong_bundleExists_fillsBundleCorrectly() throws Exception {
Bundle bundle = new Bundle();
+ long value = 123;
- Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 123);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", value);
assertThat(result).isEqualTo(bundle);
assertThat(result.size()).isEqualTo(1);
@@ -350,7 +351,8 @@
@Test
public void putMonitoringExtraLong_bundleDoesNotExist_fillsBundleCorrectly() throws Exception {
- Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 123);
+ long value = 123;
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", value);
assertThat(result).isNotNull();
assertThat(result.size()).isEqualTo(1);
@@ -377,4 +379,25 @@
assertThat(result.size()).isEqualTo(1);
assertThat(result.getBoolean("key")).isTrue();
}
+
+ @Test
+ public void putMonitoringExtraInt_bundleExists_fillsBundleCorrectly() throws Exception {
+ Bundle bundle = new Bundle();
+
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 1);
+
+ assertThat(result).isEqualTo(bundle);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getInt("key")).isEqualTo(1);
+ }
+
+ @Test
+ public void putMonitoringExtraInt_bundleDoesNotExist_fillsBundleCorrectly()
+ throws Exception {
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 1);
+
+ assertThat(result).isNotNull();
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getInt("key")).isEqualTo(1);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 552b59c..a250ac7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -33,7 +33,6 @@
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -75,11 +74,11 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.platform.test.annotations.LargeTest;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
@@ -2597,7 +2596,9 @@
@Test
public void testIsWithinEJQuotaLocked_TempAllowlisting_Restricted() {
setDischarging();
- JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1);
+ JobStatus js =
+ createExpeditedJobStatus(
+ "testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1);
setStandbyBucket(RESTRICTED_INDEX, js);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -6088,7 +6089,8 @@
Handler handler = mQuotaController.getHandler();
spyOn(handler);
- JobStatus job = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1);
+ JobStatus job =
+ createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1);
setStandbyBucket(RESTRICTED_INDEX, job);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeStartTrackingJobLocked(job, null);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index d988063..eb50556 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -17,8 +17,11 @@
package com.android.server.pm;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -157,11 +160,12 @@
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
mock(Resources.class));
- when(mIcon.compress(eq(Bitmap.CompressFormat.PNG), eq(100), any())).thenReturn(true);
mArchiveManager = spy(new PackageArchiver(mContext, pm));
doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
- any(LauncherActivityInfo.class), eq(mUserId));
+ any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+ doReturn(mIcon).when(mArchiveManager).decodeIcon(
+ any(ArchiveState.ArchiveActivityInfo.class));
}
@Test
@@ -249,7 +253,7 @@
public void archiveApp_storeIconFails() throws IntentSender.SendIntentException, IOException {
IOException e = new IOException("IO");
doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
- any(LauncherActivityInfo.class), eq(mUserId));
+ any(LauncherActivityInfo.class), eq(mUserId), anyInt());
mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
@@ -272,7 +276,7 @@
verify(mInstallerService).uninstall(
eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
- eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender),
+ eq(CALLER_PACKAGE), eq(DELETE_ARCHIVE | DELETE_KEEP_DATA), eq(mIntentSender),
eq(UserHandle.CURRENT.getIdentifier()), anyInt());
assertThat(mPackageSetting.readUserState(
UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo(
@@ -311,7 +315,8 @@
}
@Test
- public void unarchiveApp_notArchived() {
+ public void unarchiveApp_notArchived_missingArchiveState() {
+ mUserState.setInstalled(false);
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE,
@@ -322,8 +327,21 @@
}
@Test
- public void unarchiveApp_noInstallerFound() {
+ public void unarchiveApp_notArchived_stillInstalled() {
mUserState.setArchiveState(createArchiveState());
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s is not currently archived.", PACKAGE));
+ }
+
+
+ @Test
+ public void unarchiveApp_noInstallerFound() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
InstallSource otherInstallSource =
InstallSource.create(
CALLER_PACKAGE,
@@ -372,6 +390,38 @@
assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
}
+ @Test
+ public void getArchivedAppIcon_packageNotInstalled() {
+ when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
+ null);
+
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
+ }
+
+ @Test
+ public void getArchivedAppIcon_notArchived() {
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s is not currently archived.", PACKAGE));
+ }
+
+ @Test
+ public void getArchivedAppIcon_success() {
+ mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+
+ assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isEqualTo(
+ mIcon);
+ }
+
+
private static ArchiveState createArchiveState() {
List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 4a06611f..1cd61e9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -253,7 +253,8 @@
goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
moveEachPointers(mLastEvent, p(10, 10));
send(mLastEvent);
-
+ // Wait 10 ms to make sure that hover enter and exit are not scheduled for the same moment.
+ mHandler.fastForward(10);
send(upEvent());
// Wait for sending hover exit event to transit to clear state.
mHandler.fastForward(USER_INTENT_TIMEOUT);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
index 8faddf8..fcfe48e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
@@ -17,7 +17,6 @@
package com.android.server.accessibility.magnification;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -27,13 +26,13 @@
import android.graphics.Rect;
import android.os.Handler;
-import android.platform.test.annotations.LargeTest;
import android.testing.TestableContext;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
new file mode 100644
index 0000000..9fdbdda
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:AnrTimerTest
+ */
+@SmallTest
+@Presubmit
+public class AnrTimerTest {
+
+ /**
+ * A handler that allows control over when to dispatch messages and callbacks. Because most
+ * Handler methods are final, the only thing this handler can intercept is sending messages.
+ * This handler allows unit tests to be written without a need to sleep (which leads to flaky
+ * tests).
+ *
+ * This code was cloned from {@link com.android.systemui.utils.os.FakeHandler}.
+ */
+ static class TestHandler extends Handler {
+
+ private boolean mImmediate = true;
+ private ArrayList<Message> mQueuedMessages = new ArrayList<>();
+
+ ArrayList<Long> mDelays = new ArrayList<>();
+
+ TestHandler(Looper looper, Callback callback, boolean immediate) {
+ super(looper, callback);
+ mImmediate = immediate;
+ }
+
+ TestHandler(Looper looper, Callback callback) {
+ this(looper, callback, true);
+ }
+
+ /**
+ * Override sendMessageAtTime. In immediate mode, the message is immediately dispatched.
+ * In non-immediate mode, the message is enqueued to the real handler. In both cases, the
+ * original delay is computed by comparing the target dispatch time with 'now'. This
+ * computation is prone to errors if the code experiences delays. The computed time is
+ * captured in the mDelays list.
+ */
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ long delay = uptimeMillis - SystemClock.uptimeMillis();
+ mDelays.add(delay);
+ if (mImmediate) {
+ mQueuedMessages.add(msg);
+ dispatchQueuedMessages();
+ } else {
+ super.sendMessageAtTime(msg, uptimeMillis);
+ }
+ return true;
+ }
+
+ void setImmediate(boolean immediate) {
+ mImmediate = immediate;
+ }
+
+ /** Dispatch any messages that have been queued on the calling thread. */
+ void dispatchQueuedMessages() {
+ ArrayList<Message> messages = new ArrayList<>(mQueuedMessages);
+ mQueuedMessages.clear();
+ for (Message msg : messages) {
+ dispatchMessage(msg);
+ }
+ }
+
+ /**
+ * Compare the captured delays with the input array. The comparison is fuzzy because the
+ * captured delay (see sendMessageAtTime) is affected by process delays.
+ */
+ void verifyDelays(long[] r) {
+ final long FUZZ = 10;
+ assertEquals(r.length, mDelays.size());
+ for (int i = 0; i < mDelays.size(); i++) {
+ long t = r[i];
+ long v = mDelays.get(i);
+ assertTrue(v >= t - FUZZ && v <= t + FUZZ);
+ }
+ }
+ }
+
+ private Handler mHandler;
+ private CountDownLatch mLatch = null;
+ private ArrayList<Message> mMessages;
+
+ // The commonly used message timeout key.
+ private static final int MSG_TIMEOUT = 1;
+
+ @Before
+ public void setUp() {
+ mHandler = new Handler(Looper.getMainLooper(), this::expirationHandler);
+ mMessages = new ArrayList<>();
+ mLatch = new CountDownLatch(1);
+ AnrTimer.resetTimerListForHermeticTest();
+ }
+
+ @After
+ public void tearDown() {
+ mHandler = null;
+ mMessages = null;
+ }
+
+ // When a timer expires, set the expiration time in the message and add it to the queue.
+ private boolean expirationHandler(Message msg) {
+ mMessages.add(Message.obtain(msg));
+ mLatch.countDown();
+ return false;
+ }
+
+ // The test argument includes a pid and uid, and a tag. The tag is used to distinguish
+ // different message instances.
+ private static class TestArg {
+ final int pid;
+ final int uid;
+ final int tag;
+
+ TestArg(int pid, int uid, int tag) {
+ this.pid = pid;
+ this.uid = uid;
+ this.tag = tag;
+ }
+ @Override
+ public String toString() {
+ return String.format("pid=%d uid=%d tag=%d", pid, uid, tag);
+ }
+ }
+
+ /**
+ * An instrumented AnrTimer.
+ */
+ private class TestAnrTimer extends AnrTimer {
+ // A local copy of 'what'. The field in AnrTimer is private.
+ final int mWhat;
+
+ TestAnrTimer(Handler h, int key, String tag) {
+ super(h, key, tag);
+ mWhat = key;
+ }
+
+ TestAnrTimer() {
+ this(mHandler, MSG_TIMEOUT, caller());
+ }
+
+ TestAnrTimer(Handler h, int key, String tag, boolean extend, TestInjector injector) {
+ super(h, key, tag, extend, injector);
+ mWhat = key;
+ }
+
+ TestAnrTimer(boolean extend, TestInjector injector) {
+ this(mHandler, MSG_TIMEOUT, caller(), extend, injector);
+ }
+
+ // Return the name of method that called the constructor, assuming that this function is
+ // called from inside the constructor. The calling method is used to name the AnrTimer
+ // instance so that logs are easier to understand.
+ private static String caller() {
+ final int n = 4;
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ if (stack.length < n+1) return "test";
+ return stack[n].getMethodName();
+ }
+
+ boolean start(TestArg arg, long millis) {
+ return start(arg, arg.pid, arg.uid, millis);
+ }
+
+ int what() {
+ return mWhat;
+ }
+ }
+
+ private static class TestTracker extends AnrTimer.CpuTracker {
+ long index = 0;
+ final int skip;
+ TestTracker(int skip) {
+ this.skip = skip;
+ }
+ long delay(int pid) {
+ return index++ * skip;
+ }
+ }
+
+ private class TestInjector extends AnrTimer.Injector {
+ final boolean mImmediate;
+ final AnrTimer.CpuTracker mTracker;
+ TestHandler mTestHandler;
+
+ TestInjector(int skip, boolean immediate) {
+ mTracker = new TestTracker(skip);
+ mImmediate = immediate;
+ }
+
+ TestInjector(int skip) {
+ this(skip, true);
+ }
+
+ @Override
+ Handler getHandler(Handler.Callback callback) {
+ if (mTestHandler == null) {
+ mTestHandler = new TestHandler(mHandler.getLooper(), callback, mImmediate);
+ }
+ return mTestHandler;
+ }
+
+ /** Fetch the allocated handle. This does not check for nulls. */
+ TestHandler getHandler() {
+ return mTestHandler;
+ }
+
+ AnrTimer.CpuTracker getTracker() {
+ return mTracker;
+ }
+ }
+
+ // Tests
+ // 1. Start a timer and wait for expiration.
+ // 2. Start a timer and cancel it. Verify no expiration.
+ // 3. Start a timer. Shortly thereafter, restart it. Verify only one expiration.
+ // 4. Start a couple of timers. Verify max active timers. Discard one and verify the active
+ // count drops by 1. Accept one and verify the active count drops by 1.
+
+
+ @Test
+ public void testSimpleTimeout() throws Exception {
+ // Create an immediate TestHandler.
+ TestInjector injector = new TestInjector(0);
+ TestAnrTimer timer = new TestAnrTimer(false, injector);
+ TestArg t = new TestArg(1, 1, 3);
+ assertTrue(timer.start(t, 10));
+ // Delivery is immediate but occurs on a different thread.
+ assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+ assertEquals(1, mMessages.size());
+ Message m = mMessages.get(0);
+ assertEquals(timer.what(), m.what);
+ assertEquals(t, m.obj);
+
+ // Verify that the timer is still present.
+ assertEquals(1, AnrTimer.sizeOfTimerList());
+ assertTrue(timer.accept(t));
+ assertEquals(0, AnrTimer.sizeOfTimerList());
+
+ // Verify that the timer no longer exists.
+ assertFalse(timer.accept(t));
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ // Create an non-immediate TestHandler.
+ TestInjector injector = new TestInjector(0, false);
+ TestAnrTimer timer = new TestAnrTimer(false, injector);
+
+ Handler handler = injector.getHandler();
+ assertNotNull(handler);
+ assertTrue(handler instanceof TestHandler);
+
+ // The tests that follow check for a 'what' of 0 (zero), which is the message key used
+ // by AnrTimer internally.
+ TestArg t = new TestArg(1, 1, 3);
+ assertFalse(handler.hasMessages(0));
+ assertTrue(timer.start(t, 100));
+ assertTrue(handler.hasMessages(0));
+ assertTrue(timer.cancel(t));
+ assertFalse(handler.hasMessages(0));
+
+ // Verify that no expiration messages were delivered.
+ assertEquals(0, mMessages.size());
+ assertEquals(0, AnrTimer.sizeOfTimerList());
+ }
+
+ @Test
+ public void testRestart() throws Exception {
+ // Create an non-immediate TestHandler.
+ TestInjector injector = new TestInjector(0, false);
+ TestAnrTimer timer = new TestAnrTimer(false, injector);
+
+ TestArg t = new TestArg(1, 1, 3);
+ assertTrue(timer.start(t, 2500));
+ assertTrue(timer.start(t, 1000));
+
+ // Verify that the test handler saw two timeouts.
+ injector.getHandler().verifyDelays(new long[] { 2500, 1000 });
+
+ // Verify that there is a single timer. Then cancel it.
+ assertEquals(1, AnrTimer.sizeOfTimerList());
+ assertTrue(timer.cancel(t));
+ assertEquals(0, AnrTimer.sizeOfTimerList());
+ }
+
+ @Test
+ public void testExtendNormal() throws Exception {
+ // Create an immediate TestHandler.
+ TestInjector injector = new TestInjector(5);
+ TestAnrTimer timer = new TestAnrTimer(true, injector);
+ TestArg t = new TestArg(1, 1, 3);
+ assertTrue(timer.start(t, 10));
+
+ assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+ assertEquals(1, mMessages.size());
+ Message m = mMessages.get(0);
+ assertEquals(timer.what(), m.what);
+ assertEquals(t, m.obj);
+
+ // Verify that the test handler saw two timeouts: one of 10ms and one of 5ms.
+ injector.getHandler().verifyDelays(new long[] { 10, 5 });
+
+ // Verify that the timer is still present. Then remove it and verify that the list is
+ // empty.
+ assertEquals(1, AnrTimer.sizeOfTimerList());
+ assertTrue(timer.accept(t));
+ assertEquals(0, AnrTimer.sizeOfTimerList());
+ }
+
+ @Test
+ public void testExtendOversize() throws Exception {
+ // Create an immediate TestHandler.
+ TestInjector injector = new TestInjector(25);
+ TestAnrTimer timer = new TestAnrTimer(true, injector);
+ TestArg t = new TestArg(1, 1, 3);
+ assertTrue(timer.start(t, 10));
+
+ assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+ assertEquals(1, mMessages.size());
+ Message m = mMessages.get(0);
+ assertEquals(timer.what(), m.what);
+ assertEquals(t, m.obj);
+
+ // Verify that the test handler saw two timeouts: one of 10ms and one of 10ms.
+ injector.getHandler().verifyDelays(new long[] { 10, 10 });
+
+ // Verify that the timer is still present. Then remove it and verify that the list is
+ // empty.
+ assertEquals(1, AnrTimer.sizeOfTimerList());
+ assertTrue(timer.accept(t));
+ assertEquals(0, AnrTimer.sizeOfTimerList());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
index f5005fd..38bb3de 100644
--- a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
@@ -152,6 +152,50 @@
}
@Test
+ public void testApiStartStopFgs() throws InterruptedException {
+ ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+ record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+ mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+ 1, 1, "aPackageHasNoName");
+ Thread.sleep(2000);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+ resetAndVerifyZeroInteractions();
+ }
+
+ @Test
+ public void testFgsStartStopApiStartStop() throws InterruptedException {
+ ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+ record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+ mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceStop(1, record);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+ 1, 1, "aPackageHasNoName");
+ Thread.sleep(2000);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+ resetAndVerifyZeroInteractions();
+ }
+
+ @Test
public void testMultipleStartStopApis() throws InterruptedException {
ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index a11a8f5..d2e83e9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -87,6 +87,8 @@
public void setUp() {
when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+ .thenReturn(true);
when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold),
anyInt(), anyInt())).thenReturn(FRR_THRESHOLD);
@@ -110,7 +112,6 @@
0 /* modality */, mBiometricNotification);
}
-
@Test
public void authenticate_authenticationSucceeded_mapShouldBeUpdated() {
// Assert that the user doesn't exist in the map initially.
@@ -342,4 +343,32 @@
// Assert that notification count has been updated.
assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
+
+ @Test
+ public void authenticate_featureDisabled_mapMustNotBeUpdated() {
+ // Disable the feature.
+ when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+ .thenReturn(false);
+ AuthenticationStatsCollector authenticationStatsCollector =
+ new AuthenticationStatsCollector(mContext, 0 /* modality */,
+ mBiometricNotification);
+
+ authenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
+ new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */,
+ 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */,
+ 0 /* modality */));
+
+ authenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
+
+ // Assert that no notification should be sent.
+ verify(mBiometricNotification, never()).sendFaceEnrollNotification(any());
+ verify(mBiometricNotification, never()).sendFpEnrollNotification(any());
+ // Assert that data hasn't been updated.
+ AuthenticationStats authenticationStats = authenticationStatsCollector
+ .getAuthenticationStatsForUser(USER_ID_1);
+ assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
+ assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
+ assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index c40ad28..1c48b8a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -88,7 +88,8 @@
/* secureWindowCallback= */ null,
/* intentListenerCallback= */ null,
/* displayCategories= */ new ArraySet<>(),
- /* showTasksInHostDeviceRecents= */ true);
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 8f88ba6..7d73563 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -30,7 +30,6 @@
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertThrows;
-import android.app.ActivityManager;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -582,10 +581,10 @@
// When the app is foreground, the state should not change
() -> {
int pid = Binder.getCallingPid();
- when(mWindowProcessController.getPid()).thenReturn(pid);
+ int uid = Binder.getCallingUid();
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ true /* foregroundActivities */);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -594,8 +593,11 @@
() -> {
when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ false /* foregroundActivities */);
+
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index 728606f..bb0de03 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -33,6 +33,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -89,11 +91,12 @@
@Before
public void setup() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- Handler handler = mock(Handler.class);
PackageManager packageManager = mock(PackageManager.class);
Runnable cancelStateRunnable = mock(Runnable.class);
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+ Handler handler = new DeviceStateNotificationControllerTestHandler(Looper.getMainLooper());
+
final SparseArray<DeviceStateNotificationController.NotificationInfo> notificationInfos =
new SparseArray<>();
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
@@ -259,4 +262,16 @@
assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
clearInvocations(mNotificationInfoProvider);
}
+
+ private static class DeviceStateNotificationControllerTestHandler extends Handler {
+ DeviceStateNotificationControllerTestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 0d172fdb..708ee35 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -21,8 +21,15 @@
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_HOTPLUG;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_SCREEN_ON;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_SOUNDBAR_MODE;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
import static com.google.common.truth.Truth.assertThat;
@@ -51,6 +58,7 @@
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
@@ -71,6 +79,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
/**
* Tests for {@link HdmiControlService} class.
@@ -137,7 +146,7 @@
mLocalDevices.add(mAudioSystemDeviceSpy);
mLocalDevices.add(mPlaybackDeviceSpy);
- mHdmiPortInfo = new HdmiPortInfo[4];
+ mHdmiPortInfo = new HdmiPortInfo[5];
mHdmiPortInfo[0] =
new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100)
.setCecSupported(true)
@@ -166,6 +175,13 @@
.setArcSupported(false)
.setEarcSupported(false)
.build();
+ mHdmiPortInfo[4] =
+ new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .setEarcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlServiceSpy.initService();
mWakeLockSpy = spy(new FakePowerManagerWrapper.FakeWakeLockWrapper());
@@ -1396,6 +1412,207 @@
}
@Test
+ public void triggerMultipleAddressAllocations_uniqueLocalDevicePerDeviceType() {
+ long allocationDelay = TimeUnit.SECONDS.toMillis(60);
+ mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
+ mTestLooper.dispatchAll();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ // Wake up process that will trigger the address allocation to start.
+ mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // Hotplug In will trigger the address allocation to start.
+ mHdmiControlServiceSpy.onHotplug(4, true);
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_HOTPLUG));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The first allocation finished. The second allocation is still in progress.
+ HdmiCecLocalDevicePlayback firstAllocatedPlayback = mHdmiControlServiceSpy.playback();
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The second allocation finished.
+ HdmiCecLocalDevicePlayback secondAllocatedPlayback = mHdmiControlServiceSpy.playback();
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_HOTPLUG));
+ // Local devices have the same identity.
+ assertTrue(firstAllocatedPlayback == secondAllocatedPlayback);
+ }
+
+ @Test
+ public void triggerMultipleAddressAllocations_keepLastAllocatedAddress() {
+ // First logical address for playback is free.
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.NACK);
+ mTestLooper.dispatchAll();
+
+ long allocationDelay = TimeUnit.SECONDS.toMillis(60);
+ mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
+ mTestLooper.dispatchAll();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ // Wake up process that will trigger the address allocation to start.
+ mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+
+ // First logical address for playback is busy.
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SCREEN_ON));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The first allocation finished. The second allocation is still in progress.
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The second allocation finished. Second logical address for playback is used.
+ HdmiCecLocalDevicePlayback allocatedPlayback = mHdmiControlServiceSpy.playback();
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SCREEN_ON));
+ assertThat(allocatedPlayback.getDeviceInfo().getLogicalAddress())
+ .isEqualTo(ADDR_PLAYBACK_2);
+ }
+
+ @Test
+ public void triggerMultipleAddressAllocations_toggleSoundbarMode_addThenRemoveAudioSystem() {
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ long allocationDelay = TimeUnit.SECONDS.toMillis(60);
+ mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
+ mTestLooper.dispatchAll();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ // Enabling Dynamic soundbar mode will trigger address allocation.
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
+ HdmiControlManager.SOUNDBAR_MODE_ENABLED);
+ mTestLooper.dispatchAll();
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // Disabling Dynamic soundbar mode will trigger another address allocation.
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
+ HdmiControlManager.SOUNDBAR_MODE_DISABLED);
+ mTestLooper.dispatchAll();
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The first allocation finished. The second allocation is still in progress.
+ // The audio system is present in the network.
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The second allocation finished. The audio system is not present in the network.
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNull();
+ }
+
+ @Test
+ public void triggerMultipleAddressAllocations_toggleSoundbarMode_removeThenAddAudioSystem() {
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ // Enable the setting and check if the audio system local device is found in the network.
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
+ HdmiControlManager.SOUNDBAR_MODE_ENABLED);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull();
+
+ long allocationDelay = TimeUnit.SECONDS.toMillis(60);
+ mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
+ mTestLooper.dispatchAll();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ // Disabling Dynamic soundbar mode will trigger address allocation.
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
+ HdmiControlManager.SOUNDBAR_MODE_DISABLED);
+ mTestLooper.dispatchAll();
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // Enabling Dynamic soundbar mode will trigger another address allocation.
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
+ HdmiControlManager.SOUNDBAR_MODE_ENABLED);
+ mTestLooper.dispatchAll();
+ verify(mHdmiControlServiceSpy, times(1))
+ .allocateLogicalAddress(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The first allocation finished. The second allocation is still in progress.
+ // The audio system is not present in the network.
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNull();
+ Mockito.clearInvocations(mHdmiControlServiceSpy);
+
+ mTestLooper.moveTimeForward(allocationDelay / 2);
+ mTestLooper.dispatchAll();
+ // The second allocation finished. The audio system is present in the network.
+ verify(mHdmiControlServiceSpy, times(1))
+ .notifyAddressAllocated(any(), eq(INITIATED_BY_SOUNDBAR_MODE));
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNotNull();
+ }
+
+ @Test
+ public void failedAddressAllocation_noLocalDevice() {
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.clearCecLocalDevices();
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlServiceSpy.playback()).isNull();
+ assertThat(mHdmiControlServiceSpy.audioSystem()).isNull();
+ }
+
+ @Test
public void earcIdle_blocksArcConnection() {
mHdmiControlServiceSpy.clearEarcLocalDevice();
HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy);
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
index be13753..213e05e 100644
--- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -18,7 +18,6 @@
import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
import static android.app.job.JobInfo.NETWORK_TYPE_NONE;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -28,13 +27,14 @@
import android.app.job.JobInfo;
import android.content.ComponentName;
-import android.platform.test.annotations.LargeTest;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
+import androidx.test.filters.LargeTest;
+
import com.android.server.job.controllers.JobStatus;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index b0fd606..d85768d 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -79,6 +79,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for the {@link MediaProjectionManagerService} class.
*
@@ -202,7 +205,8 @@
}
@Test
- public void testCreateProjection_priorProjectionGrant() throws NameNotFoundException {
+ public void testCreateProjection_priorProjectionGrant() throws
+ NameNotFoundException, InterruptedException {
// Create a first projection.
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
FakeIMediaProjectionCallback callback1 = new FakeIMediaProjectionCallback();
@@ -214,9 +218,13 @@
FakeIMediaProjectionCallback callback2 = new FakeIMediaProjectionCallback();
secondProjection.start(callback2);
- // Check that the second projection's callback hasn't been stopped.
- assertThat(callback1.mStopped).isTrue();
- assertThat(callback2.mStopped).isFalse();
+ // Check that the first projection get stopped, but not the second projection.
+ final int timeout = 5;
+ boolean stoppedCallback1 = callback1.mLatch.await(timeout, TimeUnit.SECONDS);
+ boolean stoppedCallback2 = callback2.mLatch.await(timeout, TimeUnit.SECONDS);
+
+ assertThat(stoppedCallback1).isTrue();
+ assertThat(stoppedCallback2).isFalse();
}
@Test
@@ -803,11 +811,10 @@
}
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
- boolean mStopped = false;
-
+ CountDownLatch mLatch = new CountDownLatch(1);
@Override
public void onStop() throws RemoteException {
- mStopped = true;
+ mLatch.countDown();
}
@Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
new file mode 100644
index 0000000..81867df
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -0,0 +1,1973 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://`www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.GROUP_ALERT_ALL;
+import static android.app.Notification.GROUP_ALERT_CHILDREN;
+import static android.app.Notification.GROUP_ALERT_SUMMARY;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
+import com.android.internal.config.sysui.TestableFlagResolver;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.InstanceIdSequenceFake;
+import com.android.internal.util.IntPair;
+import com.android.server.UiServiceTestCase;
+import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
+import com.android.server.pm.PackageManagerService;
+import java.util.Objects;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SuppressLint("GuardedBy")
+public class NotificationAttentionHelperTest extends UiServiceTestCase {
+
+ @Mock AudioManager mAudioManager;
+ @Mock Vibrator mVibrator;
+ @Mock android.media.IRingtonePlayer mRingtonePlayer;
+ @Mock LogicalLight mLight;
+ @Mock
+ NotificationManagerService.WorkerHandler mHandler;
+ @Mock
+ NotificationUsageStats mUsageStats;
+ @Mock
+ IAccessibilityManager mAccessibilityService;
+ @Mock
+ KeyguardManager mKeyguardManager;
+ NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
+ private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
+ 1 << 30);
+
+ private NotificationManagerService mService;
+ private String mPkg = "com.android.server.notification";
+ private int mId = 1001;
+ private int mOtherId = 1002;
+ private String mTag = null;
+ private int mUid = 1000;
+ private int mPid = 2000;
+ private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ private NotificationChannel mChannel;
+
+ private NotificationAttentionHelper mAttentionHelper;
+ private TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
+ private AccessibilityManager mAccessibilityManager;
+ private static final NotificationAttentionHelper.Signals DEFAULT_SIGNALS =
+ new NotificationAttentionHelper.Signals(false, 0);
+
+ private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
+ private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
+
+ private static final long[] CUSTOM_VIBRATION = new long[] {
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 };
+ private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ .build();
+ private static final int CUSTOM_LIGHT_COLOR = Color.BLACK;
+ private static final int CUSTOM_LIGHT_ON = 10000;
+ private static final int CUSTOM_LIGHT_OFF = 10000;
+ private static final int MAX_VIBRATION_DELAY = 1000;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ getContext().addMockSystemService(Vibrator.class, mVibrator);
+
+ when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
+ when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
+ when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+
+ long serviceReturnValue = IntPair.of(
+ AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
+ AccessibilityEvent.TYPES_ALL_MASK);
+ when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue);
+ mAccessibilityManager =
+ new AccessibilityManager(getContext(), Handler.getMain(), mAccessibilityService, 0,
+ true);
+ verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
+ assertTrue(mAccessibilityManager.isEnabled());
+
+ // TODO (b/291907312): remove feature flag
+ mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true);
+
+ mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
+ mNotificationInstanceIdSequence));
+
+ initAttentionHelper(mTestFlagResolver);
+
+ mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+ }
+
+ private void initAttentionHelper(TestableFlagResolver flagResolver) {
+ mAttentionHelper = new NotificationAttentionHelper(getContext(), mock(LightsManager.class),
+ mAccessibilityManager, getContext().getPackageManager(), mUsageStats,
+ mService.mNotificationManagerPrivate, mock(ZenModeHelper.class), flagResolver);
+ mAttentionHelper.setVibratorHelper(new VibratorHelper(getContext()));
+ mAttentionHelper.setAudioManager(mAudioManager);
+ mAttentionHelper.setSystemReady(true);
+ mAttentionHelper.setLights(mLight);
+ mAttentionHelper.setScreenOn(false);
+ mAttentionHelper.setAccessibilityManager(mAccessibilityManager);
+ mAttentionHelper.setKeyguardManager(mKeyguardManager);
+ mAttentionHelper.setScreenOn(false);
+ mAttentionHelper.setInCallStateOffHook(false);
+ mAttentionHelper.mNotificationPulseEnabled = true;
+ }
+
+ //
+ // Convenience functions for creating notification records
+ //
+
+ private NotificationRecord getNoisyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBeepyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBeepyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBeepyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getQuietNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getQuietOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getQuietOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getInsistentBeepyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getInsistentBeepyOnceNotification() {
+ return getNotificationRecord(mId, true /* insistent */, true /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getInsistentBeepyLeanbackNotification() {
+ return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBuzzyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBuzzyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBuzzyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getInsistentBuzzyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getBuzzyBeepyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
+ private NotificationRecord getLightsNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/, true /* lights */);
+ }
+
+ private NotificationRecord getLightsOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, false /* buzzy*/, true /* lights */);
+ }
+
+ private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) {
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH);
+ Notification n = builder.build();
+ if (looping) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ mService.addNotification(r);
+
+ return r;
+ }
+
+ private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+ lights, null, Notification.GROUP_ALERT_ALL, false);
+ }
+
+ private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
+ boolean once,
+ boolean noisy, boolean buzzy, boolean lights) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
+ true,
+ null, Notification.GROUP_ALERT_ALL, true);
+ }
+
+ private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) {
+ return getNotificationRecord(mId, false, false, true, false, false, true, true, true,
+ groupKey, groupAlertBehavior, false);
+ }
+
+ private NotificationRecord getLightsNotificationRecord(String groupKey,
+ int groupAlertBehavior) {
+ return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
+ true, true, groupKey, groupAlertBehavior, false);
+ }
+
+ private NotificationRecord getNotificationRecord(int id,
+ boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+ boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+ boolean isLeanback) {
+
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setOnlyAlertOnce(once);
+
+ int defaults = 0;
+ if (noisy) {
+ if (defaultSound) {
+ defaults |= Notification.DEFAULT_SOUND;
+ mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+ Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ } else {
+ builder.setSound(CUSTOM_SOUND);
+ mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
+ }
+ } else {
+ mChannel.setSound(null, null);
+ }
+ if (buzzy) {
+ if (defaultVibration) {
+ defaults |= Notification.DEFAULT_VIBRATE;
+ } else {
+ builder.setVibrate(CUSTOM_VIBRATION);
+ mChannel.setVibrationPattern(CUSTOM_VIBRATION);
+ }
+ mChannel.enableVibration(true);
+ } else {
+ mChannel.setVibrationPattern(null);
+ mChannel.enableVibration(false);
+ }
+
+ if (lights) {
+ if (defaultLights) {
+ defaults |= Notification.DEFAULT_LIGHTS;
+ } else {
+ builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
+ }
+ mChannel.enableLights(true);
+ } else {
+ mChannel.enableLights(false);
+ }
+ builder.setDefaults(defaults);
+
+ builder.setGroup(groupKey);
+ builder.setGroupAlertBehavior(groupAlertBehavior);
+
+ Notification n = builder.build();
+ if (insistent) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+
+ Context context = spy(getContext());
+ PackageManager packageManager = spy(context.getPackageManager());
+ when(context.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isLeanback);
+
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
+ mService.addNotification(r);
+ return r;
+ }
+
+ //
+ // Convenience functions for interacting with mocks
+ //
+
+ private void verifyNeverBeep() throws RemoteException {
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
+ }
+
+ private void verifyBeepUnlooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
+ }
+
+ private void verifyBeepLooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ }
+
+ private void verifyBeep(int times) throws RemoteException {
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
+ }
+
+ private void verifyNeverStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, never()).stopAsync();
+ }
+
+ private void verifyStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).stopAsync();
+ }
+
+ private void verifyNeverVibrate() {
+ verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(),
+ any(VibrationAttributes.class));
+ }
+
+ private void verifyVibrate() {
+ verifyVibrate(/* times= */ 1);
+ }
+
+ private void verifyVibrate(int times) {
+ verifyVibrate(mVibrateOnceMatcher, times(times));
+ }
+
+ private void verifyVibrateLooped() {
+ verifyVibrate(mVibrateLoopMatcher, times(1));
+ }
+
+ private void verifyDelayedVibrateLooped() {
+ verifyVibrate(mVibrateLoopMatcher, timeout(MAX_VIBRATION_DELAY).times(1));
+ }
+
+ private void verifyDelayedVibrate(VibrationEffect effect) {
+ verifyVibrate(argument -> Objects.equals(effect, argument),
+ timeout(MAX_VIBRATION_DELAY).times(1));
+ }
+
+ private void verifyDelayedNeverVibrate() {
+ verify(mVibrator, after(MAX_VIBRATION_DELAY).never()).vibrate(anyInt(), anyString(), any(),
+ anyString(), any(VibrationAttributes.class));
+ }
+
+ private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
+ VerificationMode verification) {
+ ArgumentCaptor<VibrationAttributes> captor =
+ ArgumentCaptor.forClass(VibrationAttributes.class);
+ verify(mVibrator, verification).vibrate(eq(Process.SYSTEM_UID),
+ eq(PackageManagerService.PLATFORM_PACKAGE_NAME), argThat(effectMatcher),
+ anyString(), captor.capture());
+ assertEquals(0, (captor.getValue().getFlags()
+ & VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+ }
+
+ private void verifyStopVibrate() {
+ int alarmClassUsageFilter =
+ VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK;
+ verify(mVibrator, times(1)).cancel(eq(alarmClassUsageFilter));
+ }
+
+ private void verifyNeverStopVibrate() {
+ verify(mVibrator, never()).cancel();
+ verify(mVibrator, never()).cancel(anyInt());
+ }
+
+ private void verifyNeverLights() {
+ verify(mLight, never()).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ private void verifyLights() {
+ verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ //
+ // Tests
+ //
+
+ @Test
+ public void testLights() throws Exception {
+ NotificationRecord r = getLightsNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyLights();
+ assertTrue(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ verifyNeverVibrate();
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLockedPrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mAttentionHelper.setAccessibilityManager(accessibilityManager);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedOverridePrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mAttentionHelper.setAccessibilityManager(accessibilityManager);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedPublicA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mAttentionHelper.setAccessibilityManager(accessibilityManager);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
+ public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mAttentionHelper.setAccessibilityManager(accessibilityManager);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
+ public void testBeepInsistently() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyBeepLooped();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoLeanbackBeep() throws Exception {
+ NotificationRecord r = getInsistentBeepyLeanbackNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoBeepForAutomotiveIfEffectsDisabled() throws Exception {
+ mAttentionHelper.setIsAutomotive(true);
+ mAttentionHelper.setNotificationEffectsEnabledForAutomotive(false);
+
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testNoBeepForImportanceDefaultInAutomotiveIfEffectsEnabled() throws Exception {
+ mAttentionHelper.setIsAutomotive(true);
+ mAttentionHelper.setNotificationEffectsEnabledForAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testBeepForImportanceHighInAutomotiveIfEffectsEnabled() throws Exception {
+ mAttentionHelper.setIsAutomotive(true);
+ mAttentionHelper.setNotificationEffectsEnabledForAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ assertTrue(r.isInterruptive());
+ }
+
+ @Test
+ public void testNoInterruptionForMin() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoInterruptionForIntercepted() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setIntercepted(true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepTwice() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepUnlooped();
+ verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testHonorAlertOnlyOnceForBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should not beep
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testNoisyUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverStopAudio();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+
+ verifyNeverStopAudio();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ /**
+ * Tests the case where the user re-posts a {@link Notification} with looping sound where
+ * {@link Notification.Builder#setOnlyAlertOnce(true)} has been called. This should silence
+ * the sound associated with the notification.
+ * @throws Exception
+ */
+ @Test
+ public void testNoisyOnceUpdateDoesCancelAudio() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+ NotificationRecord s = getInsistentBeepyOnceNotification();
+ s.isUpdate = true;
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+
+ verifyStopAudio();
+ }
+
+ @Test
+ public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ mAttentionHelper.buzzBeepBlinkLocked(other, DEFAULT_SIGNALS); // this takes the audio stream
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since we no longer own it
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS); // this no longer owns the stream
+ verifyNeverStopAudio();
+ assertTrue(other.isInterruptive());
+ assertNotEquals(-1, other.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietInterloperDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since it does not own it
+ mAttentionHelper.buzzBeepBlinkLocked(other, DEFAULT_SIGNALS);
+ verifyNeverStopAudio();
+ }
+
+ @Test
+ public void testQuietUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ Mockito.reset(mRingtonePlayer);
+
+ // quiet update should stop making noise
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyStopAudio();
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietOnceUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ Mockito.reset(mRingtonePlayer);
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyStopAudio();
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testInCallNotification() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper = spy(mAttentionHelper);
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ mAttentionHelper.setInCallStateOffHook(true);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(mAttentionHelper, times(1)).playInCallNotification();
+ verifyNeverBeep(); // doesn't play normal beep
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoDemoteSoundToVibrateIfVibrateGiven() throws Exception {
+ NotificationRecord r = getBuzzyBeepyNotification();
+ assertTrue(r.getSound() != null);
+
+ // the phone is quiet
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyDelayedVibrate(r.getVibration());
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoDemoteSoundToVibrateIfNonNotificationStream() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ assertTrue(r.getSound() != null);
+ assertNull(r.getVibration());
+
+ // the phone is quiet
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(1);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverVibrate();
+ verifyBeepUnlooped();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testDemoteSoundToVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ assertTrue(r.getSound() != null);
+ assertNull(r.getVibration());
+
+ // the phone is quiet
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyDelayedVibrate(
+ mAttentionHelper.getVibratorHelper().createFallbackVibration(
+ /* insistent= */ false));
+ verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(),
+ anyObject());
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testDemoteInsistentSoundToVibrate() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+ assertTrue(r.getSound() != null);
+ assertNull(r.getVibration());
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyDelayedVibrateLooped();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testInsistentVibrate() {
+ NotificationRecord r = getInsistentBuzzyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyVibrateLooped();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testVibrateTwice() {
+ NotificationRecord r = getBuzzyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mVibrator);
+
+ // update should vibrate
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testPostSilently() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ r.setPostSilently(true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummarySilenceChild() throws Exception {
+ NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
+
+ mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(child.isInterruptive());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummaryNoSilenceSummary() throws Exception {
+ NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ // summaries are never interruptive for notification counts
+ assertFalse(summary.isInterruptive());
+ assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummaryNoSilenceNonGroupChild() throws Exception {
+ NotificationRecord nonGroup = getBeepyNotificationRecord(null, GROUP_ALERT_SUMMARY);
+
+ mAttentionHelper.buzzBeepBlinkLocked(nonGroup, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ assertTrue(nonGroup.isInterruptive());
+ assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildSilenceSummary() throws Exception {
+ NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ assertFalse(summary.isInterruptive());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildNoSilenceChild() throws Exception {
+ NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
+
+ mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ assertTrue(child.isInterruptive());
+ assertNotEquals(-1, child.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildNoSilenceNonGroupSummary() throws Exception {
+ NotificationRecord nonGroup = getBeepyNotificationRecord(null, GROUP_ALERT_CHILDREN);
+
+ mAttentionHelper.buzzBeepBlinkLocked(nonGroup, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ assertTrue(nonGroup.isInterruptive());
+ assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertAllNoSilenceGroup() throws Exception {
+ NotificationRecord group = getBeepyNotificationRecord("a", GROUP_ALERT_ALL);
+
+ mAttentionHelper.buzzBeepBlinkLocked(group, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ assertTrue(group.isInterruptive());
+ assertNotEquals(-1, group.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testHonorAlertOnlyOnceForBuzz() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mVibrator);
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+
+ // update should not beep
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyNeverVibrate();
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoisyUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+
+ verifyNeverStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ // this takes the vibrate stream
+ mAttentionHelper.buzzBeepBlinkLocked(other, DEFAULT_SIGNALS);
+ Mockito.reset(mVibrator);
+
+ // should not stop vibrate, since we no longer own it
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS); // this no longer owns the stream
+ verifyNeverStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertTrue(other.isInterruptive());
+ assertNotEquals(-1, other.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietInterloperDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mVibrator);
+
+ // should not stop noise, since it does not own it
+ mAttentionHelper.buzzBeepBlinkLocked(other, DEFAULT_SIGNALS);
+ verifyNeverStopVibrate();
+ assertFalse(other.isInterruptive());
+ assertEquals(-1, other.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietUpdateCancelsVibrate() {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyVibrate();
+
+ // quiet update should stop making noise
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietOnceUpdateCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyVibrate();
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testQuietUpdateCancelsDemotedVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyDelayedVibrate(mAttentionHelper.getVibratorHelper().createFallbackVibration(false));
+
+ // quiet update should stop making noise
+ mAttentionHelper.buzzBeepBlinkLocked(s, DEFAULT_SIGNALS);
+ verifyStopVibrate();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ assertFalse(s.isInterruptive());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testEmptyUriSoundTreatedAsNoSound() throws Exception {
+ NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+ channel.setSound(Uri.EMPTY, null);
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ mService.addNotification(r);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testRepeatedSoundOverLimitMuted() throws Exception {
+ when(mUsageStats.isAlertRateLimited(any())).thenReturn(true);
+
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testPostingSilentNotificationDoesNotAffectRateLimiting() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(mUsageStats, never()).isAlertRateLimited(any());
+ }
+
+ @Test
+ public void testPostingGroupSuppressedDoesNotAffectRateLimiting() throws Exception {
+ NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+ verify(mUsageStats, never()).isAlertRateLimited(any());
+ }
+
+ @Test
+ public void testGroupSuppressionFailureDoesNotAffectRateLimiting() {
+ NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+ verify(mUsageStats, times(1)).isAlertRateLimited(any());
+ }
+
+ @Test
+ public void testCrossUserSoundMuted() throws Exception {
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+
+ int userId = mUser.getIdentifier() + 1;
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, UserHandle.of(userId), null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn,
+ new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testA11yMinInitialPost() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ r.setSystemImportance(IMPORTANCE_MIN);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testA11yQuietInitialPost() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testA11yQuietUpdate() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testA11yCrossUserEventNotSent() throws Exception {
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+ int userId = mUser.getIdentifier() + 1;
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, UserHandle.of(userId), null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn,
+ new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testLightsScreenOn() {
+ mAttentionHelper.setScreenOn(true);
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertTrue(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsInCall() {
+ mAttentionHelper.setInCallStateOffHook(true);
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsSilentUpdate() {
+ NotificationRecord r = getLightsOnceNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyLights();
+ assertTrue(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+
+ r = getLightsOnceNotification();
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ // checks that lights happened once, i.e. this new call didn't trigger them again
+ verifyLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsUnimportant() {
+ NotificationRecord r = getLightsNotification();
+ r.setSystemImportance(IMPORTANCE_LOW);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsNoLights() {
+ NotificationRecord r = getQuietNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsNoLightOnDevice() {
+ mAttentionHelper.mHasLight = false;
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsLightsOffGlobally() {
+ mAttentionHelper.mNotificationPulseEnabled = false;
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsDndIntercepted() {
+ NotificationRecord r = getLightsNotification();
+ r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummaryNoLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+
+ mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
+
+ verifyNeverLights();
+ assertFalse(child.isInterruptive());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+
+ verifyLights();
+ // summaries should never count for interruptiveness counts
+ assertFalse(summary.isInterruptive());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsNonGroupChild() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_SUMMARY);
+
+ mAttentionHelper.buzzBeepBlinkLocked(nonGroup, DEFAULT_SIGNALS);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildNoLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
+
+ verifyNeverLights();
+ assertFalse(summary.isInterruptive());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+
+ mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
+
+ verifyLights();
+ assertTrue(child.isInterruptive());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsNonGroupSummary() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_CHILDREN);
+
+ mAttentionHelper.buzzBeepBlinkLocked(nonGroup, DEFAULT_SIGNALS);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testGroupAlertAllLightsGroup() {
+ NotificationRecord group = getLightsNotificationRecord("a", GROUP_ALERT_ALL);
+
+ mAttentionHelper.buzzBeepBlinkLocked(group, DEFAULT_SIGNALS);
+
+ verifyLights();
+ assertTrue(group.isInterruptive());
+ assertEquals(-1, group.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsCheckCurrentUser() {
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+ int userId = mUser.getIdentifier() + 10;
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, UserHandle.of(userId), null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn,
+ new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testListenerHintCall() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS));
+
+ verifyNeverBeep();
+ }
+
+ @Test
+ public void testListenerHintCall_notificationSound() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS));
+
+ verifyBeepUnlooped();
+ }
+
+ @Test
+ public void testListenerHintNotification() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS));
+
+ verifyNeverBeep();
+ }
+
+ @Test
+ public void testListenerHintBoth() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
+ NotificationRecord s = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+ | NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS));
+ mAttentionHelper.buzzBeepBlinkLocked(s, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+ | NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS));
+
+ verifyNeverBeep();
+ }
+
+ @Test
+ public void testListenerHintNotification_callSound() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, new NotificationAttentionHelper.Signals(false,
+ NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS));
+
+ verifyBeepLooped();
+ }
+
+ @Test
+ public void testCannotInterruptRingtoneInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ assertTrue(mAttentionHelper.shouldMuteNotificationLocked(interrupter, DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyBeep(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_canUpdate() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
+ DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+ verifyDelayedVibrateLooped();
+ Mockito.reset(mVibrator);
+ Mockito.reset(mRingtonePlayer);
+
+ assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
+ DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+
+ // beep wasn't reset
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ verifyNeverStopAudio();
+ verifyNeverStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_clearEffectsStopsSoundAndVibration() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
+ DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+ verifyDelayedVibrateLooped();
+
+ mAttentionHelper.clearSoundLocked();
+ mAttentionHelper.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_neverVibratesWhenEffectsClearedBeforeDelay()
+ throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
+ DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+ verifyNeverVibrate();
+
+ mAttentionHelper.clearSoundLocked();
+ mAttentionHelper.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyDelayedNeverVibrate();
+ }
+
+ @Test
+ public void testCannotInterruptRingtoneInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.EMPTY,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
+ DEFAULT_SIGNALS));
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyVibrateLooped();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ assertTrue(mAttentionHelper.shouldMuteNotificationLocked(interrupter, DEFAULT_SIGNALS));
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyVibrate(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepUnlooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyVibrate();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+
+ mAttentionHelper.clearSoundLocked();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyVibrateLooped();
+
+ mAttentionHelper.clearVibrateLocked();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBeep() throws Exception {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBuzz() {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ fakeRingtoneChannel.enableVibration(true);
+ fakeRingtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testBubbleSuppressedNotificationDoesntMakeSound() {
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+ mock(PendingIntent.class), mock(Icon.class))
+ .build();
+
+ NotificationRecord record = getBuzzyNotification();
+ metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+ record.getNotification().setBubbleMetadata(metadata);
+ record.setAllowBubble(true);
+ record.getNotification().flags |= FLAG_BUBBLE;
+ record.isUpdate = true;
+ record.setInterruptive(false);
+
+ mAttentionHelper.buzzBeepBlinkLocked(record, DEFAULT_SIGNALS);
+ verifyNeverVibrate();
+ }
+
+ @Test
+ public void testOverflowBubbleSuppressedNotificationDoesntMakeSound() {
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+ mock(PendingIntent.class), mock(Icon.class))
+ .build();
+
+ NotificationRecord record = getBuzzyNotification();
+ metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+ record.getNotification().setBubbleMetadata(metadata);
+ record.setFlagBubbleRemoved(true);
+ record.setAllowBubble(true);
+ record.isUpdate = true;
+ record.setInterruptive(false);
+
+ mAttentionHelper.buzzBeepBlinkLocked(record, DEFAULT_SIGNALS);
+ verifyNeverVibrate();
+ }
+
+ @Test
+ public void testBubbleUpdateMakesSound() {
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+ mock(PendingIntent.class), mock(Icon.class))
+ .build();
+
+ NotificationRecord record = getBuzzyNotification();
+ record.getNotification().setBubbleMetadata(metadata);
+ record.setAllowBubble(true);
+ record.getNotification().flags |= FLAG_BUBBLE;
+ record.isUpdate = true;
+ record.setInterruptive(true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(record, DEFAULT_SIGNALS);
+ verifyVibrate(1);
+ }
+
+ @Test
+ public void testNewBubbleSuppressedNotifMakesSound() {
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
+ mock(PendingIntent.class), mock(Icon.class))
+ .build();
+
+ NotificationRecord record = getBuzzyNotification();
+ metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+ record.getNotification().setBubbleMetadata(metadata);
+ record.setAllowBubble(true);
+ record.getNotification().flags |= FLAG_BUBBLE;
+ record.isUpdate = false;
+ record.setInterruptive(true);
+
+ mAttentionHelper.buzzBeepBlinkLocked(record, DEFAULT_SIGNALS);
+ verifyVibrate(1);
+ }
+
+ @Test
+ public void testStartFlashNotificationEvent_receiveBeepyNotification() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ verifyNeverVibrate();
+ verify(mAccessibilityService).startFlashNotificationEvent(any(), anyInt(),
+ eq(r.getSbn().getPackageName()));
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testStartFlashNotificationEvent_receiveBuzzyNotification() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ verify(mAccessibilityService).startFlashNotificationEvent(any(), anyInt(),
+ eq(r.getSbn().getPackageName()));
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testStartFlashNotificationEvent_receiveBuzzyBeepyNotification() throws Exception {
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyBeepUnlooped();
+ verifyDelayedVibrate(r.getVibration());
+ verify(mAccessibilityService).startFlashNotificationEvent(any(), anyInt(),
+ eq(r.getSbn().getPackageName()));
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testStartFlashNotificationEvent_receiveBuzzyBeepyNotification_ringerModeSilent()
+ throws Exception {
+ NotificationRecord r = getBuzzyBeepyNotification();
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ verify(mAccessibilityService).startFlashNotificationEvent(any(), anyInt(),
+ eq(r.getSbn().getPackageName()));
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
+ private final int mRepeatIndex;
+
+ VibrateRepeatMatcher(int repeatIndex) {
+ mRepeatIndex = repeatIndex;
+ }
+
+ @Override
+ public boolean matches(VibrationEffect actual) {
+ if (actual instanceof VibrationEffect.Composed
+ && ((VibrationEffect.Composed) actual).getRepeatIndex() == mRepeatIndex) {
+ return true;
+ }
+ // All non-waveform effects are essentially one shots.
+ return mRepeatIndex == -1;
+ }
+
+ @Override
+ public String toString() {
+ return "repeatIndex=" + mRepeatIndex;
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4576e9b..9543a2d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -84,6 +84,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -272,6 +273,7 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -279,6 +281,7 @@
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -372,6 +375,8 @@
private DevicePolicyManagerInternal mDevicePolicyManager;
@Mock
private PowerManager mPowerManager;
+ @Mock
+ private LightsManager mLightsManager;
private final ArrayList<WakeLock> mAcquiredWakeLocks = new ArrayList<>();
private final TestPostNotificationTrackerFactory mPostNotificationTrackerFactory =
new TestPostNotificationTrackerFactory();
@@ -503,9 +508,6 @@
setDpmAppOppsExemptFromDismissal(false);
- mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
- mNotificationInstanceIdSequence);
-
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
@@ -527,8 +529,7 @@
Object[] args = invocation.getArguments();
return (int) args[1] == mUid;
});
- final LightsManager mockLightsManager = mock(LightsManager.class);
- when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
+ when(mLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
@@ -601,6 +602,15 @@
return wl;
});
+ // TODO (b/291907312): remove feature flag
+ mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, false);
+ initNMS();
+ }
+
+ private void initNMS() throws Exception {
+ mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
+ mNotificationInstanceIdSequence);
+
// apps allowed as convos
mService.setStringArrayResourceValue(PKG_O);
@@ -617,7 +627,7 @@
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
- mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
+ mLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
mAppUsageStats, mDevicePolicyManager, mUgm, mUgmInternal,
mAppOpsManager, mUm, mHistoryManager, mStatsManager,
@@ -628,11 +638,17 @@
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+ Mockito.reset(mHistoryManager);
verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
verify(mHistoryManager).onBootPhaseAppsCanStart();
- mService.setAudioManager(mAudioManager);
+ // TODO b/291907312: remove feature flag
+ if (mTestFlagResolver.isEnabled(ENABLE_ATTENTION_HELPER_REFACTOR)) {
+ mService.mAttentionHelper.setAudioManager(mAudioManager);
+ } else {
+ mService.setAudioManager(mAudioManager);
+ }
mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext);
mService.setStrongAuthTracker(mStrongAuthTracker);
@@ -1653,6 +1669,23 @@
}
@Test
+ public void testEnqueueNotificationWithTag_WritesExpectedLogs_NAHRefactor() throws Exception {
+ // TODO b/291907312: remove feature flag
+ mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ // Cleanup NMS before re-initializing
+ if (mService != null) {
+ try {
+ mService.onDestroy();
+ } catch (IllegalStateException | IllegalArgumentException e) {
+ // can throw if a broadcast receiver was never registered
+ }
+ }
+ initNMS();
+
+ testEnqueueNotificationWithTag_WritesExpectedLogs();
+ }
+
+ @Test
public void testEnqueueNotificationWithTag_LogsOnMajorUpdates() throws Exception {
final String tag = "testEnqueueNotificationWithTag_LogsOnMajorUpdates";
Notification original = new Notification.Builder(mContext,
@@ -9015,6 +9048,24 @@
}
@Test
+ public void testOnBubbleMetadataChangedToSuppressNotification_soundStopped_NAHRefactor()
+ throws Exception {
+ // TODO b/291907312: remove feature flag
+ mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ // Cleanup NMS before re-initializing
+ if (mService != null) {
+ try {
+ mService.onDestroy();
+ } catch (IllegalStateException | IllegalArgumentException e) {
+ // can throw if a broadcast receiver was never registered
+ }
+ }
+ initNMS();
+
+ testOnBubbleMetadataChangedToSuppressNotification_soundStopped();
+ }
+
+ @Test
public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
int userId = UserManager.isHeadlessSystemUserMode()
? UserHandle.getUserId(UID_HEADLESS)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index b522cab..5147a08 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -28,6 +28,7 @@
import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_OTHER;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -48,6 +49,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Duration;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -230,4 +233,12 @@
NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason(
REASON_CANCEL, DISMISSAL_OTHER));
}
+
+ @Test
+ public void testGetAgeInMinutes() {
+ long postTimeMs = Duration.ofMinutes(5).toMillis();
+ long whenMs = Duration.ofMinutes(2).toMillis();
+ int age = NotificationRecordLogger.getAgeInMinutes(postTimeMs, whenMs);
+ assertThat(age).isEqualTo(3);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 318f932..3034942 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -248,8 +248,7 @@
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -267,8 +266,7 @@
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -282,8 +280,7 @@
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
- FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
@@ -310,8 +307,7 @@
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
- true, Context.DEVICE_ID_DEFAULT, 10);
+ USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10);
}
@Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index d6a3877..42e3383 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -47,8 +47,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
- <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
-
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -106,11 +104,6 @@
android:showWhenLocked="true"
android:turnScreenOn="true" />
- <activity android:name="android.app.Activity"
- android:exported="true"
- android:showWhenLocked="true"
- android:turnScreenOn="true" />
-
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
android:exported="true">
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 8db09f9..61c4d06 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,7 +46,6 @@
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
-import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -99,10 +98,6 @@
* settings values.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 4791946..0a7bb00 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -297,6 +297,7 @@
mPhoneWindowManager.overrideUserSetupComplete();
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideTogglePanel();
+ mPhoneWindowManager.overrideInjectKeyEvent();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index e301da7..6e2c4bd 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,13 +70,14 @@
import android.hardware.input.InputManager;
import android.media.AudioManagerInternal;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.Vibrator;
+import android.os.VibratorInfo;
+import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
@@ -138,6 +139,7 @@
@Mock private TelecomManager mTelecomManager;
@Mock private NotificationManager mNotificationManager;
@Mock private Vibrator mVibrator;
+ @Mock private VibratorInfo mVibratorInfo;
@Mock private PowerManager mPowerManager;
@Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl;
@Mock private InputMethodManagerInternal mInputMethodManagerInternal;
@@ -158,8 +160,8 @@
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
- private HandlerThread mHandlerThread;
private Handler mHandler;
+ private TestLooper mTestLooper;
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
@@ -182,12 +184,11 @@
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
- mHandlerThread = new HandlerThread("fake window manager");
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
- waitForIdle();
+ mHandler.post(() -> setUp(supportSettingsUpdate));
+ mTestLooper.dispatchAll();
}
private void setUp(boolean supportSettingsUpdate) {
@@ -246,6 +247,7 @@
doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService();
doNothing().when(mNotificationManager).silenceNotificationSound();
doReturn(mNotificationManager).when(mPhoneWindowManager).getNotificationService();
+ doReturn(mVibratorInfo).when(mVibrator).getInfo();
doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE));
final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
@@ -298,7 +300,6 @@
}
void tearDown() {
- mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
@@ -325,7 +326,7 @@
}
void waitForIdle() {
- mHandler.runWithScissors(() -> { }, 0 /* timeout */);
+ mTestLooper.dispatchAll();
}
/**
@@ -447,7 +448,7 @@
}
void overrideSearchManager(SearchManager searchManager) {
- mPhoneWindowManager.mSearchManager = searchManager;
+ doReturn(searchManager).when(mContext).getSystemService(eq(SearchManager.class));
}
void assumeResolveActivityNotNull() {
@@ -464,6 +465,10 @@
doReturn(device).when(mInputManager).getInputDevice(anyInt());
}
+ void overrideInjectKeyEvent() {
+ doReturn(true).when(mInputManager).injectInputEvent(any(KeyEvent.class), anyInt());
+ }
+
void overrideSearchKeyBehavior(int behavior) {
mPhoneWindowManager.mSearchKeyBehavior = behavior;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ae58700..0494dfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -489,7 +489,7 @@
ensureActivityConfiguration(activity);
verify(mAtm.getLifecycleManager(), never())
- .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class));
+ .scheduleTransaction(any(), isA(ActivityConfigurationChangeItem.class));
}
@Test
@@ -519,7 +519,7 @@
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
verify(mAtm.getLifecycleManager()).scheduleTransaction(
- eq(activity.app.getThread()), eq(activity.token), eq(expected));
+ eq(activity.app.getThread()), eq(expected));
}
@Test
@@ -599,7 +599,7 @@
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
- eq(activity.token), eq(expected));
+ eq(expected));
verify(displayRotation).onSetRequestedOrientation();
}
@@ -817,7 +817,7 @@
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
verify(mAtm.getLifecycleManager()).scheduleTransaction(
- eq(activity.app.getThread()), eq(activity.token), eq(expected));
+ eq(activity.app.getThread()), eq(expected));
} finally {
stack.getDisplayArea().removeChild(stack);
}
@@ -1787,9 +1787,10 @@
final ActivityRecord activity = createActivityWithTask();
final WindowProcessController wpc = activity.app;
setup.accept(activity);
+ clearInvocations(mAtm.getLifecycleManager());
activity.getTask().removeImmediately("test");
try {
- verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.token),
+ verify(mAtm.getLifecycleManager()).scheduleTransaction(any(),
isA(DestroyActivityItem.class));
} catch (RemoteException ignored) {
}
@@ -2871,14 +2872,14 @@
.setTask(sourceRecord.getTask()).build();
secondRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, sourceRecord);
- assertFalse(secondRecord.mSplashScreenStyleSolidColor);
+ assertTrue(secondRecord.mAllowIconSplashScreen);
secondRecord.onStartingWindowDrawn();
final ActivityRecord finalRecord = new ActivityBuilder(mAtm)
.setTask(sourceRecord.getTask()).build();
finalRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, secondRecord);
- assertTrue(finalRecord.mSplashScreenStyleSolidColor);
+ assertFalse(finalRecord.mAllowIconSplashScreen);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 0989db4..568471d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
@@ -26,7 +25,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,7 +34,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,13 +41,11 @@
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
@@ -76,7 +71,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
/**
@@ -509,88 +503,4 @@
verify(callback, times(1)).onActivityLaunched(any(), any(), any());
}
-
- @Test
- public void testSandboxServiceInterceptionHappensToIntentWithSandboxActivityAction() {
- ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
- mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
- PackageManager packageManagerMock = mock(PackageManager.class);
- String sandboxPackageNameMock = "com.sandbox.mock";
- when(mContext.getPackageManager()).thenReturn(packageManagerMock);
- when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
- Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- verify(spyCallback, times(1)).onInterceptActivityLaunch(
- any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
- }
-
- @Test
- public void testSandboxServiceInterceptionHappensToIntentWithSandboxPackage() {
- ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
- mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
- PackageManager packageManagerMock = mock(PackageManager.class);
- String sandboxPackageNameMock = "com.sandbox.mock";
- when(mContext.getPackageManager()).thenReturn(packageManagerMock);
- when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
- Intent intent = new Intent().setPackage(sandboxPackageNameMock);
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- verify(spyCallback, times(1)).onInterceptActivityLaunch(
- any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
- }
-
- @Test
- public void testSandboxServiceInterceptionHappensToIntentWithComponentNameWithSandboxPackage() {
- ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
- mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
- PackageManager packageManagerMock = mock(PackageManager.class);
- String sandboxPackageNameMock = "com.sandbox.mock";
- when(mContext.getPackageManager()).thenReturn(packageManagerMock);
- when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
- Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, ""));
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- verify(spyCallback, times(1)).onInterceptActivityLaunch(
- any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
- }
-
- @Test
- public void testSandboxServiceInterceptionNotCalledWhenIntentNotRelatedToSandbox() {
- ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
- mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
- PackageManager packageManagerMock = mock(PackageManager.class);
- String sandboxPackageNameMock = "com.sandbox.mock";
- when(mContext.getPackageManager()).thenReturn(packageManagerMock);
- when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
- // Intent: null
- mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null);
-
- // Action: null, Package: null, ComponentName: null
- Intent intent = new Intent();
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- // Wrong Action
- intent = new Intent().setAction(Intent.ACTION_VIEW);
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- // Wrong Package
- intent = new Intent().setPackage("Random");
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- // Wrong ComponentName's package
- intent = new Intent().setComponent(new ComponentName("Random", ""));
- mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
- verify(spyCallback, never()).onInterceptActivityLaunch(
- any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 28dd458..a18dbaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -23,7 +23,6 @@
import android.app.IApplicationThread;
import android.app.servertransaction.ClientTransaction;
-import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -40,8 +39,7 @@
@Test
public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
- ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class),
- new Binder()));
+ ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class)));
ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
clientLifecycleManager.scheduleTransaction(item);
@@ -51,8 +49,7 @@
@Test
public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
- ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class),
- new Binder()));
+ ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class)));
ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
clientLifecycleManager.scheduleTransaction(item);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index ecd84e1..d2eb1cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -31,7 +31,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ContentRecorder.KEY_RECORD_TASK_FEATURE;
import static com.google.common.truth.Truth.assertThat;
@@ -51,25 +50,21 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
import android.view.ContentRecordingSession;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.SurfaceControl;
-import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.server.wm.ContentRecorder.MediaProjectionManagerWrapper;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.concurrent.CountDownLatch;
/**
* Tests for the {@link ContentRecorder} class.
@@ -93,9 +88,6 @@
private ContentRecorder mContentRecorder;
@Mock private MediaProjectionManagerWrapper mMediaProjectionManagerWrapper;
private SurfaceControl mRecordedSurface;
- // Handle feature flag.
- private ConfigListener mConfigListener;
- private CountDownLatch mLatch;
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -133,23 +125,11 @@
mWaitingDisplaySession.setVirtualDisplayId(displayId);
mWaitingDisplaySession.setWaitingForConsent(true);
- mConfigListener = new ConfigListener();
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- mContext.getMainExecutor(), mConfigListener);
- mLatch = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE,
- "true", true);
-
// Skip unnecessary operations of relayout.
spyOn(mWm.mWindowPlacerLocked);
doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
}
- @After
- public void teardown() {
- DeviceConfig.removeOnPropertiesChangedListener(mConfigListener);
- }
-
@Test
public void testIsCurrentlyRecording() {
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
@@ -184,24 +164,6 @@
}
@Test
- public void testUpdateRecording_task_featureDisabled() {
- mLatch = new CountDownLatch(1);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE,
- "false", false);
- mContentRecorder.setContentRecordingSession(mTaskSession);
- mContentRecorder.updateRecording();
- assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
- }
-
- @Test
- public void testUpdateRecording_task_featureEnabled() {
- // Feature already enabled; don't need to again.
- mContentRecorder.setContentRecordingSession(mTaskSession);
- mContentRecorder.updateRecording();
- assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
- }
-
- @Test
public void testUpdateRecording_task_nullToken() {
ContentRecordingSession session = mTaskSession;
session.setVirtualDisplayId(mDisplaySession.getVirtualDisplayId());
@@ -304,6 +266,30 @@
anyFloat(), anyFloat());
}
+ /**
+ * Test that resizing the output surface results in resizing the mirrored content to fit.
+ */
+ @Test
+ public void testOnConfigurationChanged_resizeSurface() {
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+
+ // Resize the output surface.
+ final Point newSurfaceSize = new Point(Math.round(sSurfaceSize.x / 2f),
+ Math.round(sSurfaceSize.y * 2));
+ doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
+ anyInt());
+ mContentRecorder.onConfigurationChanged(
+ mVirtualDisplayContent.getConfiguration().orientation);
+
+ // No resize is issued, only the initial transformations when we started recording.
+ verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
+ anyFloat());
+ verify(mTransaction, atLeast(2)).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+
+ }
+
@Test
public void testOnTaskOrientationConfigurationChanged_resizesSurface() {
mContentRecorder.setContentRecordingSession(mTaskSession);
@@ -679,13 +665,4 @@
anyInt());
return mirroredSurface;
}
-
- private class ConfigListener implements DeviceConfig.OnPropertiesChangedListener {
- @Override
- public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- if (mLatch != null && properties.getKeyset().contains(KEY_RECORD_TASK_FEATURE)) {
- mLatch.countDown();
- }
- }
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 1b44c01..2af6745 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -571,8 +571,7 @@
verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
.setIsRefreshAfterRotationRequested(true);
- final ClientTransaction transaction = ClientTransaction.obtain(
- mActivity.app.getThread(), mActivity.token);
+ final ClientTransaction transaction = ClientTransaction.obtain(mActivity.app.getThread());
transaction.addCallback(RefreshCallbackItem.obtain(mActivity.token,
cycleThroughStop ? ON_STOP : ON_PAUSE));
transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(mActivity.token,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index 30a8941..cf620fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -275,5 +275,10 @@
public boolean isEnteringPipAllowed(int uid) {
return true;
}
+
+ @Override
+ public ComponentName getCustomHomeComponent() {
+ return null;
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index be3f01e..80e169d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -17,15 +17,14 @@
package com.android.server.wm;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
-import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -38,8 +37,6 @@
import androidx.test.filters.SmallTest;
-import com.android.window.flags.FakeFeatureFlagsImpl;
-
import org.junit.Before;
import org.junit.Test;
@@ -60,16 +57,12 @@
private LetterboxConfiguration mLetterboxConfiguration;
private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
- private MutableFakeFeatureFlagsImpl mMutableFakeFeatureFlags;
-
-
@Before
public void setUp() throws Exception {
mContext = getInstrumentation().getTargetContext();
- mMutableFakeFeatureFlags = new MutableFakeFeatureFlagsImpl();
mLetterboxConfigurationPersister = mock(LetterboxConfigurationPersister.class);
mLetterboxConfiguration = new LetterboxConfiguration(mContext,
- mLetterboxConfigurationPersister, mMutableFakeFeatureFlags);
+ mLetterboxConfigurationPersister);
}
@Test
@@ -99,22 +92,6 @@
}
@Test
- public void test_whenFlagEnabled_wallpaperIsDefaultBackground() {
- mMutableFakeFeatureFlags.setLetterboxBackgroundWallpaperFlag(true);
- assertEquals(LETTERBOX_BACKGROUND_WALLPAPER,
- mLetterboxConfiguration.getLetterboxBackgroundType());
- assertEquals(1, mMutableFakeFeatureFlags.getInvocationCount());
- }
-
- @Test
- public void test_whenFlagDisabled_solidColorIsDefaultBackground() {
- mMutableFakeFeatureFlags.setLetterboxBackgroundWallpaperFlag(false);
- assertEquals(LETTERBOX_BACKGROUND_SOLID_COLOR,
- mLetterboxConfiguration.getLetterboxBackgroundType());
- assertEquals(1, mMutableFakeFeatureFlags.getInvocationCount());
- }
-
- @Test
public void test_whenMovedHorizontally_updatePositionAccordingly() {
// Starting from center
assertForHorizontalMove(
@@ -311,23 +288,4 @@
false /* forTabletopMode */,
LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
}
-
- private static class MutableFakeFeatureFlagsImpl extends FakeFeatureFlagsImpl {
- private boolean mLetterboxBackgroundWallpaperFlag;
- private int mInvocationCount;
-
- public void setLetterboxBackgroundWallpaperFlag(boolean letterboxBackgroundWallpaperFlag) {
- mLetterboxBackgroundWallpaperFlag = letterboxBackgroundWallpaperFlag;
- }
-
- @Override
- public boolean letterboxBackgroundWallpaperFlag() {
- mInvocationCount++;
- return mLetterboxBackgroundWallpaperFlag;
- }
-
- int getInvocationCount() {
- return mInvocationCount;
- }
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 2ad9fa0..5f92fd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -32,6 +32,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -811,6 +812,31 @@
/* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
}
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_LOCKED), SCREEN_ORIENTATION_PORTRAIT);
+
+ // unchanged if orientation is specified
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_LANDSCAPE), SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_returnsUnchanged() {
+ spyOn(mController);
+ doReturn(false).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+
// shouldApplyUser...Override
@Test
public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
@@ -862,6 +888,39 @@
}
@Test
+ public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertTrue(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientaion_returnsFalse()
+ throws Exception {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
throws Exception {
prepareActivityThatShouldApplyUserMinAspectRatioOverride();
@@ -898,13 +957,26 @@
assertTrue(mController.shouldApplyUserMinAspectRatioOverride());
}
- private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientationreturnsFalse() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+
+ assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
+ }
+
+ private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
+ boolean orientationRequest) {
spyOn(mController);
- doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+ doReturn(orientationRequest).when(
+ mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
mDisplayContent.setIgnoreOrientationRequest(true);
doReturn(USER_MIN_ASPECT_RATIO_3_2).when(mController).getUserMinAspectRatioOverrideCode();
}
+ private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
+ }
+
private void prepareActivityThatShouldApplyUserFullscreenOverride() {
spyOn(mController);
doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index de3a526..491d5b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -148,8 +149,9 @@
anyInt() /* startFlags */, any() /* profilerInfo */);
// Assume its process is alive because the caller should be the recents service.
- mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */,
- aInfo.applicationInfo.uid);
+ final WindowProcessController proc = mSystemServicesTestRule.addProcess(aInfo.packageName,
+ aInfo.processName, 12345 /* pid */, aInfo.applicationInfo.uid);
+ proc.setCurrentProcState(PROCESS_STATE_HOME);
Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
// Null animation indicates to preload.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1dd71e0..fb27d63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -571,26 +571,28 @@
final Task task = rootTask.getBottomMostTask();
final ActivityRecord root = task.getTopNonFinishingActivity();
spyOn(mWm.mLetterboxConfiguration);
-
- // When device config flag is disabled the button is not enabled
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isTranslucentLetterboxingEnabled();
- assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
-
- // The flag is enabled
- doReturn(true).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
spyOn(root);
- doReturn(task).when(root).getOrganizedTask();
- // When the flag is enabled and the top activity is not in size compat mode.
+ spyOn(root.mLetterboxUiController);
+
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
doReturn(false).when(root).inSizeCompatMode();
+ doReturn(task).when(root).getOrganizedTask();
+
+ // The button should be eligible to be displayed
assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
+ doReturn(false).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+
// When in size compat mode the button is not enabled
doReturn(true).when(root).inSizeCompatMode();
assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(false).when(root).inSizeCompatMode();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
deleted file mode 100644
index e8a847c..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.BuildUtils;
-import android.server.wm.CtsWindowInfoUtils;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@Presubmit
-public class TrustedOverlayTests {
- private static final String TAG = "TrustedOverlayTests";
- private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
- @Rule
- public TestName mName = new TestName();
-
- private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(
- Activity.class);
-
- private Instrumentation mInstrumentation;
- private Activity mActivity;
-
- @Before
- public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mActivity = mActivityRule.launchActivity(null);
- }
-
- @Test
- public void setTrustedOverlayInputWindow() throws InterruptedException {
- assumeFalse(USE_SURFACE_TRUSTED_OVERLAY);
- testTrustedOverlayChildHelper(false);
- }
-
- @Test
- public void setTrustedOverlayChildLayer() throws InterruptedException {
- assumeTrue(USE_SURFACE_TRUSTED_OVERLAY);
- testTrustedOverlayChildHelper(true);
- }
-
- private void testTrustedOverlayChildHelper(boolean expectTrusted) throws InterruptedException {
- IBinder[] tokens = new IBinder[2];
- CountDownLatch hostTokenReady = new CountDownLatch(1);
- mInstrumentation.runOnMainSync(() -> {
- mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
- View rootView = mActivity.getWindow().getDecorView();
- if (rootView.isAttachedToWindow()) {
- tokens[0] = rootView.getWindowToken();
- hostTokenReady.countDown();
- } else {
- rootView.getViewTreeObserver().addOnWindowAttachListener(
- new ViewTreeObserver.OnWindowAttachListener() {
- @Override
- public void onWindowAttached() {
- tokens[0] = rootView.getWindowToken();
- hostTokenReady.countDown();
- }
-
- @Override
- public void onWindowDetached() {
- }
- });
- }
- });
-
- assertTrue("Failed to wait for host to get added",
- hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
-
- mInstrumentation.runOnMainSync(() -> {
- WindowManager wm = mActivity.getSystemService(WindowManager.class);
-
- View childView = new View(mActivity) {
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- tokens[1] = getWindowToken();
- }
- };
- WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- params.token = tokens[0];
- params.type = TYPE_APPLICATION_PANEL;
- wm.addView(childView, params);
- });
-
- boolean[] foundTrusted = new boolean[2];
-
- CtsWindowInfoUtils.waitForWindowInfos(
- windowInfos -> {
- for (var windowInfo : windowInfos) {
- if (windowInfo.windowToken == tokens[0]
- && windowInfo.isTrustedOverlay) {
- foundTrusted[0] = true;
- } else if (windowInfo.windowToken == tokens[1]
- && windowInfo.isTrustedOverlay) {
- foundTrusted[1] = true;
- }
- }
- return foundTrusted[0] && foundTrusted[1];
- }, TIMEOUT_S, TimeUnit.SECONDS);
-
- if (!foundTrusted[0] || !foundTrusted[1]) {
- CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
- }
-
- assertEquals("Failed to find parent window or was not marked trusted", expectTrusted,
- foundTrusted[0]);
- assertEquals("Failed to find child window or was not marked trusted", expectTrusted,
- foundTrusted[1]);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
index f173d66..c5dd447 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
@@ -18,16 +18,16 @@
import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.platform.test.annotations.Presubmit;
-import android.util.Log;
+import android.server.wm.CtsWindowInfoUtils;
import android.view.SurfaceControl;
import android.view.SurfaceControl.TrustedPresentationThresholds;
+import androidx.annotation.GuardedBy;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.server.wm.utils.CommonUtils;
@@ -36,9 +36,8 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -53,6 +52,15 @@
private static final float FRACTION_VISIBLE = 0.1f;
+ private final Object mResultsLock = new Object();
+ @GuardedBy("mResultsLock")
+ private boolean mResult;
+ @GuardedBy("mResultsLock")
+ private boolean mReceivedResults;
+
+ @Rule
+ public TestName mName = new TestName();
+
@Rule
public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule(
TestActivity.class);
@@ -71,36 +79,32 @@
@Test
public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException {
- boolean[] results = new boolean[1];
- CountDownLatch receivedResults = new CountDownLatch(1);
TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds,
Runnable::run, inTrustedPresentationState -> {
- Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState);
- results[0] = inTrustedPresentationState;
- receivedResults.countDown();
+ synchronized (mResultsLock) {
+ mResult = inTrustedPresentationState;
+ mReceivedResults = true;
+ mResultsLock.notify();
+ }
});
t.apply();
-
- assertTrue("Timed out waiting for results",
- receivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertTrue(results[0]);
+ synchronized (mResultsLock) {
+ assertResults();
+ }
}
@Test
public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException {
- final Object resultsLock = new Object();
- boolean[] results = new boolean[1];
- boolean[] receivedResults = new boolean[1];
TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> {
- synchronized (resultsLock) {
- results[0] = inTrustedPresentationState;
- receivedResults[0] = true;
- resultsLock.notify();
+ synchronized (mResultsLock) {
+ mResult = inTrustedPresentationState;
+ mReceivedResults = true;
+ mResultsLock.notify();
}
};
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -108,34 +112,43 @@
Runnable::run, trustedPresentationCallback);
t.apply();
- synchronized (resultsLock) {
- if (!receivedResults[0]) {
- resultsLock.wait(WAIT_TIME_MS);
+ synchronized (mResultsLock) {
+ if (!mReceivedResults) {
+ mResultsLock.wait(WAIT_TIME_MS);
}
- // Make sure we received the results and not just timed out
- assertTrue("Timed out waiting for results", receivedResults[0]);
- assertTrue(results[0]);
-
+ assertResults();
// reset the state
- receivedResults[0] = false;
+ mReceivedResults = false;
}
mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t,
trustedPresentationCallback);
t.apply();
- synchronized (resultsLock) {
- if (!receivedResults[0]) {
- resultsLock.wait(WAIT_TIME_MS);
+ synchronized (mResultsLock) {
+ if (!mReceivedResults) {
+ mResultsLock.wait(WAIT_TIME_MS);
}
// Ensure we waited the full time and never received a notify on the result from the
// callback.
- assertFalse("Should never have received a callback", receivedResults[0]);
+ assertFalse("Should never have received a callback", mReceivedResults);
// results shouldn't have changed.
- assertTrue(results[0]);
+ assertTrue(mResult);
}
}
+ @GuardedBy("mResultsLock")
+ private void assertResults() throws InterruptedException {
+ mResultsLock.wait(WAIT_TIME_MS);
+
+ if (!mReceivedResults) {
+ CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName());
+ }
+ // Make sure we received the results and not just timed out
+ assertTrue("Timed out waiting for results", mReceivedResults);
+ assertTrue(mResult);
+ }
+
public static class TestActivity extends Activity {
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index d85d9b5..2d5b72b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -111,7 +111,7 @@
return null;
}
final WindowContextInfoChangeItem infoChangeItem = (WindowContextInfoChangeItem) item;
- infoChangeItem.execute(mHandler, null, null);
+ infoChangeItem.execute(mHandler, null /* pendingActions */);
return null;
}).when(mWpc).scheduleClientTransactionItem(any());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index ebe40b0..b89182d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -330,7 +330,7 @@
ArgumentCaptor.forClass(ConfigurationChangeItem.class);
verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
- captor.getValue().preExecute(client, null /* token */);
+ captor.getValue().preExecute(client);
final ArgumentCaptor<Configuration> configCaptor =
ArgumentCaptor.forClass(Configuration.class);
verify(client).updatePendingConfiguration(configCaptor.capture());
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 7db32a9..f93c703 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -85,6 +85,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -452,7 +453,9 @@
// Read pending reported events from disk and merge them with those stored in memory
final LinkedList<Event> pendingEvents = new LinkedList<>();
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "loadPendingEvents");
loadPendingEventsLocked(userId, pendingEvents);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
final LinkedList<Event> eventsInMem = mReportedEvents.get(userId);
if (eventsInMem != null) {
pendingEvents.addAll(eventsInMem);
@@ -967,6 +970,12 @@
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
return;
}
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ final String traceTag = "usageStatsQueueEvent(" + userId + ") #"
+ + UserUsageStatsService.eventToString(event.mEventType);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, traceTag);
+ }
synchronized (mLock) {
LinkedList<Event> events = mReportedEvents.get(userId);
if (events == null) {
@@ -980,6 +989,7 @@
mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
@@ -1944,17 +1954,23 @@
case MSG_FLUSH_TO_DISK:
flushToDisk();
break;
- case MSG_UNLOCKED_USER:
+ case MSG_UNLOCKED_USER: {
+ final int userId = msg.arg1;
try {
- onUserUnlocked(msg.arg1);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ "usageStatsHandleUserUnlocked(" + userId + ")");
+ onUserUnlocked(userId);
} catch (Exception e) {
- if (mUserManager.isUserUnlocked(msg.arg1)) {
+ if (mUserManager.isUserUnlocked(userId)) {
throw e; // rethrow exception - user is unlocked
} else {
Slog.w(TAG, "Attempted to unlock stopped or removed user " + msg.arg1);
}
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
break;
+ }
case MSG_REMOVE_USER:
onUserRemoved(msg.arg1);
break;
@@ -1986,7 +2002,10 @@
break;
case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: {
final int userId = msg.arg1;
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ "usageStatsHandleEstimatedLaunchTimesOnUser(" + userId + ")");
handleEstimatedLaunchTimesOnUserUnlock(userId);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
break;
case MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED: {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 7d2e1a4..b5d8096 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1306,7 +1306,7 @@
}
}
- private static String eventToString(int eventType) {
+ static String eventToString(int eventType) {
switch (eventType) {
case Event.NONE:
return "NONE";
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 3d78a1d..0a70a5f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -27,9 +27,9 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH;
-import android.app.AppOpsManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.content.ComponentName;
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SharedMemory;
+import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetectionServiceFailure;
@@ -114,6 +115,9 @@
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
+ private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+ SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
+
/**
* Indicates the {@link HotwordDetectionService} is created.
*/
@@ -680,7 +684,8 @@
mIntent = intent;
mDetectionServiceType = detectionServiceType;
int flags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
- if (mVisualQueryDetectionComponentName != null
+ if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED
+ && mVisualQueryDetectionComponentName != null
&& mHotwordDetectionComponentName != null) {
flags |= Context.BIND_SHARED_ISOLATED_PROCESS;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 471acc1..6ba77da 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -57,6 +57,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SharedMemory;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
@@ -96,6 +97,8 @@
/** The delay time for retrying to request DirectActions. */
private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200;
+ private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+ SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
final boolean mValid;
@@ -715,7 +718,7 @@
} else {
verifyDetectorForVisualQueryDetectionLocked(sharedMemory);
}
- if (!verifyProcessSharingLocked()) {
+ if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED && !verifyProcessSharingLocked()) {
Slog.w(TAG, "Sandboxed detection service not in shared isolated process");
throw new IllegalStateException("VisualQueryDetectionService or HotworDetectionService "
+ "not in a shared isolated process. Please make sure to set "
@@ -914,6 +917,7 @@
if (hotwordInfo == null || visualQueryInfo == null) {
return true;
}
+ // Enforce shared isolated option is used when VisualQueryDetectionservice is enabled
return (hotwordInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0
&& (visualQueryInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0;
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72f780..1f32c97 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -412,6 +412,15 @@
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
/**
+ * Optional extra for incoming containing a long which specifies the time the
+ * call was answered by user. This value is in milliseconds.
+ * @hide
+ */
+ public static final String EXTRA_CALL_ANSWERED_TIME_MILLIS =
+ "android.telecom.extra.CALL_ANSWERED_TIME_MILLIS";
+
+
+ /**
* Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
* time the call was created.
* @hide
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66f3bed..36a8fc1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4607,12 +4607,12 @@
*
* <p>Example:
*
- * <pre><code>
+ * <pre>{@code
* <string-array name="carrier_service_name_array" num="2">
* <item value="Police"/>
* <item value="Ambulance"/>
* </string-array>
- * </code></pre>
+ * }</pre>
*/
public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
@@ -4626,18 +4626,18 @@
*
* <ul>
* <li>The number of items in both the arrays are equal
- * <li>The item added in this key follows a specific format. Either it should be all numbers,
- * or "+" followed by all numbers.
+ * <li>The item should contain dialable characters only which includes 0-9, -, *, #, (, ),
+ * SPACE.
* </ul>
*
* <p>Example:
*
- * <pre><code>
+ * <pre>{@code
* <string-array name="carrier_service_number_array" num="2">
- * <item value="123"/>
- * <item value="+343"/>
+ * <item value="*123"/>
+ * <item value="+ (111) 111-111"/>
* </string-array>
- * </code></pre>
+ * }</pre>
*/
public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY =
"carrier_service_number_array";
@@ -10229,7 +10229,7 @@
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
- sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, true);
+ sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, false);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f1af68f..53f347e 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -71,13 +71,6 @@
*/
public static final int INVALID = Integer.MAX_VALUE;
- private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
-
- private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
-
- /* The type of signal measurement */
- private static final String MEASUREMENT_TYPE_RSCP = "rscp";
-
// Timestamp of SignalStrength since boot
// Effectively final. Timestamp is set during construction of SignalStrength
private long mTimestampMillis;
@@ -92,28 +85,9 @@
CellSignalStrengthNr mNr;
/**
- * Create a new SignalStrength from a intent notifier Bundle
- *
- * This method may be used by external applications.
- *
- * @param m Bundle from intent notifier
- * @return newly created SignalStrength
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static SignalStrength newFromBundle(Bundle m) {
- SignalStrength ret;
- ret = new SignalStrength();
- ret.setFromNotifierBundle(m);
- return ret;
- }
-
- /**
* This constructor is used to create SignalStrength with default
* values.
*
- * @return newly created SignalStrength
* @hide
*/
@UnsupportedAppUsage
@@ -164,20 +138,20 @@
* Returns a List of CellSignalStrength Components of this SignalStrength Report.
*
* Use this API to access underlying
- * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
* granular information about the SignalStrength report. Only valid (non-empty)
* CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
* and the list may contain more than one instance of a CellSignalStrength type.
*
* @return a List of CellSignalStrength or an empty List if there are no valid measurements.
*
- * @see android.telephony#CellSignalStrength
- * @see android.telephony#CellSignalStrengthNr
- * @see android.telephony#CellSignalStrengthLte
- * @see android.telephony#CellSignalStrengthTdscdma
- * @see android.telephony#CellSignalStrengthWcdma
- * @see android.telephony#CellSignalStrengthCdma
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrength
+ * @see android.telephony.CellSignalStrengthNr
+ * @see android.telephony.CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthGsm
*/
@NonNull public List<CellSignalStrength> getCellSignalStrengths() {
return getCellSignalStrengths(CellSignalStrength.class);
@@ -187,7 +161,7 @@
* Returns a List of CellSignalStrength Components of this SignalStrength Report.
*
* Use this API to access underlying
- * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+ * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
* granular information about the SignalStrength report. Only valid (non-empty)
* CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
* and the list may contain more than one instance of a CellSignalStrength type.
@@ -197,13 +171,13 @@
* return values.
* @return a List of CellSignalStrength or an empty List if there are no valid measurements.
*
- * @see android.telephony#CellSignalStrength
- * @see android.telephony#CellSignalStrengthNr
- * @see android.telephony#CellSignalStrengthLte
- * @see android.telephony#CellSignalStrengthTdscdma
- * @see android.telephony#CellSignalStrengthWcdma
- * @see android.telephony#CellSignalStrengthCdma
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrength
+ * @see android.telephony.CellSignalStrengthNr
+ * @see android.telephony.CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthGsm
*/
@NonNull public <T extends CellSignalStrength> List<T> getCellSignalStrengths(
@NonNull Class<T> clazz) {
@@ -319,7 +293,7 @@
*
*/
public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
- new Parcelable.Creator<SignalStrength>() {
+ new Parcelable.Creator<>() {
public SignalStrength createFromParcel(Parcel in) {
return new SignalStrength(in);
}
@@ -327,7 +301,7 @@
public SignalStrength[] newArray(int size) {
return new SignalStrength[size];
}
- };
+ };
/**
* Get the GSM RSSI in ASU.
@@ -338,7 +312,7 @@
*
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getAsuLevel}.
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths
*/
@Deprecated
@@ -352,7 +326,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getBitErrorRate}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -368,7 +342,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getCdmaDbm}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -382,7 +356,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getCdmaEcio}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -398,7 +372,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoDbm}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -412,7 +386,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoEcio}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -426,7 +400,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoSnr}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
@Deprecated
@@ -438,7 +412,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRssi}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -452,7 +426,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRsrp}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -466,7 +440,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRsrq}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -480,7 +454,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getRssnr}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -494,7 +468,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getCqi}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -527,7 +501,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology,
* this method is misleading and should not be used.
- * @see android.telephony#CellSignalStrength
+ * @see android.telephony.CellSignalStrength
* @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
@@ -543,7 +517,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrength#getDbm()}. Because the levels vary by technology,
* this method is misleading and should not be used.
- * @see android.telephony#CellSignalStrength
+ * @see android.telephony.CellSignalStrength
* @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
@@ -559,7 +533,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getDbm}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -575,7 +549,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getLevel}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -591,7 +565,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthGsm#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.CellSignalStrengthGsm
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -607,7 +581,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getLevel}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -625,7 +599,7 @@
* ASU for CDMA, the resultant value is Android-specific and is not recommended
* for use.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -641,7 +615,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthCdma#getEvdoLevel}.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -659,7 +633,7 @@
* ASU for EvDO, the resultant value is Android-specific and is not recommended
* for use.
*
- * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.CellSignalStrengthCdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -675,7 +649,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getDbm}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -691,7 +665,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getLevel}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -708,7 +682,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthLte#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.CellSignalStrengthLte
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -739,7 +713,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -758,7 +732,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getLevel}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -774,7 +748,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthTdscdma#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.CellSignalStrengthTdscdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -790,7 +764,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getRscp}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -805,7 +779,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getAsuLevel}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -828,7 +802,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -843,7 +817,7 @@
* @deprecated this information should be retrieved from
* {@link CellSignalStrengthWcdma#getDbm}.
*
- * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.CellSignalStrengthWcdma
* @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
@@ -895,32 +869,12 @@
}
/**
- * Set SignalStrength based on intent notifier map
- *
- * @param m intent notifier map
- *
- * @deprecated this method relies on non-stable implementation details, and full access to
- * internal storage is available via {@link getCellSignalStrengths()}.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- private void setFromNotifierBundle(Bundle m) {
- mCdma = m.getParcelable("Cdma", android.telephony.CellSignalStrengthCdma.class);
- mGsm = m.getParcelable("Gsm", android.telephony.CellSignalStrengthGsm.class);
- mWcdma = m.getParcelable("Wcdma", android.telephony.CellSignalStrengthWcdma.class);
- mTdscdma = m.getParcelable("Tdscdma", android.telephony.CellSignalStrengthTdscdma.class);
- mLte = m.getParcelable("Lte", android.telephony.CellSignalStrengthLte.class);
- mNr = m.getParcelable("Nr", android.telephony.CellSignalStrengthNr.class);
- }
-
- /**
* Set intent notifier Bundle based on SignalStrength
*
* @param m intent notifier Bundle
*
* @deprecated this method relies on non-stable implementation details, and full access to
- * internal storage is available via {@link getCellSignalStrengths()}.
+ * internal storage is available via {@link #getCellSignalStrengths()}.
* @hide
*/
@Deprecated
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index faf97b5..de2e952 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -18,6 +18,7 @@
import static android.text.TextUtils.formatSimple;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -44,6 +45,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -255,6 +257,11 @@
private final boolean mIsGroupDisabled;
/**
+ * Whether this subscription is used for communicating with non-terrestrial networks.
+ */
+ private final boolean mIsNtn;
+
+ /**
* @hide
*
* @deprecated Use {@link SubscriptionInfo.Builder}.
@@ -378,6 +385,7 @@
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
this.mUsageSetting = usageSetting;
+ this.mIsNtn = false;
}
/**
@@ -416,6 +424,7 @@
this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
this.mPortIndex = builder.mPortIndex;
this.mUsageSetting = builder.mUsageSetting;
+ this.mIsNtn = builder.mIsNtn;
}
/**
@@ -862,6 +871,17 @@
return mUsageSetting;
}
+ /**
+ * Check if the subscription is exclusively for non-terrestrial networks.
+ *
+ * @return {@code true} if it is a non-terrestrial network subscription, {@code false}
+ * otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public boolean isNtn() {
+ return mIsNtn;
+ }
+
@NonNull
public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
new Parcelable.Creator<SubscriptionInfo>() {
@@ -898,6 +918,7 @@
UiccAccessRule.CREATOR))
.setUiccApplicationsEnabled(source.readBoolean())
.setUsageSetting(source.readInt())
+ .setNtn(source.readBoolean())
.build();
}
@@ -939,6 +960,7 @@
dest.writeTypedArray(mCarrierConfigAccessRules, flags);
dest.writeBoolean(mAreUiccApplicationsEnabled);
dest.writeInt(mUsageSetting);
+ dest.writeBoolean(mIsNtn);
}
@Override
@@ -1001,6 +1023,7 @@
+ " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+ + " ntn=" + mIsNtn
+ "]";
}
@@ -1025,7 +1048,8 @@
that.mCardString) && Arrays.equals(mNativeAccessRules,
that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
- && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner);
+ && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
+ && mIsNtn == that.mIsNtn;
}
@Override
@@ -1034,7 +1058,7 @@
mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
- mIsGroupDisabled);
+ mIsGroupDisabled, mIsNtn);
result = 31 * result + Arrays.hashCode(mEhplmns);
result = 31 * result + Arrays.hashCode(mHplmns);
result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1234,6 +1258,11 @@
private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
/**
+ * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise.
+ */
+ private boolean mIsNtn = false;
+
+ /**
* Default constructor.
*/
public Builder() {
@@ -1275,6 +1304,7 @@
mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
mPortIndex = info.mPortIndex;
mUsageSetting = info.mUsageSetting;
+ mIsNtn = info.mIsNtn;
}
/**
@@ -1660,6 +1690,18 @@
}
/**
+ * Set whether the subscription is exclusively used for non-terrestrial networks or not.
+ *
+ * @param isNtn {@code true} if the subscription is for NTN, {@code false} otherwise.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNtn(boolean isNtn) {
+ mIsNtn = isNtn;
+ return this;
+ }
+
+ /**
* Build the {@link SubscriptionInfo}.
*
* @return The {@link SubscriptionInfo} instance.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 6d9c1e6..29149b9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1117,6 +1117,14 @@
public static final String SATELLITE_ATTACH_ENABLED_FOR_CARRIER =
SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
+ /**
+ * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network.
+ * By default, it's disabled.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"USAGE_SETTING_"},
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 6130af5..a4527f12 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -724,6 +724,17 @@
*/
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7;
/**
+ * A transition state indicating that Telephony is waiting for satellite modem to connect to a
+ * satellite network before sending a datagram or polling for datagrams. If the satellite modem
+ * successfully connects to a satellite network, either
+ * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING} or
+ * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING} will be sent. Otherwise,
+ * either {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED} or
+ * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED} will be sent.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8;
+ /**
* The datagram transfer state is unknown. This generic datagram transfer state should be used
* only when the datagram transfer state cannot be mapped to other specific datagram transfer
* states.
@@ -740,6 +751,7 @@
SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 21a6b44..a5a23e8 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -323,49 +323,49 @@
*/
UserHandle getSubscriptionUserHandle(int subId);
- /**
- * Check if subscription and user are associated with each other.
- *
- * @param subscriptionId the subId of the subscription
- * @param userHandle user handle of the user
- * @return {@code true} if subscription is associated with user
- * {code true} if there are no subscriptions on device
- * else {@code false} if subscription is not associated with user.
- *
- * @throws IllegalArgumentException if subscription is invalid.
- * @throws SecurityException if the caller doesn't have permissions required.
- * @throws IllegalStateException if subscription service is not available.
- *
- * @hide
- */
- boolean isSubscriptionAssociatedWithUser(int subscriptionId, in UserHandle userHandle);
+ /**
+ * Check if subscription and user are associated with each other.
+ *
+ * @param subscriptionId the subId of the subscription
+ * @param userHandle user handle of the user
+ * @return {@code true} if subscription is associated with user
+ * {code true} if there are no subscriptions on device
+ * else {@code false} if subscription is not associated with user.
+ *
+ * @throws IllegalArgumentException if subscription is invalid.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ * @throws IllegalStateException if subscription service is not available.
+ *
+ * @hide
+ */
+ boolean isSubscriptionAssociatedWithUser(int subscriptionId, in UserHandle userHandle);
- /**
- * Get list of subscriptions associated with user.
- *
- * @param userHandle user handle of the user
- * @return list of subscriptionInfo associated with the user.
- *
- * @throws SecurityException if the caller doesn't have permissions required.
- * @throws IllegalStateException if subscription service is not available.
- *
- * @hide
- */
- List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
+ /**
+ * Get list of subscriptions associated with user.
+ *
+ * @param userHandle user handle of the user
+ * @return list of subscriptionInfo associated with the user.
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
+ * @throws IllegalStateException if subscription service is not available.
+ *
+ * @hide
+ */
+ List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
- /**
- * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
- * configs to device for all existing SIMs in the subscription database
- * {@link Telephony.SimInfo}. Internally, it will store the backup data in an internal file.
- * This file will persist on device for device's lifetime and will be used later on when a SIM
- * is inserted to restore that specific SIM's settings. End result is subscription database is
- * modified to match any backed up configs for the appropriate inserted SIMs.
- *
- * <p>
- * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any
- * {@link Telephony.SimInfo} entry is updated as the result of this method call.
- *
- * @param data with the sim specific configs to be backed up.
- */
- void restoreAllSimSpecificSettingsFromBackup(in byte[] data);
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in the subscription database
+ * {@link Telephony.SimInfo}. Internally, it will store the backup data in an internal file.
+ * This file will persist on device for device's lifetime and will be used later on when a SIM
+ * is inserted to restore that specific SIM's settings. End result is subscription database is
+ * modified to match any backed up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any
+ * {@link Telephony.SimInfo} entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ */
+ void restoreAllSimSpecificSettingsFromBackup(in byte[] data);
}
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
index bb10658..e74a30c 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
# Lint as: python3
"""
Base class for setting up devices for CDM functionalities.
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
index 5516c0f..bf7e1f3c9 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
# Lint as: python3
"""
Test E2E CDM functions on mobly.
diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp
index 3272cef..6809521 100644
--- a/tests/CtsSurfaceControlTestsStaging/Android.bp
+++ b/tests/CtsSurfaceControlTestsStaging/Android.bp
@@ -36,6 +36,7 @@
"androidx.test.rules",
"compatibility-device-util-axt",
"com.google.android.material_material",
+ "SurfaceFlingerProperties",
"truth-prebuilt",
],
resource_dirs: ["src/main/res"],
diff --git a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
index de0ad59..0b75eb3 100644
--- a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
+++ b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "CtsSurfaceControlTestsStaging"
}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 4b12053..79348d1 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.sysprop.SurfaceFlingerProperties;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
@@ -45,6 +46,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
/**
* An Activity to help with frame rate testing.
@@ -217,6 +219,27 @@
postBuffer();
}
+ Surface getSurface() {
+ return mSurface;
+ }
+
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ public int setFrameRate(float frameRate) {
+ Log.i(TAG,
+ String.format("Setting frame rate for %s: frameRate=%.2f", mName, frameRate));
+
+ int rc = 0;
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setFrameRate(
+ mSurfaceControl, frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ transaction.apply();
+ }
+ return rc;
+ }
+
public int setFrameRateCategory(int category) {
Log.i(TAG,
String.format(
@@ -230,6 +253,19 @@
return rc;
}
+ public int setFrameRateSelectionStrategy(int strategy) {
+ Log.i(TAG,
+ String.format("Setting frame rate selection strategy for %s: strategy=%d",
+ mName, strategy));
+
+ int rc = 0;
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setFrameRateSelectionStrategy(mSurfaceControl, strategy);
+ transaction.apply();
+ }
+ return rc;
+ }
+
public void setVisibility(boolean visible) {
Log.i(TAG,
String.format("Setting visibility for %s: %s", mName,
@@ -342,6 +378,11 @@
uniqueFrameRates.add(frameRate);
}
}
+ Log.i(TAG,
+ "**** Available display refresh rates: "
+ + uniqueFrameRates.stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", ")));
return uniqueFrameRates;
}
@@ -358,6 +399,10 @@
<= FRAME_RATE_TOLERANCE;
}
+ private boolean frameRateEquals(float frameRate1, float frameRate2) {
+ return Math.abs(frameRate1 - frameRate2) <= FRAME_RATE_TOLERANCE;
+ }
+
// Waits until our SurfaceHolder has a surface and the activity is resumed.
private void waitForPreconditions() throws InterruptedException {
assertNotSame(
@@ -447,9 +492,22 @@
verifyCompatibleAndStableFrameRate(0, surfaces);
}
- // Set expectedFrameRate to 0.0 to verify only stable frame rate.
+ private void verifyExactAndStableFrameRate(
+ float expectedFrameRate,
+ TestSurface... surfaces) throws InterruptedException {
+ verifyFrameRate(expectedFrameRate, false, surfaces);
+ }
+
private void verifyCompatibleAndStableFrameRate(
- float expectedFrameRate, TestSurface... surfaces) throws InterruptedException {
+ float expectedFrameRate,
+ TestSurface... surfaces) throws InterruptedException {
+ verifyFrameRate(expectedFrameRate, true, surfaces);
+ }
+
+ // Set expectedFrameRate to 0.0 to verify only stable frame rate.
+ private void verifyFrameRate(
+ float expectedFrameRate, boolean multiplesAllowed,
+ TestSurface... surfaces) throws InterruptedException {
Log.i(TAG, "Verifying compatible and stable frame rate");
long nowNanos = System.nanoTime();
long gracePeriodEndTimeNanos =
@@ -457,9 +515,19 @@
while (true) {
if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
// Wait until we switch to a compatible frame rate.
- while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate)
- && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
- // Empty
+ Log.i(TAG,
+ "Verifying expected frame rate: actual (device)=" + mDeviceFrameRate
+ + " expected=" + expectedFrameRate);
+ if (multiplesAllowed) {
+ while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate)
+ && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+ // Empty
+ }
+ } else {
+ while (!frameRateEquals(mDeviceFrameRate, expectedFrameRate)
+ && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+ // Empty
+ }
}
nowNanos = System.nanoTime();
if (nowNanos >= gracePeriodEndTimeNanos) {
@@ -604,12 +672,65 @@
"frame rate category=" + category);
}
+ private void testSurfaceControlFrameRateSelectionStrategyInternal(int parentStrategy)
+ throws InterruptedException {
+ Log.i(TAG,
+ "**** Running testSurfaceControlFrameRateSelectionStrategy for strategy "
+ + parentStrategy);
+ TestSurface parent = null;
+ TestSurface child = null;
+ try {
+ parent = new TestSurface(mSurfaceView.getSurfaceControl(), mSurface,
+ "testSurfaceParent", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+ child = new TestSurface(parent.getSurfaceControl(), parent.getSurface(),
+ "testSurfaceChild", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.BLUE);
+
+ // Test
+ Display display = getDisplay();
+ List<Float> frameRates = getRefreshRates(display.getMode(), display);
+ assumeTrue("**** SKIPPED due to frame rate override disabled",
+ SurfaceFlingerProperties.enable_frame_rate_override().orElse(true));
+ float childFrameRate = Collections.max(frameRates);
+ float parentFrameRate = childFrameRate / 2;
+ int initialNumEvents = mModeChangedEvents.size();
+ parent.setFrameRate(parentFrameRate);
+ parent.setFrameRateSelectionStrategy(parentStrategy);
+ child.setFrameRate(childFrameRate);
+
+ // Verify
+ float expectedFrameRate =
+ parentStrategy == SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN
+ ? parentFrameRate
+ : childFrameRate;
+ verifyExactAndStableFrameRate(expectedFrameRate, parent, child);
+ verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
+ } finally {
+ if (parent != null) {
+ parent.release();
+ }
+ if (child != null) {
+ child.release();
+ }
+ }
+ }
+
+ public void testSurfaceControlFrameRateSelectionStrategy(int parentStrategy)
+ throws InterruptedException {
+ runTestsWithPreconditions(
+ () -> testSurfaceControlFrameRateSelectionStrategyInternal(parentStrategy),
+ "frame rate strategy=" + parentStrategy);
+ }
+
private float getExpectedFrameRate(int category) {
Display display = getDisplay();
List<Float> frameRates = getRefreshRates(display.getMode(), display);
- if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT
- || category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) {
+ if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) {
+ // Max due to default vote and no other frame rate specifications.
+ return Collections.max(frameRates);
+ } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) {
return Collections.min(frameRates);
}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index ccbda7f..bed9cff 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -23,11 +23,14 @@
import android.os.SystemProperties;
import android.support.test.uiautomator.UiDevice;
import android.view.Surface;
+import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
+import com.android.compatibility.common.util.DisplayUtil;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -59,19 +62,15 @@
uiDevice.executeShellCommand("wm dismiss-keyguard");
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
- Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
- Manifest.permission.MANAGE_GAME_MODE);
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS);
// Prevent DisplayManager from limiting the allowed refresh rate range based on
// non-app policies (e.g. low battery, user settings, etc).
mDisplayManager = activity.getSystemService(DisplayManager.class);
mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
- mInitialRefreshRateSwitchingType =
- toSwitchingType(mDisplayManager.getMatchContentFrameRateUserPreference());
+ mInitialRefreshRateSwitchingType = DisplayUtil.getRefreshRateSwitchingType(mDisplayManager);
mDisplayManager.setRefreshRateSwitchingType(
DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
}
@@ -118,16 +117,18 @@
activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_DEFAULT);
}
- private int toSwitchingType(int matchContentFrameRateUserPreference) {
- switch (matchContentFrameRateUserPreference) {
- case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
- return DisplayManager.SWITCHING_TYPE_NONE;
- case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
- return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
- case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
- return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
- default:
- return -1;
- }
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategySelf() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategyOverrideChildren()
+ throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 87231c8..93a5bf5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -27,7 +27,11 @@
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.utils.*
+import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -63,11 +67,12 @@
.withHomeActivityVisible()
.waitForAndVerify()
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds ?:
- error("Display not found")
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Display not found")
}
transitions {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp,
+ flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
index 030b292..b3c9c96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
@@ -31,7 +31,8 @@
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButton3ButtonLandscape :
CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_90) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
index 770da143..29c11a4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
@@ -31,7 +31,8 @@
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButton3ButtonPortrait :
CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_0) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
index 4b2206d7..1bb4a25 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
@@ -31,7 +31,8 @@
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButtonGesturalNavLandscape :
CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_90) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
index 54b471e..17cb6e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
@@ -31,7 +31,8 @@
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButtonGesturalNavPortrait :
CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
index 93130c4..a1708fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -46,7 +47,9 @@
tapl.setExpectedRotation(rotation.value)
tapl.setIgnoreTaskbarVisibility(true)
testApp1.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp2.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
}
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
index 4941eea..3cae1c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
@@ -30,6 +30,7 @@
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
@Ignore("Base Test Class")
abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotation.ROTATION_0) {
@@ -46,7 +47,9 @@
tapl.setExpectedRotation(rotation.value)
testApp1.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp2.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.launchedAppState.quickSwitchToPreviousApp()
wmHelper
.StateSyncBuilder()
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index f867c98..9198ae1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -102,6 +102,20 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".PortraitImmersiveActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitImmersiveActivity"
+ android:immersive="true"
+ android:resizeableActivity="true"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="PortraitImmersiveActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name=".LaunchTransparentActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 7c5e9a3..8b334c0 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -73,6 +73,12 @@
FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
}
+ public static class PortraitImmersiveActivity {
+ public static final String LABEL = "PortraitImmersiveActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PortraitImmersiveActivity");
+ }
+
public static class TransparentActivity {
public static final String LABEL = "TransparentActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java
new file mode 100644
index 0000000..0a7f81c
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+public class PortraitImmersiveActivity extends GameActivity {
+}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 96b685d..365e00e 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -26,6 +26,7 @@
"androidx.test.runner",
"androidx.test.uiautomator_uiautomator",
"servicestests-utils",
+ "flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
"kotlin-test",
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
new file mode 100644
index 0000000..3a2a3be
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.hardware.input
+
+import android.content.ContextWrapper
+import android.graphics.drawable.Drawable
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.hardware.input.Flags
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for Keyboard layout preview
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardLayoutPreviewTests
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyboardLayoutPreviewTests {
+
+ companion object {
+ const val WIDTH = 100
+ const val HEIGHT = 100
+ }
+
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
+ private fun createDrawable(): Drawable? {
+ val context = ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())
+ val inputManager = context.getSystemService(InputManager::class.java)!!
+ return inputManager.getKeyboardLayoutPreview(null, WIDTH, HEIGHT)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ val drawable = createDrawable()!!
+ assertEquals(WIDTH, drawable.intrinsicWidth)
+ assertEquals(HEIGHT, drawable.intrinsicHeight)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
+ setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ assertNull(createDrawable())
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
new file mode 100644
index 0000000..eee486f
--- /dev/null
+++ b/tests/InputScreenshotTest/Android.bp
@@ -0,0 +1,60 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "InputScreenshotTests",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ static_libs: [
+ "androidx.arch.core_core-testing",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ "androidx.lifecycle_lifecycle-runtime-testing",
+ "androidx.compose.animation_animation",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.runtime_runtime-livedata",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.lifecycle_lifecycle-livedata-ktx",
+ "androidx.lifecycle_lifecycle-runtime-compose",
+ "androidx.navigation_navigation-compose",
+ "truth-prebuilt",
+ "androidx.compose.runtime_runtime",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.uiautomator_uiautomator",
+ "servicestests-utils",
+ "frameworks-base-testutils",
+ "platform-screenshot-diff-core",
+ "hamcrest-library",
+ "kotlin-test",
+ "flag-junit",
+ "platform-test-annotations",
+ "services.core.unboosted",
+ "testables",
+ "testng",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ ],
+ test_suites: ["device-tests"],
+ compile_multilib: "both",
+ use_embedded_native_libs: false,
+ asset_dirs: ["assets"],
+}
diff --git a/tests/InputScreenshotTest/AndroidManifest.xml b/tests/InputScreenshotTest/AndroidManifest.xml
new file mode 100644
index 0000000..9ffbb3a
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.input.screenshot">
+
+ <uses-sdk android:minSdkVersion="21"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Screenshot tests for Input"
+ android:targetPackage="com.android.input.screenshot">
+ </instrumentation>
+</manifest>
diff --git a/tests/InputScreenshotTest/AndroidTest.xml b/tests/InputScreenshotTest/AndroidTest.xml
new file mode 100644
index 0000000..cc25fa4
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+ -->
+<configuration description="Runs Input screendiff tests.">
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-suite-tag" value="apct" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="optimized-property-setting" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="InputScreenshotTests.apk" />
+ </target_preparer>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys"
+ value="/data/user/0/com.android.input.screenshot/files/input_screenshots" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.input.screenshot" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/InputScreenshotTest/OWNERS b/tests/InputScreenshotTest/OWNERS
new file mode 100644
index 0000000..3cffce9
--- /dev/null
+++ b/tests/InputScreenshotTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include /core/java/android/hardware/input/OWNERS
diff --git a/tests/InputScreenshotTest/TEST_MAPPING b/tests/InputScreenshotTest/TEST_MAPPING
new file mode 100644
index 0000000..727e609
--- /dev/null
+++ b/tests/InputScreenshotTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "InputScreenshotTests"
+ }
+ ]
+}
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 0000000..70e4a71
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
new file mode 100644
index 0000000..502c1b4
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
new file mode 100644
index 0000000..591b2fa
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 0000000..0137a85
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 0000000..37a91e1
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
new file mode 100644
index 0000000..84c971c
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.input.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
+// tests.
+fun View.drawIntoBitmap(): Bitmap {
+ val bitmap =
+ Bitmap.createBitmap(
+ measuredWidth,
+ measuredHeight,
+ Bitmap.Config.ARGB_8888,
+ )
+ val canvas = Canvas(bitmap)
+ draw(canvas)
+ return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+ if (Build.CPU_ABI == "x86_64") {
+ // Different CPU architectures can sometimes end up rendering differently, so we can't do
+ // pixel-perfect matching on different architectures using the same golden. Given that our
+ // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+ // x86_64 architecture and use the Structural Similarity Index on others.
+ // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+ // do pixel perfect matching both at presubmit time and at development time with actual
+ // devices.
+ PixelPerfectMatcher()
+ } else {
+ MSSIMMatcher()
+ }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher()
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
new file mode 100644
index 0000000..edddc6b4
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 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.input.screenshot
+
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.DisplaySpec
+
+/**
+ * The emulations specs for all 8 permutations of:
+ * - phone or tablet.
+ * - dark of light mode.
+ * - portrait or landscape.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletFull
+ get() = PhoneAndTabletFullSpec
+
+private val PhoneAndTabletFullSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, Displays.Tablet)
+
+/**
+ * The emulations specs of:
+ * - phone + light mode + portrait.
+ * - phone + light mode + landscape.
+ * - tablet + dark mode + portrait.
+ *
+ * This allows to test the most important permutations of a screen/layout with only 3
+ * configurations.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletMinimal
+ get() = PhoneAndTabletMinimalSpec
+
+private val PhoneAndTabletMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false) +
+ DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = true, isLandscape = false)
+
+/**
+ * This allows to test only single most important configuration.
+ */
+val DeviceEmulationSpec.Companion.PhoneMinimal
+ get() = PhoneMinimalSpec
+
+private val PhoneMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false, isLandscape = false)
+
+object Displays {
+ val Phone =
+ DisplaySpec(
+ "phone",
+ width = 1440,
+ height = 3120,
+ densityDpi = 560,
+ )
+
+ val Tablet =
+ DisplaySpec(
+ "tablet",
+ width = 2560,
+ height = 1600,
+ densityDpi = 320,
+ )
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
new file mode 100644
index 0000000..8faf224
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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.input.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all Input screenshot tests. */
+class InputGoldenImagePathManager(
+ pathConfig: PathConfig,
+ assetsPathRelativeToBuildRoot: String
+) :
+ GoldenImagePathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
+ deviceLocalPath =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/input_screenshots",
+ pathConfig = pathConfig,
+ ) {
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "InputGoldenImagePathManager"
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
new file mode 100644
index 0000000..c2c3d55
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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.input.screenshot
+
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.Image
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.graphics.asImageBitmap
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** A rule for Input screenshot diff tests. */
+class InputScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ assetsPathRelativeToBuildRoot: String
+) : TestRule {
+ private val colorsRule = MaterialYouColorsRule()
+ private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+ private val screenshotRule =
+ ScreenshotTestRule(
+ InputGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetsPathRelativeToBuildRoot
+ )
+ )
+ private val composeRule = createAndroidComposeRule<ComponentActivity>()
+ private val delegateRule =
+ RuleChain.outerRule(colorsRule)
+ .around(deviceEmulationRule)
+ .around(screenshotRule)
+ .around(composeRule)
+ private val matcher = UnitTestBitmapMatcher
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return delegateRule.apply(base, description)
+ }
+
+ /**
+ * Compare [content] with the golden image identified by [goldenIdentifier].
+ */
+ fun screenshotTest(
+ goldenIdentifier: String,
+ content: (Context) -> Bitmap,
+ ) {
+ // Make sure that the activity draws full screen and fits the whole display.
+ val activity = composeRule.activity
+ activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
+
+ // Set the content using the AndroidComposeRule to make sure that the Activity is set up
+ // correctly.
+ composeRule.setContent {
+ Image(
+ bitmap = content(activity).asImageBitmap(),
+ contentDescription = null,
+ )
+ }
+ composeRule.waitForIdle()
+
+ val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
+ screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
+ }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
new file mode 100644
index 0000000..e855786
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Ansi physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewAnsiScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-ansi") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.US),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
new file mode 100644
index 0000000..8ae6dfd
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Iso physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview") {
+ context: Context -> LayoutPreview.createLayoutPreview(context, null)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
new file mode 100644
index 0000000..5231c14
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for JIS physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewJisScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-jis") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.JAPAN),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
new file mode 100644
index 0000000..76ee379
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.hardware.input.InputManager
+import android.hardware.input.KeyboardLayout
+import android.util.TypedValue
+import kotlin.math.roundToInt
+
+object LayoutPreview {
+ fun createLayoutPreview(context: Context, layout: KeyboardLayout?): Bitmap {
+ val im = context.getSystemService(InputManager::class.java)!!
+ val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 600.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 200.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val drawable = im.getKeyboardLayoutPreview(layout, width, height)!!
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+ drawable.draw(canvas)
+ return bitmap
+ }
+}
\ No newline at end of file
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
index 235bd47..fd888ec 100644
--- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
Binary files differ
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
index 413e3c0..66f7bfd 100644
--- a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
@@ -1,35 +1,31 @@
-----BEGIN CERTIFICATE-----
-MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
-MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
-aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
-WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
-AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
-OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
-T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
-JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
-Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
-PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
-aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
-TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
-LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
-BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
-dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
-AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
-NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
-b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
-cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
-MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
-BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
-YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
-ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
-BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
-I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
-CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
-2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
-2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
+MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX
+MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
+CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIwMDYx
+OTAwMDA0MloXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
+GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFIx
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAthECix7joXebO9y/lD63
+ladAPKH9gvl9MgaCcfb2jH/76Nu8ai6Xl6OMS/kr9rH5zoQdsfnFl97vufKj6bwS
+iV6nqlKr+CMny6SxnGPb15l+8Ape62im9MZaRw1NEDPjTrETo8gYbEvs/AmQ351k
+KSUjB6G00j0uYODP0gmHu81I8E3CwnqIiru6z1kZ1q+PsAewnjHxgsHA3y6mbWwZ
+DrXYfiYaRQM9sHmklCitD38m5agI/pboPGiUU+6DOogrFZYJsuB6jC511pzrp1Zk
+j5ZPaK49l8KEj8C8QMALXL32h7M1bKwYUH+E4EzNktMg6TO8UpmvMrUpsyUqtEj5
+cuHKZPfmghCN6J3Cioj6OGaK/GP5Afl4/Xtcd/p2h/rs37EOeZVXtL0m79YB0esW
+CruOC7XFxYpVq9Os6pFLKcwZpDIlTirxZUTQAs6qzkm06p98g7BAe+dDq6dso499
+iYH6TKX/1Y7DzkvgtdizjkXPdsDtQCv9Uw+wp9U7DbGKogPeMa3Md+pvez7W35Ei
+Eua++tgy/BBjFFFy3l3WFpO9KWgz7zpm7AeKJt8T11dleCfeXkkUAKIAf5qoIbap
+sZWwpbkNFhHax2xIPEDgfg1azVY80ZcFuctL7TlLnMQ/0lUTbiSw1nH69MG6zO0b
+9f6BQdgAmD06yK56mDcYBZUCAwEAAaOCATgwggE0MA4GA1UdDwEB/wQEAwIBhjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTkrysmcRorSCeFL1JmLO/wiRNxPjAf
+BgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzBgBggrBgEFBQcBAQRUMFIw
+JQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnBraS5nb29nL2dzcjEwKQYIKwYBBQUH
+MAKGHWh0dHA6Ly9wa2kuZ29vZy9nc3IxL2dzcjEuY3J0MDIGA1UdHwQrMCkwJ6Al
+oCOGIWh0dHA6Ly9jcmwucGtpLmdvb2cvZ3NyMS9nc3IxLmNybDA7BgNVHSAENDAy
+MAgGBmeBDAECATAIBgZngQwBAgIwDQYLKwYBBAHWeQIFAwIwDQYLKwYBBAHWeQIF
+AwMwDQYJKoZIhvcNAQELBQADggEBADSkHrEoo9C0dhemMXoh6dFSPsjbdBZBiLg9
+NR3t5P+T4Vxfq7vqfM/b5A3Ri1fyJm9bvhdGaJQ3b2t6yMAYN/olUazsaL+yyEn9
+WprKASOshIArAoyZl+tJaox118fessmXn1hIVw41oeQa1v1vg4Fv74zPl6/AhSrw
+9U5pCZEt4Wi4wStz6dTZ/CLANx8LZh1J7QJVj2fhMtfTJr9w4z30Z209fOU0iOMy
++qduBmpvvYuR7hZL6Dupszfnw0Skfths18dG9ZKb59UhvmaSGZRVbNQpsg3BZlvi
+d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8=
-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml
index 5d23d36e..99106ad 100644
--- a/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml
+++ b/tests/NetworkSecurityConfigTest/res/xml/domain_whitespace.xml
@@ -5,7 +5,7 @@
</domain>
<domain> developer.android.com </domain>
<pin-set>
- <pin digest="SHA-256"> 7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y= </pin>
+ <pin digest="SHA-256"> zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w= </pin>
</pin-set>
</domain-config>
</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
index d45fd77..232f88f 100644
--- a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
+++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
@@ -9,7 +9,7 @@
<domain-config>
<domain>developer.android.com</domain>
<pin-set>
- <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin>
</pin-set>
</domain-config>
</domain-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
index 1773d280..7cc81b0 100644
--- a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
+++ b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
@@ -3,7 +3,7 @@
<domain-config>
<domain>android.com</domain>
<pin-set>
- <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+ <pin digest="SHA-256">zCTnfLwLKbS9S2sbp+uFz4KZOocFvXxkV06Ce9O5M2w=</pin>
</pin-set>
</domain-config>
</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
index 047be16..0494f17 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -22,23 +22,17 @@
import android.test.ActivityUnitTestCase;
import android.util.ArraySet;
import android.util.Pair;
+
+import com.android.org.conscrypt.TrustedCertificateStore;
+
import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.Socket;
-import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.TrustManager;
-import com.android.org.conscrypt.TrustedCertificateStore;
+import javax.net.ssl.SSLContext;
public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
@@ -46,9 +40,9 @@
super(Activity.class);
}
- // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015).
- private static final byte[] G2_SPKI_SHA256
- = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");
+ // SHA-256 of the GTS intermediate CA (CN = GTS CA 1C3) for android.com (as of 09/2023).
+ private static final byte[] GTS_INTERMEDIATE_SPKI_SHA256 =
+ hexToBytes("cc24e77cbc0b29b4bd4b6b1ba7eb85cf82993a8705bd7c64574e827bd3b9336c");
private static final byte[] TEST_CA_BYTES
= hexToBytes(
@@ -161,7 +155,7 @@
public void testGoodPin() throws Exception {
ArraySet<Pin> pins = new ArraySet<Pin>();
- pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256));
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
@@ -247,7 +241,7 @@
public void testWithUrlConnection() throws Exception {
ArraySet<Pin> pins = new ArraySet<Pin>();
- pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ pins.add(new Pin("SHA-256", GTS_INTERMEDIATE_SPKI_SHA256));
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
@@ -304,7 +298,7 @@
} finally {
// Delete the user added CA. We don't know the alias so just delete them all.
for (String alias : store.aliases()) {
- if (store.isUser(alias)) {
+ if (TrustedCertificateStore.isUser(alias)) {
try {
store.deleteCertificateEntry(alias);
} catch (Exception ignored) {
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
index 9dec21b..39b5cb4c 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
@@ -16,19 +16,20 @@
package android.security.net.config;
+import static org.junit.Assert.fail;
+
import android.content.pm.ApplicationInfo;
import android.os.Build;
-import java.net.Socket;
+
import java.net.URL;
+
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.TrustManager;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
-import junit.framework.Assert;
-
-public final class TestUtils extends Assert {
+public final class TestUtils {
private TestUtils() {
}
@@ -36,8 +37,8 @@
public static void assertConnectionFails(SSLContext context, String host, int port)
throws Exception {
try {
- Socket s = context.getSocketFactory().createSocket(host, port);
- s.getInputStream();
+ SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port);
+ s.startHandshake();
fail("Expected connection to " + host + ":" + port + " to fail.");
} catch (SSLHandshakeException expected) {
}
@@ -45,7 +46,8 @@
public static void assertConnectionSucceeds(SSLContext context, String host, int port)
throws Exception {
- Socket s = context.getSocketFactory().createSocket(host, port);
+ SSLSocket s = (SSLSocket) context.getSocketFactory().createSocket(host, port);
+ s.startHandshake();
s.getInputStream();
}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 4b7a014..81e05c1 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -16,26 +16,18 @@
package android.security.net.config;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
-import android.util.ArraySet;
-import android.util.Pair;
+
import java.io.IOException;
import java.net.InetAddress;
-import java.net.Socket;
-import java.net.URL;
import java.security.KeyStore;
import java.security.Provider;
-import java.security.Security;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Set;
-import javax.net.ssl.HttpsURLConnection;
+
import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -52,7 +44,7 @@
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
assertNotNull(config);
// Check defaults.
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
assertFalse(config.getTrustAnchors().isEmpty());
PinSet pinSet = config.getPins();
@@ -72,7 +64,7 @@
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
assertNotNull(config);
// Check defaults.
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
assertTrue(config.getTrustAnchors().isEmpty());
PinSet pinSet = config.getPins();
@@ -91,14 +83,14 @@
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
assertNotNull(config);
// Check defaults.
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
assertTrue(config.getTrustAnchors().isEmpty());
PinSet pinSet = config.getPins();
assertTrue(pinSet.pins.isEmpty());
// Check android.com.
config = appConfig.getConfigForHostname("android.com");
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
assertFalse(config.getTrustAnchors().isEmpty());
pinSet = config.getPins();
@@ -188,7 +180,7 @@
ApplicationConfig appConfig = new ApplicationConfig(source);
assertTrue(appConfig.hasPerDomainConfigs());
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
assertFalse(config.getTrustAnchors().isEmpty());
PinSet pinSet = config.getPins();
@@ -250,9 +242,9 @@
ApplicationConfig appConfig = new ApplicationConfig(source);
// Check android.com.
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
- assertEquals(2, config.getTrustAnchors().size());
+ assertEquals(1, config.getTrustAnchors().size());
// Try connections.
SSLContext context = TestUtils.getSSLContext(source);
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
@@ -267,9 +259,9 @@
ApplicationConfig appConfig = new ApplicationConfig(source);
// Check android.com.
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
- assertTrue(config.isCleartextTrafficPermitted());
+ assertFalse(config.isCleartextTrafficPermitted());
assertFalse(config.isHstsEnforced());
- assertEquals(2, config.getTrustAnchors().size());
+ assertEquals(1, config.getTrustAnchors().size());
// Try connections.
SSLContext context = TestUtils.getSSLContext(source);
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
diff --git a/tests/utils/testutils/TEST_MAPPING b/tests/utils/testutils/TEST_MAPPING
index d9eb44f..6468d8c 100644
--- a/tests/utils/testutils/TEST_MAPPING
+++ b/tests/utils/testutils/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "frameworks-base-testutils-tests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "exclude-annotation": "androidx.test.filters.LargeTest"
},
{
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS
index 4f655e5..55bab7d 100644
--- a/tools/aapt2/OWNERS
+++ b/tools/aapt2/OWNERS
@@ -1,4 +1,4 @@
set noparent
-toddke@google.com
zyy@google.com
-patb@google.com
\ No newline at end of file
+patb@google.com
+markpun@google.com
diff --git a/tools/lint/fix/Android.bp b/tools/lint/fix/Android.bp
index 43f2122..ddacf57 100644
--- a/tools/lint/fix/Android.bp
+++ b/tools/lint/fix/Android.bp
@@ -25,6 +25,11 @@
name: "lint_fix",
main: "soong_lint_fix.py",
srcs: ["soong_lint_fix.py"],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
}
python_library_host {
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index acc0ad0..4a2e37e 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -64,27 +64,27 @@
class SoongLintFix:
"""
- This class creates a command line tool that will
- apply lint fixes to the platform via the necessary
- combination of soong and shell commands.
+ This class creates a command line tool that will apply lint fixes to the
+ platform via the necessary combination of soong and shell commands.
- It breaks up these operations into a few "private" methods
- that are intentionally exposed so experimental code can tweak behavior.
+ It breaks up these operations into a few "private" methods that are
+ intentionally exposed so experimental code can tweak behavior.
- The entry point, `run`, will apply lint fixes using the
- intermediate `suggested-fixes` directory that soong creates during its
- invocation of lint.
+ The entry point, `run`, will apply lint fixes using the intermediate
+ `suggested-fixes` directory that soong creates during its invocation of
+ lint.
Basic usage:
```
from soong_lint_fix import SoongLintFix
- SoongLintFix().run()
+ opts = SoongLintFixOptions()
+ opts.parse_args(sys.argv)
+ SoongLintFix(opts).run()
```
"""
- def __init__(self):
- self._parser = _setup_parser()
- self._args = None
+ def __init__(self, opts):
+ self._opts = opts
self._kwargs = None
self._modules = []
@@ -96,19 +96,18 @@
self._find_modules()
self._lint()
- if not self._args.no_fix:
+ if not self._opts.no_fix:
self._fix()
- if self._args.print:
+ if self._opts.print:
self._print()
def _setup(self):
- self._args = self._parser.parse_args()
env = os.environ.copy()
- if self._args.check:
- env["ANDROID_LINT_CHECK"] = self._args.check
- if self._args.lint_module:
- env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._args.lint_module
+ if self._opts.check:
+ env["ANDROID_LINT_CHECK"] = self._opts.check
+ if self._opts.lint_module:
+ env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
self._kwargs = {
"env": env,
@@ -131,7 +130,7 @@
with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
module_info = json.load(f)
- for module_name in self._args.modules:
+ for module_name in self._opts.modules:
module = SoongModule(module_name)
module.find(module_info)
self._modules.append(module)
@@ -169,6 +168,20 @@
print(f.read())
+class SoongLintFixOptions:
+ """Options for SoongLintFix"""
+
+ def __init__(self):
+ self.modules = []
+ self.check = None
+ self.lint_module = None
+ self.no_fix = False
+ self.print = False
+
+ def parse_args(self, args=None):
+ _setup_parser().parse_args(args, self)
+
+
def _setup_parser():
parser = argparse.ArgumentParser(description="""
This is a python script that applies lint fixes to the platform:
@@ -199,4 +212,6 @@
return parser
if __name__ == "__main__":
- SoongLintFix().run()
+ opts = SoongLintFixOptions()
+ opts.parse_args()
+ SoongLintFix(opts).run()
diff --git a/tools/lint/utils/Android.bp b/tools/lint/utils/Android.bp
index 75e8d68..439c86d 100644
--- a/tools/lint/utils/Android.bp
+++ b/tools/lint/utils/Android.bp
@@ -43,3 +43,9 @@
"AndroidUtilsLintChecker",
],
}
+
+python_binary_host {
+ name: "enforce_permission_counter",
+ srcs: ["enforce_permission_counter.py"],
+ libs: ["soong_lint_fix"],
+}
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
new file mode 100644
index 0000000..b5c2ffe
--- /dev/null
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -0,0 +1,82 @@
+# 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.
+
+import re
+
+import soong_lint_fix
+
+# Libraries that constitute system_server.
+# It is non-trivial to keep in sync with services/Android.bp as some
+# module are post-processed (e.g, services.core).
+TARGETS = [
+ "services.core.unboosted",
+ "services.accessibility",
+ "services.appprediction",
+ "services.appwidget",
+ "services.autofill",
+ "services.backup",
+ "services.companion",
+ "services.contentcapture",
+ "services.contentsuggestions",
+ "services.coverage",
+ "services.devicepolicy",
+ "services.midi",
+ "services.musicsearch",
+ "services.net",
+ "services.people",
+ "services.print",
+ "services.profcollect",
+ "services.restrictions",
+ "services.searchui",
+ "services.smartspace",
+ "services.systemcaptions",
+ "services.translation",
+ "services.texttospeech",
+ "services.usage",
+ "services.usb",
+ "services.voiceinteraction",
+ "services.wallpapereffectsgeneration",
+ "services.wifi",
+]
+
+
+class EnforcePermissionMigratedCounter:
+ """Wrapper around lint_fix to count the number of AIDL methods annotated."""
+ def run(self):
+ opts = soong_lint_fix.SoongLintFixOptions()
+ opts.check = "AnnotatedAidlCounter"
+ opts.lint_module = "AndroidUtilsLintChecker"
+ opts.no_fix = True
+ opts.modules = TARGETS
+
+ self.linter = soong_lint_fix.SoongLintFix(opts)
+ self.linter.run()
+ self.parse_lint_reports()
+
+ def parse_lint_reports(self):
+ counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
+ for module in self.linter._modules:
+ with open(module.lint_report, "r") as f:
+ content = f.read()
+ keys = dict(re.findall(r'(\w+)=(\d+)', content))
+ for key in keys:
+ counts[key] += int(keys[key])
+ print(counts)
+ total = sum(counts.values())
+ annotated_percent = (1 - (counts["unannotated"] / total)) * 100
+ print("Annotated methods = %.2f%%" % (annotated_percent))
+
+
+if __name__ == "__main__":
+ EnforcePermissionMigratedCounter().run()
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index bc41829..71ac94b 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -334,6 +334,7 @@
if (mServiceConnection != null) {
mContext.unbindService(mServiceConnection);
mServiceConnection = null;
+ mService = null;
}
}