Merge "Dedup MediaSessionService foreground service handling code" into main
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 9a1796f..c7797c7 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -64,3 +64,10 @@
bug: "296829976"
is_fixed_read_only: true
}
+
+flag {
+ name: "allow_resolver_sheet_for_private_space"
+ namespace: "profile_experiences"
+ description: "Add support for Private Space in resolver sheet"
+ bug: "307515485"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 40e03db..60ad8e8 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -86,6 +86,8 @@
private static native long nativeCreate(String opPackageName);
private static native boolean nativeGetSensorAtIndex(long nativeInstance,
Sensor sensor, int index);
+ private static native boolean nativeGetDefaultDeviceSensorAtIndex(long nativeInstance,
+ Sensor sensor, int index);
private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
private static native void nativeGetRuntimeSensors(
long nativeInstance, int deviceId, List<Sensor> list);
@@ -162,11 +164,14 @@
// initialize the sensor list
for (int index = 0;; ++index) {
Sensor sensor = new Sensor();
- if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+ if (android.companion.virtual.flags.Flags.enableNativeVdm()) {
+ if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
+ } else {
+ if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+ }
mFullSensorsList.add(sensor);
mHandleToSensor.put(sensor.getHandle(), sensor);
}
-
}
/** @hide */
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index ffd7212..64a62a9 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -751,6 +751,23 @@
*/
boolean blockScreenOn(Runnable unblocker);
+ /**
+ * Get the brightness levels used to determine automatic brightness based on lux levels.
+ * @param mode The auto-brightness mode
+ * (AutomaticBrightnessController.AutomaticBrightnessMode)
+ * @return The brightness levels for the specified mode. The values are between
+ * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+ */
+ float[] getAutoBrightnessLevels(int mode);
+
+ /**
+ * Get the lux levels used to determine automatic brightness.
+ * @param mode The auto-brightness mode
+ * (AutomaticBrightnessController.AutomaticBrightnessMode)
+ * @return The lux levels for the specified mode
+ */
+ float[] getAutoBrightnessLuxLevels(int mode);
+
/** Returns whether displayoffload supports the given display state. */
static boolean isSupportedOffloadState(int displayState) {
return Display.isSuspendedState(displayState);
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index bd087f9..41dee3a 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -21,10 +21,10 @@
package android.nfc.cardemulation;
import android.annotation.FlaggedApi;
-import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -374,7 +374,7 @@
// Set uid
mUid = si.applicationInfo.uid;
- mCategoryOtherServiceEnabled = false; // support other category
+ mCategoryOtherServiceEnabled = true; // support other category
}
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index 17e0427..0d073cc 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -48,3 +48,10 @@
description: "Enable NFC Polling Loop Notifications ST shim"
bug: "294217286"
}
+
+flag {
+ name: "enable_tag_detection_broadcasts"
+ namespace: "nfc"
+ description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field."
+ bug: "306203494"
+}
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index b56bef3..30524a1 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -50,3 +50,11 @@
description: "Collect sepolicy hash from sysfs"
bug: "308471499"
}
+
+flag {
+ name: "frp_enforcement"
+ namespace: "android_hw_security"
+ description: "This flag controls whether PDB enforces FRP"
+ bug: "290312729"
+ is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7534d29..7dcbbea 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -249,6 +249,7 @@
private UserHandle mCloneProfileUserHandle;
private UserHandle mTabOwnerUserHandleForLaunch;
+ private UserHandle mPrivateProfileUserHandle;
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
@@ -441,6 +442,7 @@
mPersonalProfileUserHandle = fetchPersonalProfileUserHandle();
mWorkProfileUserHandle = fetchWorkProfileUserProfile();
mCloneProfileUserHandle = fetchCloneProfileUserHandle();
+ mPrivateProfileUserHandle = fetchPrivateProfileUserHandle();
mTabOwnerUserHandleForLaunch = fetchTabOwnerUserHandleForLaunch();
// The last argument of createResolverListAdapter is whether to do special handling
@@ -648,7 +650,8 @@
initialIntents,
rList,
filterLastUsed,
- /* userHandle */ getPersonalProfileUserHandle());
+ getPersonalProfileUserHandle());
+
QuietModeManager quietModeManager = createQuietModeManager();
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
@@ -747,6 +750,9 @@
}
protected UserHandle getPersonalProfileUserHandle() {
+ if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()){
+ return mPrivateProfileUserHandle;
+ }
return mPersonalProfileUserHandle;
}
protected @Nullable UserHandle getWorkProfileUserHandle() {
@@ -761,6 +767,10 @@
return mTabOwnerUserHandleForLaunch;
}
+ protected UserHandle getPrivateProfileUserHandle() {
+ return mPrivateProfileUserHandle;
+ }
+
protected UserHandle fetchPersonalProfileUserHandle() {
// ActivityManager.getCurrentUser() refers to the current Foreground user. When clone/work
// profile is active, we always make the personal tab from the foreground user.
@@ -795,12 +805,28 @@
return mCloneProfileUserHandle;
}
+ protected @Nullable UserHandle fetchPrivateProfileUserHandle() {
+ mPrivateProfileUserHandle = null;
+ UserManager userManager = getSystemService(UserManager.class);
+ for (final UserInfo userInfo :
+ userManager.getProfiles(mPersonalProfileUserHandle.getIdentifier())) {
+ if (userInfo.isPrivateProfile()) {
+ mPrivateProfileUserHandle = userInfo.getUserHandle();
+ break;
+ }
+ }
+ return mPrivateProfileUserHandle;
+ }
+
private UserHandle fetchTabOwnerUserHandleForLaunch() {
- // If we are in work profile's process, return WorkProfile user as owner, otherwise we
- // always return PersonalProfile user as owner
- return UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())
- ? getWorkProfileUserHandle()
- : getPersonalProfileUserHandle();
+ // If we are in work or private profile's process, return WorkProfile/PrivateProfile user
+ // as owner, otherwise we always return PersonalProfile user as owner
+ if (UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())) {
+ return getWorkProfileUserHandle();
+ } else if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+ return getPrivateProfileUserHandle();
+ }
+ return getPersonalProfileUserHandle();
}
private boolean hasWorkProfile() {
@@ -816,7 +842,15 @@
&& (UserHandle.myUserId() == getCloneProfileUserHandle().getIdentifier());
}
+ protected final boolean isLaunchedAsPrivateProfile() {
+ return getPrivateProfileUserHandle() != null
+ && (UserHandle.myUserId() == getPrivateProfileUserHandle().getIdentifier());
+ }
+
protected boolean shouldShowTabs() {
+ if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+ return false;
+ }
return hasWorkProfile() && ENABLE_TABBED_VIEW;
}
@@ -2619,6 +2653,11 @@
return resolveInfo.userHandle;
}
+ private boolean privateSpaceEnabled() {
+ return mIsIntentPicker && android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.allowResolverSheetForPrivateSpace();
+ }
+
/**
* An a11y delegate that expands resolver drawer when gesture navigation reaches a partially
* invisible target in the list.
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c89cfc4..5705b7e 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -37,6 +37,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -63,6 +64,8 @@
PackageMonitorCallback mPackageMonitorCallback;
+ private Executor mExecutor;
+
@UnsupportedAppUsage
public PackageMonitor() {
final boolean isCore = UserHandle.isCore(android.os.Process.myUid());
@@ -106,8 +109,8 @@
if (mPackageMonitorCallback == null) {
PackageManager pm = mRegisteredContext.getPackageManager();
if (pm != null) {
- mPackageMonitorCallback = new PackageMonitorCallback(this,
- new HandlerExecutor(mRegisteredHandler));
+ mExecutor = new HandlerExecutor(mRegisteredHandler);
+ mPackageMonitorCallback = new PackageMonitorCallback(this);
int userId = user != null ? user.getIdentifier() : mRegisteredContext.getUserId();
pm.registerPackageMonitorCallback(mPackageMonitorCallback, userId);
}
@@ -131,6 +134,7 @@
}
mPackageMonitorCallback = null;
mRegisteredContext = null;
+ mExecutor = null;
}
public void onBeginPackageChanges() {
@@ -362,6 +366,13 @@
doHandlePackageEvent(intent);
}
+
+ private void postHandlePackageEvent(Intent intent) {
+ if (mExecutor != null) {
+ mExecutor.execute(() -> doHandlePackageEvent(intent));
+ }
+ }
+
/**
* Handle the package related event
* @param intent the intent that contains package related event information
@@ -516,13 +527,10 @@
}
private static final class PackageMonitorCallback extends IRemoteCallback.Stub {
+ private final WeakReference<PackageMonitor> mMonitorWeakReference;
- private final PackageMonitor mPackageMonitor;
- private final Executor mExecutor;
-
- PackageMonitorCallback(PackageMonitor monitor, Executor executor) {
- mPackageMonitor = monitor;
- mExecutor = executor;
+ PackageMonitorCallback(PackageMonitor monitor) {
+ mMonitorWeakReference = new WeakReference<>(monitor);
}
@Override
@@ -537,7 +545,10 @@
Log.w(TAG, "No intent is set for PackageMonitorCallback");
return;
}
- mExecutor.execute(() -> mPackageMonitor.doHandlePackageEvent(intent));
+ PackageMonitor monitor = mMonitorWeakReference.get();
+ if (monitor != null) {
+ monitor.postHandlePackageEvent(intent);
+ }
}
}
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9c883d1..56ea52d 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -225,6 +225,19 @@
return translateNativeSensorToJavaSensor(env, sensor, *sensorList[index]) != NULL;
}
+static jboolean nativeGetDefaultDeviceSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager,
+ jobject sensor, jint index) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+
+ Vector<Sensor> sensorList;
+ ssize_t count = mgr->getDefaultDeviceSensorList(sensorList);
+ if (ssize_t(index) >= count) {
+ return false;
+ }
+
+ return translateNativeSensorToJavaSensor(env, sensor, sensorList[index]) != NULL;
+}
+
static void
nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
@@ -539,6 +552,9 @@
{"nativeGetSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
(void *)nativeGetSensorAtIndex},
+ {"nativeGetDefaultDeviceSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
+ (void *)nativeGetDefaultDeviceSensorAtIndex},
+
{"nativeGetDynamicSensors", "(JLjava/util/List;)V", (void *)nativeGetDynamicSensors},
{"nativeGetRuntimeSensors", "(JILjava/util/List;)V", (void *)nativeGetRuntimeSensors},
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index b6813ff..b209c7c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -30,6 +30,7 @@
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.allOf;
@@ -46,6 +47,7 @@
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;
@@ -88,7 +90,8 @@
public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
new ActivityTestRule<>(ResolverWrapperActivity.class, false,
false);
-
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void cleanOverrideData() {
sOverrides.reset();
@@ -1156,6 +1159,97 @@
sOverrides.cloneProfileUserHandle)));
}
+ @Test
+ public void testTriggerFromPrivateProfile_withoutWorkProfile() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> privateResolvedComponentInfos =
+ createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+ setupResolverControllers(privateResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+ onView(withId(R.id.button_once)).check(matches(not(isEnabled())));
+ onView(withId(R.id.button_always)).check(matches(not(isEnabled())));
+ for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ sOverrides.privateProfileUserHandle);
+ }
+ }
+
+ @Test
+ public void testTriggerFromPrivateProfile_withWorkProfilePresent(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> privateResolvedComponentInfos =
+ createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+ setupResolverControllers(privateResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ assertEquals(activity.getMultiProfilePagerAdapterCount(), 1);
+ for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ sOverrides.privateProfileUserHandle);
+ }
+ }
+
+ @Test
+ public void testPrivateProfile_triggerFromPrimaryUser_withWorkProfilePresent(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+ sOverrides.workProfileUserHandle);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getAdapter().getCount(), is(2));
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ activity.getPersonalProfileUserHandle());
+ }
+ }
+
+ @Test
+ public void testPrivateProfile_triggerFromWorkProfile(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+ sOverrides.workProfileUserHandle);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getAdapter().getCount(), is(2));
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+ assertTrue(resolvedInfo.getResolveInfoAt(0).userHandle.equals(
+ activity.getPersonalProfileUserHandle()) || resolvedInfo.getResolveInfoAt(
+ 0).userHandle.equals(activity.getWorkProfileUserHandle()));
+ }
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -1237,6 +1331,10 @@
ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(11);
}
+ private void markPrivateProfileUserAvailable() {
+ ResolverWrapperActivity.sOverrides.privateProfileUserHandle = UserHandle.of(12);
+ }
+
private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
@@ -1256,4 +1354,13 @@
eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> resolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(resolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index e193de0..862cbd5 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -88,6 +88,10 @@
return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
}
+ int getMultiProfilePagerAdapterCount(){
+ return mMultiProfilePagerAdapter.getCount();
+ }
+
@Override
public boolean isVoiceInteraction() {
if (sOverrides.isVoiceInteraction != null) {
@@ -144,6 +148,11 @@
}
@Override
+ protected UserHandle getPrivateProfileUserHandle() {
+ return sOverrides.privateProfileUserHandle;
+ }
+
+ @Override
protected UserHandle getTabOwnerUserHandleForLaunch() {
if (sOverrides.tabOwnerUserHandleForLaunch == null) {
return super.getTabOwnerUserHandleForLaunch();
@@ -176,6 +185,7 @@
public Boolean isVoiceInteraction;
public UserHandle workProfileUserHandle;
public UserHandle cloneProfileUserHandle;
+ public UserHandle privateProfileUserHandle;
public UserHandle tabOwnerUserHandleForLaunch;
public Integer myUserId;
public boolean hasCrossProfileIntents;
@@ -191,6 +201,7 @@
workResolverListController = mock(ResolverListController.class);
workProfileUserHandle = null;
cloneProfileUserHandle = null;
+ privateProfileUserHandle = null;
tabOwnerUserHandleForLaunch = null;
myUserId = null;
hasCrossProfileIntents = true;
diff --git a/core/tests/overlaytests/Android.mk b/core/tests/overlaytests/Android.mk
deleted file mode 100644
index b798d87..0000000
--- a/core/tests/overlaytests/Android.mk
+++ /dev/null
@@ -1,15 +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.
-
-include $(call all-subdir-makefiles)
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
deleted file mode 100644
index d58d939..0000000
--- a/core/tests/overlaytests/host/Android.mk
+++ /dev/null
@@ -1,19 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Include to build test-apps.
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/core/tests/overlaytests/host/test-apps/Android.mk b/core/tests/overlaytests/host/test-apps/Android.mk
deleted file mode 100644
index 5c7187e..0000000
--- a/core/tests/overlaytests/host/test-apps/Android.mk
+++ /dev/null
@@ -1,16 +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.
-
-include $(call all-subdir-makefiles)
-
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
new file mode 100644
index 0000000..bb7d63e
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
@@ -0,0 +1,57 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_NonPlatformSignatureOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package com.android.server.om.hosttest.signature_overlay_bad",
+ ],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_PlatformSignatureStaticOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ manifest: "static/AndroidManifest.xml",
+ aaptflags: [
+ "--custom-package com.android.server.om.hosttest.signature_overlay_static",
+ ],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_PlatformSignatureOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.signature_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+}
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
deleted file mode 100644
index b453cde9..0000000
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ /dev/null
@@ -1,56 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-my_package_prefix := com.android.server.om.hosttest.signature_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_NonPlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_static
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
new file mode 100644
index 0000000..ee0c0e5
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
@@ -0,0 +1,97 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_UpdateOverlay",
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ static_libs: ["androidx.test.rules"],
+ aaptflags: ["--no-resource-removal"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_FrameworkOverlayV1",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.framework_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+ resource_dirs: ["framework/v1/res"],
+ manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_FrameworkOverlayV2",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.framework_overlay_v2",
+ "--version-code",
+ "2",
+ "--version-name",
+ "v2",
+ ],
+ resource_dirs: ["framework/v2/res"],
+ manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_AppOverlayV1",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.app_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+ resource_dirs: ["app/v1/res"],
+ manifest: "app/v1/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_AppOverlayV2",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.app_overlay_v2",
+ "--version-code",
+ "2",
+ "--version-name",
+ "v2",
+ ],
+ resource_dirs: ["app/v2/res"],
+ manifest: "app/v2/AndroidManifest.xml",
+}
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
deleted file mode 100644
index 77fc887..0000000
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ /dev/null
@@ -1,93 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT_FLAGS := --no-resource-removal
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.framework_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v1/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v2/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.app_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
-LOCAL_MANIFEST_FILE := app/v1/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
-LOCAL_MANIFEST_FILE := app/v2/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4530f6..13d38d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -347,7 +347,9 @@
<!-- Allow IMS service entitlement app to schedule jobs to run when app in background. -->
<allow-in-power-save-except-idle package="com.android.imsserviceentitlement" />
- <!-- Allow device lock controller app to schedule jobs and alarms when app in background,
- otherwise, it may not be able to enforce provision for managed devices. -->
+ <!-- Allow device lock controller app to schedule jobs and alarms, and have network access
+ when app in background; otherwise, it may not be able to enforce provision for managed
+ devices. -->
<allow-in-power-save package="com.android.devicelockcontroller" />
+ <allow-in-data-usage-save package="com.android.devicelockcontroller" />
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c6f920f..b9efe65 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -575,6 +575,7 @@
<permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
<permission name="android.permission.BIND_WALLPAPER"/>
<permission name="android.permission.CUSTOMIZE_SYSTEM_UI"/>
+ <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
</privapp-permissions>
<privapp-permissions package="com.android.dynsystem">
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
index db8ebb4..1ac5db6 100644
--- a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -18,54 +18,63 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- style="@style/ScrollViewStyle">
+ style="@style/ScrollViewStyle"
+ android:importantForAccessibility="no">
<LinearLayout
- android:id="@+id/data_transfer_confirmation"
- style="@style/ContainerLayout">
-
- <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
-
- <ImageView
- android:id="@+id/header_icon"
- android:layout_width="match_parent"
- android:layout_height="32dp"
- android:gravity="center"
- android:layout_marginTop="18dp"
- android:src="@drawable/ic_warning"
- android:contentDescription="@null" />
-
- <LinearLayout style="@style/Description">
-
- <TextView
- android:id="@+id/title"
- style="@style/DescriptionTitle" />
-
- <TextView
- android:id="@+id/summary"
- style="@style/DescriptionSummary" />
-
- </LinearLayout>
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:importantForAccessibility="no">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="18dp">
+ android:id="@+id/data_transfer_confirmation"
+ style="@style/ContainerLayout">
- <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+ <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
- <Button
- android:id="@+id/btn_positive"
- style="@style/PositiveButton"
- android:text="@string/consent_yes" />
+ <ImageView
+ android:id="@+id/header_icon"
+ android:layout_width="match_parent"
+ android:layout_height="32dp"
+ android:gravity="center"
+ android:layout_marginTop="18dp"
+ android:src="@drawable/ic_warning"
+ android:contentDescription="@null" />
- <Button
- android:id="@+id/btn_negative"
- style="@style/NegativeButton"
- android:text="@string/consent_no" />
+ <LinearLayout style="@style/Description">
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/DescriptionTitle" />
+
+ <TextView
+ android:id="@+id/summary"
+ style="@style/DescriptionSummary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="18dp">
+
+ <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+ <Button
+ android:id="@+id/btn_positive"
+ style="@style/PositiveButton"
+ android:text="@string/consent_yes" />
+
+ <Button
+ android:id="@+id/btn_negative"
+ style="@style/NegativeButton"
+ android:text="@string/consent_no" />
+
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 933be11..6a4bb21 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -134,8 +134,7 @@
@UsesReflection({
// As the runtime class name is used to generate the returned name, and the returned
// name may be used used with reflection, generate the necessary keep rules.
- @KeepTarget(classConstant = LocalTransport.class),
- @KeepTarget(extendsClassConstant = LocalTransport.class)
+ @KeepTarget(instanceOfClassConstant = LocalTransport.class)
})
@Override
public String name() {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
new file mode 100644
index 0000000..472484a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalFoundationApi::class)
+
+package com.android.systemui.keyguard.ui.composable
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+
+/** Container for lockscreen content that handles long-press to bring up the settings menu. */
+@Composable
+fun LockscreenLongPress(
+ viewModel: KeyguardLongPressViewModel,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
+) {
+ val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+ val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
+ val interactionSource = remember { MutableInteractionSource() }
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ enabled = isEnabled,
+ onLongClick = viewModel::onLongPress,
+ onClick = {},
+ interactionSource = interactionSource,
+ // Passing null for the indication removes the ripple effect.
+ indication = null,
+ )
+ .pointerInput(settingsMenuBounds) {
+ awaitEachGesture {
+ val pointerInputChange = awaitFirstDown()
+ if (settingsMenuBounds?.contains(pointerInputChange.position) == false) {
+ viewModel.onTouchedOutside()
+ }
+ }
+ },
+ ) {
+ content(setSettingsMenuBounds)
+ }
+}
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 93f31ec..67a6820 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
@@ -14,36 +14,15 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalFoundationApi::class)
-
package com.android.systemui.keyguard.ui.composable
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.toComposeRect
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.qualifiers.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -68,8 +47,8 @@
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
- @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
private val lockscreenContent: Lazy<LockscreenContent>,
+ private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -93,9 +72,8 @@
modifier: Modifier,
) {
LockscreenScene(
- viewProvider = viewProvider,
- viewModel = viewModel,
lockscreenContent = lockscreenContent,
+ viewBasedLockscreenContent = viewBasedLockscreenContent,
modifier = modifier,
)
}
@@ -116,98 +94,21 @@
@Composable
private fun SceneScope.LockscreenScene(
- viewProvider: () -> View,
- viewModel: LockscreenSceneViewModel,
lockscreenContent: Lazy<LockscreenContent>,
+ viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
modifier: Modifier = Modifier,
) {
- fun findSettingsMenu(): View {
- return viewProvider().requireViewById(R.id.keyguard_settings_button)
- }
-
- Box(
- modifier = modifier,
- ) {
- LongPressSurface(
- viewModel = viewModel.longPress,
- isSettingsMenuVisible = { findSettingsMenu().isVisible },
- settingsMenuBounds = {
- val bounds = android.graphics.Rect()
- findSettingsMenu().getHitRect(bounds)
- bounds.toComposeRect()
- },
- modifier = Modifier.fillMaxSize(),
- )
-
- if (UseLockscreenContent) {
- lockscreenContent
- .get()
- .Content(
- modifier = Modifier.fillMaxSize(),
- )
- } else {
- AndroidView(
- factory = { _ ->
- val keyguardRootView = viewProvider()
- // Remove the KeyguardRootView from any parent it might already have in legacy
- // code just in case (a view can't have two parents).
- (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
- keyguardRootView
- },
- modifier = Modifier.fillMaxSize(),
+ if (UseLockscreenContent) {
+ lockscreenContent
+ .get()
+ .Content(
+ modifier = modifier.fillMaxSize(),
+ )
+ } else {
+ with(viewBasedLockscreenContent.get()) {
+ Content(
+ modifier = modifier.fillMaxSize(),
)
}
-
- val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()
-
- Layout(
- modifier = Modifier.fillMaxSize(),
- content = {
- NotificationStack(
- viewModel = viewModel.notifications,
- isScrimVisible = false,
- )
- }
- ) { measurables, constraints ->
- check(measurables.size == 1)
- val height = notificationStackPosition.height.toInt()
- val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
- val placeable = measurables[0].measure(childConstraints)
- layout(constraints.maxWidth, constraints.maxHeight) {
- val start = (constraints.maxWidth - placeable.measuredWidth) / 2
- placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
- }
- }
}
}
-
-@Composable
-private fun LongPressSurface(
- viewModel: KeyguardLongPressViewModel,
- isSettingsMenuVisible: () -> Boolean,
- settingsMenuBounds: () -> Rect,
- modifier: Modifier = Modifier,
-) {
- val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
-
- Box(
- modifier =
- modifier
- .combinedClickable(
- enabled = isEnabled,
- onLongClick = viewModel::onLongPress,
- onClick = {},
- )
- .pointerInput(Unit) {
- awaitEachGesture {
- val pointerInputChange = awaitFirstDown()
- if (
- isSettingsMenuVisible() &&
- !settingsMenuBounds().contains(pointerInputChange.position)
- ) {
- viewModel.onTouchedOutside()
- }
- }
- },
- )
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
new file mode 100644
index 0000000..976161b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.composable
+
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Renders the content of the lockscreen.
+ *
+ * This is different from [LockscreenContent] (which is pure compose) and uses a view-based
+ * implementation of the lockscreen scene content that relies on [KeyguardRootView].
+ *
+ * TODO(b/316211368): remove this once [LockscreenContent] is feature complete.
+ */
+class ViewBasedLockscreenContent
+@Inject
+constructor(
+ private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
+) {
+ @Composable
+ fun SceneScope.Content(
+ modifier: Modifier = Modifier,
+ ) {
+ fun findSettingsMenu(): View {
+ return viewProvider().requireViewById(R.id.keyguard_settings_button)
+ }
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy
+ // code just in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+
+ val notificationStackPosition by
+ viewModel.keyguardRoot.notificationBounds.collectAsState()
+
+ Layout(
+ modifier =
+ Modifier.fillMaxSize().onPlaced {
+ val settingsMenuView = findSettingsMenu()
+ onSettingsMenuPlaced(
+ if (settingsMenuView.isVisible) {
+ val bounds = Rect()
+ settingsMenuView.getHitRect(bounds)
+ bounds.toComposeRect()
+ } else {
+ null
+ }
+ )
+ },
+ content = {
+ NotificationStack(
+ viewModel = viewModel.notifications,
+ isScrimVisible = false,
+ )
+ }
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val height = notificationStackPosition.height.toInt()
+ val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
+ val placeable = measurables[0].measure(childConstraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ val start = (constraints.maxWidth - placeable.measuredWidth) / 2
+ placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 7eddaaf..86124c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -24,24 +24,35 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
import javax.inject.Inject
/** Renders the lockscreen scene when showing the communal glanceable hub. */
-class CommunalBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class CommunalBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
override val id: String = "communal"
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- Box(modifier.background(Color.Black)) {
- Text(
- text = "TODO(b/316211368): communal blueprint",
- color = Color.White,
- modifier = Modifier.align(Alignment.Center),
- )
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): communal blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 7314453..d9d98cb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -17,17 +17,20 @@
package com.android.systemui.keyguard.ui.composable.blueprint
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,6 +54,7 @@
private val lockSection: LockSection,
private val ambientIndicationSection: AmbientIndicationSection,
private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
) : LockscreenSceneBlueprint {
override val id: String = "default"
@@ -59,102 +63,116 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- },
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
modifier = modifier,
- ) { measurables, constraints ->
- check(measurables.size == 5)
- val (
- aboveLockIconMeasurable,
- lockIconMeasurable,
- belowLockIconMeasurable,
- startShortcutMeasurable,
- endShortcutMeasurable,
- ) = measurables
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ with(lockSection) { LockIcon() }
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val belowLockIconMeasurable = measurables[2]
+ val startShortcutMeasurable = measurables[3]
+ val endShortcutMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 4c119c7..4704f5c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -17,17 +17,20 @@
package com.android.systemui.keyguard.ui.composable.blueprint
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,6 +54,7 @@
private val lockSection: LockSection,
private val ambientIndicationSection: AmbientIndicationSection,
private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
) : LockscreenSceneBlueprint {
override val id: String = "shortcuts-besides-udfps"
@@ -59,105 +63,123 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- // Constrained to the left of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
-
- with(lockSection) { LockIcon() }
-
- // Constrained to the right of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
- }
- },
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
modifier = modifier,
- ) { measurables, constraints ->
- check(measurables.size == 5)
- val (
- aboveLockIconMeasurable,
- startSideShortcutMeasurable,
- lockIconMeasurable,
- endSideShortcutMeasurable,
- belowLockIconMeasurable,
- ) = measurables
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
+ // Constrained to the left of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ with(lockSection) { LockIcon() }
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
- val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
- )
+ // Constrained to the right of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- startSideShortcutPlaceable.placeRelative(
- x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- endSideShortcutPlaceable.placeRelative(
- x =
- lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 -
- endSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val startSideShortcutMeasurable = measurables[1]
+ val lockIconMeasurable = measurables[2]
+ val endSideShortcutMeasurable = measurables[3]
+ val belowLockIconMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val startSideShortcutPlaceable =
+ startSideShortcutMeasurable.measure(noMinConstraints)
+ val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ startSideShortcutPlaceable.placeRelative(
+ x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ endSideShortcutPlaceable.placeRelative(
+ x =
+ lockIconBounds.right +
+ (constraints.maxWidth - lockIconBounds.right) / 2 -
+ endSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 7545d5f..fdf1166 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -24,6 +24,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@@ -33,18 +35,27 @@
* Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
* tablet form factor).
*/
-class SplitShadeBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class SplitShadeBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
override val id: String = "split-shade"
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- Box(modifier.background(Color.Black)) {
- Text(
- text = "TODO(b/316211368): split shade blueprint",
- color = Color.White,
- modifier = Modifier.align(Alignment.Center),
- )
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): split shade blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index ebc346d..c547e2b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -16,76 +16,25 @@
package com.android.systemui.keyguard.ui.composable.section
-import android.view.View
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onPlaced
-import androidx.compose.ui.layout.positionInWindow
-import androidx.compose.ui.viewinterop.AndroidView
-import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
-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.notifications.ui.composable.NotificationStack
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
class NotificationSection
@Inject
constructor(
- private val rootViewModel: KeyguardRootViewModel,
- private val sharedNotificationContainer: SharedNotificationContainer,
- private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
- private val controller: NotificationStackScrollLayoutController,
- private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
- @Main private val dispatcher: CoroutineDispatcher,
- private val sceneContainerFlags: SceneContainerFlags,
+ private val viewModel: NotificationsPlaceholderViewModel,
) {
@Composable
fun SceneScope.Notifications(modifier: Modifier = Modifier) {
- MovableElement(
- key = NotificationsElementKey,
+ NotificationStack(
+ viewModel = viewModel,
+ isScrimVisible = false,
modifier = modifier,
- ) {
- val (bounds, onBoundsChanged) = remember { mutableStateOf<Pair<Float, Float>?>(null) }
- LaunchedEffect(bounds) {
- bounds?.let {
- rootViewModel.onNotificationContainerBoundsChanged(bounds.first, bounds.second)
- }
- }
-
- AndroidView(
- factory = { context ->
- View(context, null).apply {
- id = R.id.nssl_placeholder
- SharedNotificationContainerBinder.bind(
- view = sharedNotificationContainer,
- viewModel = sharedNotificationContainerViewModel,
- sceneContainerFlags = sceneContainerFlags,
- controller = controller,
- notificationStackSizeCalculator = notificationStackSizeCalculator,
- mainImmediateDispatcher = dispatcher,
- )
- }
- },
- modifier =
- modifier.onPlaced {
- val positionInRoot = it.positionInWindow()
- val size = it.size
- onBoundsChanged(positionInRoot.y to positionInRoot.y + size.height)
- },
- )
- }
+ )
}
}
-
-private val NotificationsElementKey = ElementKey("Notifications")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
new file mode 100644
index 0000000..44b0535
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
@@ -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.systemui.keyguard.ui.composable.section
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+class SettingsMenuSection
+@Inject
+constructor(
+ private val viewModel: KeyguardSettingsMenuViewModel,
+ private val longPressViewModel: KeyguardLongPressViewModel,
+ private val vibratorHelper: VibratorHelper,
+ private val activityStarter: ActivityStarter,
+) {
+ @Composable
+ @SuppressWarnings("InflateParams") // null is passed into the inflate call, on purpose.
+ fun SettingsMenu(
+ onPlaced: (Rect?) -> Unit,
+ modifier: Modifier = Modifier,
+ ) {
+ val (disposableHandle, setDisposableHandle) =
+ remember { mutableStateOf<DisposableHandle?>(null) }
+ AndroidView(
+ factory = { context ->
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.keyguard_settings_popup_menu,
+ null,
+ )
+ .apply {
+ isVisible = false
+ alpha = 0f
+
+ setDisposableHandle(
+ KeyguardSettingsViewBinder.bind(
+ view = this,
+ viewModel = viewModel,
+ longPressViewModel = longPressViewModel,
+ rootViewModel = null,
+ vibratorHelper = vibratorHelper,
+ activityStarter = activityStarter,
+ )
+ )
+ }
+ },
+ onRelease = { disposableHandle?.dispose() },
+ modifier =
+ modifier
+ .padding(
+ bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset),
+ )
+ .padding(
+ horizontal =
+ dimensionResource(R.dimen.keyguard_affordance_horizontal_offset),
+ )
+ .onPlaced { coordinates ->
+ onPlaced(
+ if (!coordinates.size.toSize().isEmpty()) {
+ Rect(coordinates.positionInParent(), coordinates.size.toSize())
+ } else {
+ null
+ }
+ )
+ },
+ )
+ }
+}
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 92f66902..387f2e1 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
@@ -101,7 +101,7 @@
}
/** Alerts listener and plugin that the plugin has been created. */
- public void onCreate() {
+ public synchronized void onCreate() {
boolean loadPlugin = mListener.onPluginAttached(this);
if (!loadPlugin) {
if (mPlugin != null) {
@@ -128,7 +128,7 @@
}
/** Alerts listener and plugin that the plugin is being shutdown. */
- public void onDestroy() {
+ public synchronized void onDestroy() {
logDebug("onDestroy");
unloadPlugin();
mListener.onPluginDetached(this);
@@ -143,12 +143,13 @@
/**
* Loads and creates the plugin if it does not exist.
*/
- public void loadPlugin() {
+ public synchronized void loadPlugin() {
if (mPlugin != null) {
logDebug("Load request when already loaded");
return;
}
+ // Both of these calls take about 1 - 1.5 seconds in test runs
mPlugin = mPluginFactory.createPlugin();
mPluginContext = mPluginFactory.createPluginContext();
if (mPlugin == null || mPluginContext == null) {
@@ -171,7 +172,7 @@
*
* This will free the associated memory if there are not other references.
*/
- public void unloadPlugin() {
+ public synchronized void unloadPlugin() {
if (mPlugin == null) {
logDebug("Unload request when already unloaded");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index b1de127..49e0df6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -31,7 +31,7 @@
* Controls the interaction between {@link MagnetizedObject} and
* {@link MagnetizedObject.MagneticTarget}.
*/
-class DismissAnimationController {
+class DragToInteractAnimationController {
private static final boolean ENABLE_FLING_TO_DISMISS_MENU = false;
private static final float COMPLETELY_OPAQUE = 1.0f;
private static final float COMPLETELY_TRANSPARENT = 0.0f;
@@ -45,7 +45,7 @@
private float mMinDismissSize;
private float mSizePercent;
- DismissAnimationController(DismissView dismissView, MenuView menuView) {
+ DragToInteractAnimationController(DismissView dismissView, MenuView menuView) {
mDismissView = dismissView;
mDismissView.setPivotX(dismissView.getWidth() / 2.0f);
mDismissView.setPivotY(dismissView.getHeight() / 2.0f);
@@ -127,7 +127,7 @@
* @param event that move the magnetized object which is also the menu list view.
* @return true if the location of the motion events moves within the magnetic field of a
* target, but false if didn't set
- * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+ * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
boolean maybeConsumeMoveMotionEvent(MotionEvent event) {
return mMagnetizedObject.maybeConsumeMotionEvent(event);
@@ -140,7 +140,7 @@
* @param event that move the magnetized object which is also the menu list view.
* @return true if the location of the motion events moves within the magnetic field of a
* target, but false if didn't set
- * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+ * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
boolean maybeConsumeUpMotionEvent(MotionEvent event) {
return mMagnetizedObject.maybeConsumeMotionEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 34d7cec..a270558 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -73,7 +73,7 @@
private final ValueAnimator mFadeOutAnimator;
private final Handler mHandler;
private boolean mIsFadeEffectEnabled;
- private DismissAnimationController.DismissCallback mDismissCallback;
+ private DragToInteractAnimationController.DismissCallback mDismissCallback;
private Runnable mSpringAnimationsEndAction;
// Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
@@ -171,7 +171,7 @@
}
void setDismissCallback(
- DismissAnimationController.DismissCallback dismissCallback) {
+ DragToInteractAnimationController.DismissCallback dismissCallback) {
mDismissCallback = dismissCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index d01590f..52e7b91 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -40,13 +40,13 @@
private final PointF mMenuTranslationDown = new PointF();
private boolean mIsDragging = false;
private float mTouchSlop;
- private final DismissAnimationController mDismissAnimationController;
+ private final DragToInteractAnimationController mDragToInteractAnimationController;
private Optional<Runnable> mOnActionDownEnd = Optional.empty();
MenuListViewTouchHandler(MenuAnimationController menuAnimationController,
- DismissAnimationController dismissAnimationController) {
+ DragToInteractAnimationController dragToInteractAnimationController) {
mMenuAnimationController = menuAnimationController;
- mDismissAnimationController = dismissAnimationController;
+ mDragToInteractAnimationController = dragToInteractAnimationController;
}
@Override
@@ -67,7 +67,7 @@
mMenuTranslationDown.set(menuView.getTranslationX(), menuView.getTranslationY());
mMenuAnimationController.cancelAnimations();
- mDismissAnimationController.maybeConsumeDownMotionEvent(motionEvent);
+ mDragToInteractAnimationController.maybeConsumeDownMotionEvent(motionEvent);
mOnActionDownEnd.ifPresent(Runnable::run);
break;
@@ -78,9 +78,10 @@
mMenuAnimationController.onDraggingStart();
}
- mDismissAnimationController.showDismissView(/* show= */ true);
+ mDragToInteractAnimationController.showDismissView(/* show= */ true);
- if (!mDismissAnimationController.maybeConsumeMoveMotionEvent(motionEvent)) {
+ if (!mDragToInteractAnimationController.maybeConsumeMoveMotionEvent(
+ motionEvent)) {
mMenuAnimationController.moveToPositionX(mMenuTranslationDown.x + dx);
mMenuAnimationController.moveToPositionYIfNeeded(
mMenuTranslationDown.y + dy);
@@ -94,17 +95,18 @@
mIsDragging = false;
if (mMenuAnimationController.maybeMoveToEdgeAndHide(endX)) {
- mDismissAnimationController.showDismissView(/* show= */ false);
+ mDragToInteractAnimationController.showDismissView(/* show= */ false);
mMenuAnimationController.fadeOutIfEnabled();
return true;
}
- if (!mDismissAnimationController.maybeConsumeUpMotionEvent(motionEvent)) {
+ if (!mDragToInteractAnimationController.maybeConsumeUpMotionEvent(
+ motionEvent)) {
mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
mMenuAnimationController.flingMenuThenSpringToEdge(endX,
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- mDismissAnimationController.showDismissView(/* show= */ false);
+ mDragToInteractAnimationController.showDismissView(/* show= */ false);
}
// Avoid triggering the listener of the item.
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 ff3a9e3..62d5feb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -94,7 +94,7 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final IAccessibilityFloatingMenu mFloatingMenu;
private final SecureSettings mSecureSettings;
- private final DismissAnimationController mDismissAnimationController;
+ private final DragToInteractAnimationController mDragToInteractAnimationController;
private final MenuViewModel mMenuViewModel;
private final Observer<Boolean> mDockTooltipObserver =
this::onDockTooltipVisibilityChanged;
@@ -188,29 +188,30 @@
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController = new DismissAnimationController(mDismissView, mMenuView);
- mDismissAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, mMenuView);
+ mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ true);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ true);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velocityX, float velocityY, boolean wasFlungOut) {
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
}
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
hideMenuAndShowMessage();
mDismissView.hide();
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
}
});
mMenuListViewTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
- mDismissAnimationController);
+ mDragToInteractAnimationController);
mMenuView.addOnItemTouchListenerToList(mMenuListViewTouchHandler);
mMenuView.setMoveToTuckedListener(this);
@@ -243,7 +244,7 @@
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
mDismissView.updateResources();
- mDismissAnimationController.updateResources();
+ mDragToInteractAnimationController.updateResources();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
index 629b361..cfa5294 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
@@ -65,4 +65,11 @@
SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
}
}
+
+ /** The arrow navigation that was operating the slider has stopped. */
+ fun onArrowUp() {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.ARROW_UP, previousEvent.currentProgress)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
index d89cf63..10098fa 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -58,7 +58,7 @@
override suspend fun iterateState(event: SliderEvent) {
when (currentState) {
- SliderState.IDLE -> handleIdle(event.type)
+ SliderState.IDLE -> handleIdle(event.type, event.currentProgress)
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)
@@ -67,17 +67,26 @@
SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH -> setState(SliderState.IDLE)
SliderState.JUMP_TRACK_LOCATION_SELECTED -> handleJumpToTrack(event.type)
SliderState.JUMP_BOOKEND_SELECTED -> handleJumpToBookend(event.type)
+ SliderState.ARROW_HANDLE_MOVED_ONCE -> handleArrowOnce(event.type)
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY ->
+ handleArrowContinuous(event.type, event.currentProgress)
+ SliderState.ARROW_HANDLE_REACHED_BOOKEND -> handleArrowBookend()
}
latestProgress = event.currentProgress
}
- private fun handleIdle(newEventType: SliderEventType) {
+ private fun handleIdle(newEventType: SliderEventType, currentProgress: Float) {
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)
+ } else if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) {
+ val state =
+ if (bookendReached(currentProgress)) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+ else SliderState.ARROW_HANDLE_MOVED_ONCE
+ setState(state)
}
}
@@ -176,6 +185,13 @@
SliderState.DRAG_HANDLE_REACHED_BOOKEND -> executeOnBookend()
SliderState.JUMP_TRACK_LOCATION_SELECTED ->
sliderListener.onProgressJump(latestProgress)
+ SliderState.ARROW_HANDLE_MOVED_ONCE -> sliderListener.onSelectAndArrow(latestProgress)
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY -> sliderListener.onProgress(latestProgress)
+ SliderState.ARROW_HANDLE_REACHED_BOOKEND -> {
+ executeOnBookend()
+ // This transitory execution must also reset the state
+ resetState()
+ }
else -> {}
}
}
@@ -204,6 +220,43 @@
currentProgress <= config.lowerBookendThreshold
}
+ private fun handleArrowOnce(newEventType: SliderEventType) {
+ val nextState =
+ when (newEventType) {
+ SliderEventType.STARTED_TRACKING_TOUCH -> {
+ // Launching the timer and going to WAIT
+ timerJob = launchTimer()
+ SliderState.WAIT
+ }
+ SliderEventType.PROGRESS_CHANGE_BY_PROGRAM ->
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ SliderEventType.ARROW_UP -> SliderState.IDLE
+ else -> SliderState.ARROW_HANDLE_MOVED_ONCE
+ }
+ setState(nextState)
+ }
+
+ private fun handleArrowContinuous(newEventType: SliderEventType, currentProgress: Float) {
+ val reachedBookend = bookendReached(currentProgress)
+ val nextState =
+ when (newEventType) {
+ SliderEventType.ARROW_UP -> SliderState.IDLE
+ SliderEventType.STARTED_TRACKING_TOUCH -> {
+ // Launching the timer and going to WAIT
+ timerJob = launchTimer()
+ SliderState.WAIT
+ }
+ SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> {
+ if (reachedBookend) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+ else SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ }
+ else -> SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ }
+ setState(nextState)
+ }
+
+ private fun handleArrowBookend() = setState(SliderState.IDLE)
+
@VisibleForTesting
fun setState(state: SliderState) {
currentState = state
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
index 413e277..4a63941 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
@@ -29,5 +29,5 @@
/* The slider has stopped tracking touch events. */
STOPPED_TRACKING_TOUCH,
/* The external (not touch) stimulus that was modifying the slider progress has stopped. */
- EXTERNAL_STIMULUS_RELEASE,
+ ARROW_UP,
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
index fe092e6..de6ddd7 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
@@ -32,6 +32,12 @@
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. */
+ /* The slider handle moved to a bookend after it was selected. */
JUMP_BOOKEND_SELECTED,
+ /** The slider handle moved due to single select-and-arrow operation */
+ ARROW_HANDLE_MOVED_ONCE,
+ /** The slider handle moves continuously due to constant select-and-arrow operations */
+ ARROW_HANDLE_MOVES_CONTINUOUSLY,
+ /** The slider handle reached a bookend due to a select-and-arrow operation */
+ ARROW_HANDLE_REACHED_BOOKEND,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index eee5206..96e83b0 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
@@ -241,7 +241,6 @@
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
settingsMenu.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = settingsMenu,
viewModel = viewModel.settingsMenuViewModel,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
index c54203c..c6dfcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
@@ -20,12 +20,10 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
class KeyguardSettingsButtonOnTouchListener(
- private val view: LaunchableLinearLayout,
private val viewModel: KeyguardSettingsMenuViewModel,
) : View.OnTouchListener {
@@ -41,8 +39,10 @@
MotionEvent.ACTION_UP -> {
view.isPressed = false
val distanceMoved =
- motionEvent
- .rawDistanceFrom(downPositionDisplayCoords.x, downPositionDisplayCoords.y)
+ motionEvent.rawDistanceFrom(
+ downPositionDisplayCoords.x,
+ downPositionDisplayCoords.y
+ )
val isClick = distanceMoved < ViewConfiguration.getTouchSlop()
viewModel.onTouchGestureEnded(isClick)
if (isClick) {
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 11e63e7..f67cb68 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
@@ -23,7 +23,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -43,15 +42,13 @@
object KeyguardSettingsViewBinder {
fun bind(
- parentView: View,
+ view: View,
viewModel: KeyguardSettingsMenuViewModel,
longPressViewModel: KeyguardLongPressViewModel,
- rootViewModel: KeyguardRootViewModel,
+ rootViewModel: KeyguardRootViewModel?,
vibratorHelper: VibratorHelper,
activityStarter: ActivityStarter
): DisposableHandle {
- val view = parentView.requireViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button)
-
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -62,7 +59,6 @@
vibratorHelper.vibrate(KeyguardBottomAreaVibrations.Activated)
view.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = view,
viewModel = viewModel,
)
)
@@ -96,7 +92,7 @@
}
launch {
- rootViewModel.lastRootViewTapPosition.filterNotNull().collect { point ->
+ rootViewModel?.lastRootViewTapPosition?.filterNotNull()?.collect { point ->
if (view.isVisible) {
val hitRect = Rect()
view.getHitRect(hitRect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 24240df..940d1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -142,6 +142,11 @@
return true
}
+ if (renderer == null || onDestroy == null) {
+ Log.wtf(TAG, "Renderer/onDestroy should not be null.")
+ return true
+ }
+
when (message.what) {
KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d57e569..36bbe4e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -33,6 +33,7 @@
constructor(
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
+ val longPress: KeyguardLongPressViewModel,
) {
val isUdfpsVisible: Boolean
get() = authController.isUdfpsSupported
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index bc5090f..be1fa2b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -227,7 +227,7 @@
mListener.onChanged(mTracking, progress, false);
SeekableSliderEventProducer eventProducer =
mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
- if (eventProducer != null) {
+ if (eventProducer != null && fromUser) {
eventProducer.onProgressChanged(seekBar, progress, fromUser);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index fd258e3..9bcab57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -40,12 +40,12 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-/** Tests for {@link DismissAnimationController}. */
+/** Tests for {@link DragToInteractAnimationController}. */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class DismissAnimationControllerTest extends SysuiTestCase {
- private DismissAnimationController mDismissAnimationController;
+public class DragToInteractAnimationControllerTest extends SysuiTestCase {
+ private DragToInteractAnimationController mDragToInteractAnimationController;
private DismissView mDismissView;
@Rule
@@ -65,19 +65,20 @@
stubMenuViewAppearance);
mDismissView = spy(new DismissView(mContext));
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController = new DismissAnimationController(mDismissView, stubMenuView);
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, stubMenuView);
}
@Test
public void showDismissView_success() {
- mDismissAnimationController.showDismissView(true);
+ mDragToInteractAnimationController.showDismissView(true);
verify(mDismissView).show();
}
@Test
public void hideDismissView_success() {
- mDismissAnimationController.showDismissView(false);
+ mDragToInteractAnimationController.showDismissView(false);
verify(mDismissView).hide();
}
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 7f12c05..9c8de30 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
@@ -62,7 +62,7 @@
@Mock
private SecureSettings mSecureSettings;
@Mock
- private DismissAnimationController.DismissCallback mStubDismissCallback;
+ private DragToInteractAnimationController.DismissCallback mStubDismissCallback;
private RecyclerView mStubListView;
private MenuView mMenuView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 9797f2a..e1522f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -68,7 +68,7 @@
private MenuView mStubMenuView;
private MenuListViewTouchHandler mTouchHandler;
private MenuAnimationController mMenuAnimationController;
- private DismissAnimationController mDismissAnimationController;
+ private DragToInteractAnimationController mDragToInteractAnimationController;
private RecyclerView mStubListView;
private DismissView mDismissView;
@@ -92,10 +92,10 @@
mStubMenuView, stubMenuViewAppearance));
mDismissView = spy(new DismissView(mContext));
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController =
- spy(new DismissAnimationController(mDismissView, mStubMenuView));
+ mDragToInteractAnimationController =
+ spy(new DragToInteractAnimationController(mDismissView, mStubMenuView));
mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
- mDismissAnimationController);
+ mDragToInteractAnimationController);
final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
mStubListView.setAdapter(stubAdapter);
@@ -115,7 +115,7 @@
@Test
public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
- doReturn(false).when(mDismissAnimationController).maybeConsumeMoveMotionEvent(
+ doReturn(false).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
any(MotionEvent.class));
final int offset = 100;
final MotionEvent stubDownEvent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
index 71a56cd..c22d35c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
@@ -123,4 +123,25 @@
assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5F), latest)
}
+
+ @Test
+ fun onArrowUp_afterStartTrackingTouch_ArrowUpProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStartTrackingTouch(seekBar)
+ eventProducer.onArrowUp()
+
+ assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0f), latest)
+ }
+
+ @Test
+ fun onArrowUp_afterChangeByProgram_ArrowUpProduced_withProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+ eventProducer.onArrowUp()
+
+ assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0.5f), latest)
+ }
}
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
index 8d12e49..db04962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -528,6 +528,194 @@
verifyNoMoreInteractions(sliderStateListener)
}
+ @Test
+ fun onProgressChangeByProgram_atTheMiddle_onIdle_movesToArrowHandleMovedOnce() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ initTracker(testScheduler)
+
+ // GIVEN a progress due to an external source that lands at the middle of the slider
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the state moves to ARROW_HANDLE_MOVED_ONCE and the listener is called to play
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
+ verify(sliderStateListener).onSelectAndArrow(progress)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_atUpperBookend_onIdle_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a progress due to an external source that lands at the upper bookend
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes upper bookend haptics before moving back to IDLE
+ verify(sliderStateListener).onUpperBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_atLowerBookend_onIdle_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // WHEN a progress is recorded due to an external source that lands at the lower bookend
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes lower bookend haptics before moving to IDLE
+ verify(sliderStateListener).onLowerBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onArrowUp_onArrowMovedOnce_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the external stimulus is released
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+ // THEN the tracker moves back to IDLE and there are no haptics
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onStartTrackingTouch_onArrowMovedOnce_movesToWait() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the slider starts tracking touch
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // THEN the tracker moves back to WAIT and starts the waiting job. Also, there are no
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onArrowMovedOnce_movesToArrowMovesContinuously() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the slider gets an external progress change
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker moves to ARROW_HANDLE_MOVES_CONTINUOUSLY and calls the appropriate
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+ verify(sliderStateListener).onProgress(progress)
+ }
+
+ @Test
+ fun onArrowUp_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the external stimulus is released
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+ // THEN the tracker moves to IDLE and no haptics are played
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onStartTrackingTouch_onArrowMovesContinuously_movesToWait() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider starts tracking touch
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // THEN the tracker moves to WAIT and the wait job starts.
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onArrowMovesContinuously_preservesState() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider changes progress programmatically at the middle
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker stays in the same state and haptics are delivered appropriately
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+ verify(sliderStateListener).onProgress(progress)
+ }
+
+ @Test
+ fun onProgramProgress_atLowerBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider reaches the lower bookend programmatically
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes lower bookend haptics before moving to IDLE
+ verify(sliderStateListener).onLowerBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onProgramProgress_atUpperBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider reaches the lower bookend programmatically
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes upper bookend haptics before moving to IDLE
+ verify(sliderStateListener).onUpperBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
private fun initTracker(
scheduler: TestCoroutineScheduler,
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 6eabf44..5e57c83 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
@@ -17,13 +17,17 @@
package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -40,7 +44,11 @@
import java.lang.ref.WeakReference;
import java.util.Collections;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -104,6 +112,7 @@
mPluginInstance = mPluginInstanceFactory.create(
mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME,
TestPlugin.class, mPluginListener);
+ mPluginInstance.setIsDebug(true);
mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext());
}
@@ -158,7 +167,7 @@
@Test
public void testOnAttach_SkipLoad() {
- mPluginListener.mAttachReturn = false;
+ mPluginListener.mOnAttach = () -> false;
mPluginInstance.onCreate();
assertEquals(1, mPluginListener.mAttachedCount);
assertEquals(0, mPluginListener.mLoadCount);
@@ -166,6 +175,65 @@
assertInstances(0, 0);
}
+ @Test
+ public void testLoadUnloadSimultaneous_HoldsUnload() throws Exception {
+ final Semaphore loadLock = new Semaphore(1);
+ final Semaphore unloadLock = new Semaphore(1);
+
+ mPluginListener.mOnAttach = () -> false;
+ mPluginListener.mOnLoad = () -> {
+ assertNotNull(mPluginInstance.getPlugin());
+
+ // Allow the bg thread the opportunity to delete the plugin
+ loadLock.release();
+ Thread.yield();
+ boolean isLocked = getLock(unloadLock, 1000);
+
+ // Ensure the bg thread failed to do delete the plugin
+ assertNotNull(mPluginInstance.getPlugin());
+ // We expect that bgThread deadlocked holding the semaphore
+ assertFalse(isLocked);
+ };
+
+ AtomicBoolean isBgThreadFailed = new AtomicBoolean(false);
+ Thread bgThread = new Thread(() -> {
+ assertTrue(getLock(unloadLock, 10));
+ assertTrue(getLock(loadLock, 3000)); // Wait for the foreground thread
+ assertNotNull(mPluginInstance.getPlugin());
+ // Attempt to delete the plugin, this should block until the load completes
+ mPluginInstance.unloadPlugin();
+ assertNull(mPluginInstance.getPlugin());
+ unloadLock.release();
+ loadLock.release();
+ });
+
+ // This protects the test suite from crashing due to the uncaught exception.
+ bgThread.setUncaughtExceptionHandler((Thread t, Throwable ex) -> {
+ Log.e("testLoadUnloadSimultaneous_HoldsUnload", "Exception from BG Thread", ex);
+ isBgThreadFailed.set(true);
+ });
+
+ loadLock.acquire();
+ mPluginInstance.onCreate();
+
+ assertNull(mPluginInstance.getPlugin());
+ bgThread.start();
+ mPluginInstance.loadPlugin();
+
+ bgThread.join(5000);
+ assertFalse(isBgThreadFailed.get());
+ assertNull(mPluginInstance.getPlugin());
+ }
+
+ private boolean getLock(Semaphore lock, long millis) {
+ try {
+ return lock.tryAcquire(millis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ex) {
+ fail();
+ return false;
+ }
+ }
+
// This target class doesn't matter, it just needs to have a Requires to hit the flow where
// the mock version info is called.
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
@@ -226,7 +294,10 @@
}
public class FakeListener implements PluginListener<TestPlugin> {
- public boolean mAttachReturn = true;
+ public Supplier<Boolean> mOnAttach = null;
+ public Runnable mOnDetach = null;
+ public Runnable mOnLoad = null;
+ public Runnable mOnUnload = null;
public int mAttachedCount = 0;
public int mDetachedCount = 0;
public int mLoadCount = 0;
@@ -236,13 +307,16 @@
public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
mAttachedCount++;
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
- return mAttachReturn;
+ return mOnAttach != null ? mOnAttach.get() : true;
}
@Override
public void onPluginDetached(PluginLifecycleManager<TestPlugin> manager) {
mDetachedCount++;
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnDetach != null) {
+ mOnDetach.run();
+ }
}
@Override
@@ -261,6 +335,9 @@
assertEquals(expectedContext, pluginContext);
}
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnLoad != null) {
+ mOnLoad.run();
+ }
}
@Override
@@ -274,6 +351,9 @@
assertEquals(expectedPlugin, plugin);
}
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnUnload != null) {
+ mOnUnload.run();
+ }
}
}
}
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 7558974..1236fcf 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
@@ -38,6 +38,7 @@
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.metrics.LogMaker;
+import android.platform.test.annotations.DisableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -84,6 +85,7 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -218,6 +220,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
initController(/* viewIsAttached= */ true);
@@ -238,6 +241,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -258,6 +262,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -285,6 +290,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -306,6 +312,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -327,6 +334,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -348,6 +356,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -504,6 +513,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
initController(/* viewIsAttached= */ true);
mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true);
@@ -545,6 +555,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
// GIVEN: Controller is attached, active notifications is empty,
// and mNotificationStackScrollLayout.onKeyguard() is true
@@ -561,6 +572,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is not empty,
// and mNotificationStackScrollLayout.onKeyguard() is true
@@ -584,6 +596,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is not empty,
// and mNotificationStackScrollLayout.onKeyguard() is false
@@ -607,6 +620,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is empty,
// and mNotificationStackScrollLayout.onKeyguard() is false
@@ -623,6 +637,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
initController(/* viewIsAttached= */ true);
mController.onKeyguardTransitionChanged(
@@ -633,6 +648,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
initController(/* viewIsAttached= */ true);
mController.onKeyguardTransitionChanged(
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 ad7dee3..83ba684 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
@@ -51,6 +51,8 @@
import android.graphics.Insets;
import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
@@ -81,6 +83,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -191,7 +194,7 @@
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setResetUserExpandedStatesRunnable(()->{});
+ mStackScroller.setResetUserExpandedStatesRunnable(() -> {});
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
@@ -309,7 +312,9 @@
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ true,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -319,7 +324,9 @@
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ false,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
}
@@ -328,10 +335,14 @@
public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ false,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ true,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -385,8 +396,8 @@
mStackScroller.setExpandedHeight(100f);
}
-
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void manageNotifications_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -399,6 +410,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void clearAll_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -411,6 +423,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testInflateFooterView() {
mStackScroller.inflateFooterView();
ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
@@ -444,7 +457,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
}
@Test
@@ -459,7 +472,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, false, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
}
@Test
@@ -474,7 +487,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true);
}
@Test
@@ -490,7 +503,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, true, false);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false);
}
@Test
@@ -505,7 +518,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
}
@Test
@@ -521,7 +534,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, false, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true);
}
@Test
@@ -529,7 +542,8 @@
mStackScroller.setCurrentUserSetup(true);
// add footer
- mStackScroller.inflateFooterView();
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
// add notification
ExpandableNotificationRow row = createClearableRow();
@@ -545,6 +559,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testReInflatesFooterViews() {
when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
clearInvocations(mStackScroller);
@@ -554,6 +569,16 @@
}
@Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testReInflatesEmptyShadeView() {
+ when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
+ clearInvocations(mStackScroller);
+ mStackScroller.reinflateViews();
+ verify(mStackScroller, never()).setFooterView(any());
+ verify(mStackScroller).setEmptyShadeView(any());
+ }
+
+ @Test
public void testSetIsBeingDraggedResetsExposedMenu() {
mStackScroller.setIsBeingDragged(true);
verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
@@ -601,6 +626,8 @@
@Test
public void testClearNotifications_clearAllInProgress() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
ExpandableNotificationRow row = createClearableRow();
when(row.getEntry().hasFinishedInitialization()).thenReturn(true);
doReturn(true).when(mStackScroller).isVisible(row);
@@ -645,6 +672,8 @@
@Test
public void testAddNotificationUpdatesSpeedBumpIndex() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -661,6 +690,8 @@
@Test
public void testAddAmbientNotificationNoSpeedBumpUpdate() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -677,6 +708,8 @@
@Test
public void testRemoveNotificationUpdatesSpeedBump() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -872,6 +905,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void hasFilteredOutSeenNotifs_updateFooter() {
mStackScroller.setCurrentUserSetup(true);
@@ -887,6 +921,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
mStackScroller.setHasFilteredOutSeenNotifications(true);
mStackScroller.updateEmptyShadeView(true, false);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 3024dd2..8910b6e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -82,6 +82,7 @@
public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0;
public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1;
public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2;
+ public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE;
// How long the current sensor reading is assumed to be valid beyond the current time.
// This provides a bit of prediction, as well as ensures that the weight for the last sample is
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e38d08f..bc3f9dd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4573,8 +4573,10 @@
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
final DisplayPowerControllerInterface displayPowerController =
mDisplayPowerControllers.get(id);
- ready &= displayPowerController.requestPowerState(request,
- waitForNegativeProximity);
+ if (displayPowerController != null) {
+ ready &= displayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 4e341a9..a43f93a 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_MAX;
+
import android.annotation.Nullable;
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManager;
@@ -65,6 +67,22 @@
return true;
}
+ @Override
+ public float[] getAutoBrightnessLevels(int mode) {
+ if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+ throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+ }
+ return mDisplayPowerController.getAutoBrightnessLevels(mode);
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(int mode) {
+ if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+ throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+ }
+ return mDisplayPowerController.getAutoBrightnessLuxLevels(mode);
+ }
+
/**
* Start the offload session. The method returns if the session is already active.
* @return Whether the session was started successfully
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 06e5f99..734381b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2214,6 +2214,20 @@
}
@Override
+ public float[] getAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ // The old DPC is no longer supported
+ return null;
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ // The old DPC is no longer supported
+ return null;
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 519224a..7df6114 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1886,6 +1886,24 @@
}
@Override
+ public float[] getAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+ return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+ return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index c279184..13acb3f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -237,4 +237,21 @@
* Indicate that boot has been completed and the screen is ready to update.
*/
void onBootCompleted();
+
+ /**
+ * Get the brightness levels used to determine automatic brightness based on lux levels.
+ * @param mode The auto-brightness mode
+ * @return The brightness levels for the specified mode. The values are between
+ * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+ */
+ float[] getAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
+
+ /**
+ * Get the lux levels used to determine automatic brightness.
+ * @param mode The auto-brightness mode
+ * @return The lux levels for the specified mode
+ */
+ float[] getAutoBrightnessLuxLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 38f0df4..9088cb9 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2700,11 +2700,8 @@
// session info from them.
sessionInfo = mSystemProvider.getDefaultSessionInfo();
}
- // TODO: b/279555229 - replace with matchingRequest.mRouterRecord.notifySessionCreated.
- notifySessionCreatedToRouter(
- matchingRequest.mRouterRecord,
- toOriginalRequestId(uniqueRequestId),
- sessionInfo);
+ matchingRequest.mRouterRecord.notifySessionCreated(
+ toOriginalRequestId(uniqueRequestId), sessionInfo);
}
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -2812,11 +2809,6 @@
return true;
}
- private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
- int requestId, @NonNull RoutingSessionInfo sessionInfo) {
- routerRecord.notifySessionCreated(requestId, sessionInfo);
- }
-
private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
int requestId) {
try {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b96b704..c920ca8 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -829,6 +829,9 @@
int returnCodeOfChild;
for (int childId : childUserIds) {
if (childId == userId) continue;
+ if (mUserManagerInternal.getProfileParentId(childId) != userId) {
+ continue;
+ }
// If package is not present in child then don't attempt to delete.
if (!packageState.getUserStateOrDefault(childId).isInstalled()) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index edae273..b286b12 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -64,6 +64,7 @@
import android.os.Message;
import android.os.PatternMatcher;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.SELinux;
import android.os.SystemClock;
import android.os.Trace;
@@ -3189,6 +3190,9 @@
pkg.isScannedAsStoppedSystemApp());
if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
+
+ serializer.attributeBoolean(null, "isSdkLibrary",
+ pkg.getAndroidPackage() != null && pkg.getAndroidPackage().isSdkLibrary());
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
@@ -4039,10 +4043,12 @@
int targetSdkVersion = 0;
byte[] restrictUpdateHash = null;
boolean isScannedAsStoppedSystemApp = false;
+ boolean isSdkLibrary = false;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
appId = parseAppId(parser);
+ isSdkLibrary = parser.getAttributeBoolean(null, "isSdkLibrary", false);
sharedUserAppId = parseSharedUserAppId(parser);
codePathStr = parser.getAttributeValue(null, "codePath");
@@ -4157,7 +4163,8 @@
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
- } else if (appId > 0) {
+ } else if (appId > 0 || (appId == Process.INVALID_UID && isSdkLibrary
+ && Flags.disallowSdkLibsToBeApps())) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
appId, pkgFlags, pkgPrivateFlags, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 59e95e7..1185a4e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2734,11 +2734,7 @@
// AdServicesManagerService (PP API service)
t.traceBegin("StartAdServicesManagerService");
- SystemService adServices = mSystemServiceManager
- .startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
- if (adServices instanceof Dumpable) {
- mDumper.addDumpable((Dumpable) adServices);
- }
+ mSystemServiceManager.startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
t.traceEnd();
// OnDevicePersonalizationSystemService
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index 25d208d..5cbb1aa 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -77,7 +77,9 @@
.autoFix()
.build()
- return LintFix.create().composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
+ return LintFix.create()
+ .name(annotateFix.getDisplayName())
+ .composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
}
private val annotation: String