Merge "Don't use trimMemory(BACKGROUND) for lockscreen trimming." into udc-dev
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index a9f720a..515ddc8 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -80,7 +80,7 @@
private void prepareForNextRun() {
SystemClock.sleep(COOL_OFF_PERIOD_MS);
- ShellHelper.runShellCommand("am wait-for-broadcast-idle");
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
@@ -102,7 +102,7 @@
* to avoid unnecessary waiting.
*/
public void resumeTiming() {
- ShellHelper.runShellCommand("am wait-for-broadcast-idle");
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
resumeTimer();
}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 19a4766..6dba5b3 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -1541,7 +1541,8 @@
private void waitForBroadcastIdle() {
try {
- ShellHelper.runShellCommandWithTimeout("am wait-for-broadcast-idle", TIMEOUT_IN_SECOND);
+ ShellHelper.runShellCommandWithTimeout(
+ "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
} catch (TimeoutException e) {
Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index df9257c..5c1b3ee 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5006,12 +5006,6 @@
return mUserExtras;
}
- private Bundle getAllExtras() {
- final Bundle saveExtras = (Bundle) mUserExtras.clone();
- saveExtras.putAll(mN.extras);
- return saveExtras;
- }
-
/**
* Add an action to this notification. Actions are typically displayed by
* the system as a button adjacent to the notification content.
@@ -6617,9 +6611,16 @@
+ " vs bubble: " + mN.mBubbleMetadata.getShortcutId());
}
- // first, add any extras from the calling code
+ // Adds any new extras provided by the user.
if (mUserExtras != null) {
- mN.extras = getAllExtras();
+ final Bundle saveExtras = (Bundle) mUserExtras.clone();
+ if (SystemProperties.getBoolean(
+ "persist.sysui.notification.builder_extras_override", false)) {
+ mN.extras.putAll(saveExtras);
+ } else {
+ saveExtras.putAll(mN.extras);
+ mN.extras = saveExtras;
+ }
}
mN.creationTime = System.currentTimeMillis();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 776e34b..385fd50 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -24,9 +24,11 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.compat.annotation.LoggingOnly;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -49,6 +51,7 @@
import android.view.KeyEvent;
import android.view.View;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.statusbar.AppClipsServiceConnector;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBarService;
@@ -170,6 +173,8 @@
public @interface Disable2Flags {}
// LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt)
+ private static final String TAG = "StatusBarManager";
+
/**
* Default disable flags for setup
*
@@ -572,13 +577,13 @@
private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L;
/**
- * Media controls based on {@link android.app.Notification.MediaStyle} notifications will be
- * required to include a non-empty title, either in the {@link android.media.MediaMetadata} or
+ * Media controls based on {@link android.app.Notification.MediaStyle} notifications should
+ * include a non-empty title, either in the {@link android.media.MediaMetadata} or
* notification title.
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private static final long MEDIA_CONTROL_REQUIRES_TITLE = 274775190L;
+ @LoggingOnly
+ private static final long MEDIA_CONTROL_BLANK_TITLE = 274775190L;
@UnsupportedAppUsage
private Context mContext;
@@ -586,6 +591,9 @@
@UnsupportedAppUsage
private IBinder mToken = new Binder();
+ private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
@UnsupportedAppUsage
StatusBarManager(Context context) {
mContext = context;
@@ -597,7 +605,7 @@
mService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (mService == null) {
- Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+ Slog.w(TAG, "warning: no STATUS_BAR_SERVICE");
}
}
return mService;
@@ -1226,18 +1234,22 @@
}
/**
- * Checks whether the given package must include a non-empty title for its media controls.
+ * Log that the given package has posted media controls with a blank title
*
* @param packageName App posting media controls
- * @param user Current user handle
- * @return true if the app is required to provide a non-empty title
+ * @param userId Current user ID
+ * @throws RuntimeException if there is an error reporting the change
*
* @hide
*/
- @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- android.Manifest.permission.LOG_COMPAT_CHANGE})
- public static boolean isMediaTitleRequiredForApp(String packageName, UserHandle user) {
- return CompatChanges.isChangeEnabled(MEDIA_CONTROL_REQUIRES_TITLE, packageName, user);
+ public void logBlankMediaTitle(String packageName, @UserIdInt int userId)
+ throws RuntimeException {
+ try {
+ mPlatformCompat.reportChangeByPackageName(MEDIA_CONTROL_BLANK_TITLE, packageName,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 6910501..78388ef 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -246,8 +246,7 @@
@Override
public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
- if (!mImeDrawsImeNavBar || mNavigationBarFrame == null
- || mService.isExtractViewShown()) {
+ if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) {
return;
}
@@ -255,53 +254,58 @@
if (systemInsets != null) {
final Window window = mService.mWindow.getWindow();
final View decor = window.getDecorView();
- Region touchableRegion = null;
- final View inputFrame = mService.mInputFrame;
- switch (originalInsets.touchableInsets) {
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
- if (inputFrame.getVisibility() == View.VISIBLE) {
- inputFrame.getLocationInWindow(mTempPos);
- mTempRect.set(mTempPos[0], mTempPos[1],
- mTempPos[0] + inputFrame.getWidth(),
- mTempPos[1] + inputFrame.getHeight());
- touchableRegion = new Region(mTempRect);
- }
- break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
- if (inputFrame.getVisibility() == View.VISIBLE) {
- inputFrame.getLocationInWindow(mTempPos);
- mTempRect.set(mTempPos[0], originalInsets.contentTopInsets,
- mTempPos[0] + inputFrame.getWidth() ,
- mTempPos[1] + inputFrame.getHeight());
- touchableRegion = new Region(mTempRect);
- }
- break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
- if (inputFrame.getVisibility() == View.VISIBLE) {
- inputFrame.getLocationInWindow(mTempPos);
- mTempRect.set(mTempPos[0], originalInsets.visibleTopInsets,
- mTempPos[0] + inputFrame.getWidth(),
- mTempPos[1] + inputFrame.getHeight());
- touchableRegion = new Region(mTempRect);
- }
- break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION:
- touchableRegion = new Region();
- touchableRegion.set(originalInsets.touchableRegion);
- break;
- }
- // Hereafter "mTempRect" means a navigation bar rect.
- mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom,
- decor.getRight(), decor.getBottom());
- if (touchableRegion == null) {
- touchableRegion = new Region(mTempRect);
- } else {
- touchableRegion.union(mTempRect);
- }
- dest.touchableRegion.set(touchableRegion);
- dest.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ // If the extract view is shown, everything is touchable, so no need to update
+ // touchable insets, but we still update normal insets below.
+ if (!mService.isExtractViewShown()) {
+ Region touchableRegion = null;
+ final View inputFrame = mService.mInputFrame;
+ switch (originalInsets.touchableInsets) {
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ inputFrame.getLocationInWindow(mTempPos);
+ mTempRect.set(mTempPos[0], mTempPos[1],
+ mTempPos[0] + inputFrame.getWidth(),
+ mTempPos[1] + inputFrame.getHeight());
+ touchableRegion = new Region(mTempRect);
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ inputFrame.getLocationInWindow(mTempPos);
+ mTempRect.set(mTempPos[0], originalInsets.contentTopInsets,
+ mTempPos[0] + inputFrame.getWidth(),
+ mTempPos[1] + inputFrame.getHeight());
+ touchableRegion = new Region(mTempRect);
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ inputFrame.getLocationInWindow(mTempPos);
+ mTempRect.set(mTempPos[0], originalInsets.visibleTopInsets,
+ mTempPos[0] + inputFrame.getWidth(),
+ mTempPos[1] + inputFrame.getHeight());
+ touchableRegion = new Region(mTempRect);
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION:
+ touchableRegion = new Region();
+ touchableRegion.set(originalInsets.touchableRegion);
+ break;
+ }
+ // Hereafter "mTempRect" means a navigation bar rect.
+ mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom,
+ decor.getRight(), decor.getBottom());
+ if (touchableRegion == null) {
+ touchableRegion = new Region(mTempRect);
+ } else {
+ touchableRegion.union(mTempRect);
+ }
+
+ dest.touchableRegion.set(touchableRegion);
+ dest.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ }
// TODO(b/215443343): See if we can use View#OnLayoutChangeListener().
// TODO(b/215443343): See if we can replace DecorView#mNavigationColorViewState.view
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 6f2a915..3f40139 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -37,7 +37,6 @@
import android.os.HandlerThread;
import android.os.Message;
import android.preference.VolumePreference.VolumeStore;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.System;
@@ -47,7 +46,6 @@
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.os.SomeArgs;
import java.util.concurrent.TimeUnit;
@@ -295,14 +293,8 @@
if (zenMuted) {
mSeekBar.setProgress(mLastAudibleStreamVolume, true);
} else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
- /**
- * the first variable above is preserved and the conditions below are made explicit
- * so that when user attempts to slide the notification seekbar out of vibrate the
- * seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
- */
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
- || mStreamType == AudioManager.STREAM_RING
+ // For ringer-mode affected streams, show volume as zero when ringermode is vibrate
+ if (mStreamType == AudioManager.STREAM_RING
|| (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
mSeekBar.setProgress(0, true);
}
@@ -397,9 +389,7 @@
// set the time of stop volume
if ((mStreamType == AudioManager.STREAM_VOICE_CALL
|| mStreamType == AudioManager.STREAM_RING
- || (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
- && mStreamType == AudioManager.STREAM_NOTIFICATION)
+ || mStreamType == AudioManager.STREAM_NOTIFICATION
|| mStreamType == AudioManager.STREAM_ALARM)) {
sStopVolumeTime = java.lang.System.currentTimeMillis();
}
@@ -686,10 +676,7 @@
}
private void updateVolumeSlider(int streamType, int streamValue) {
- final boolean streamMatch = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
- && mNotificationOrRing ? isNotificationOrRing(streamType) :
- streamType == mStreamType;
+ final boolean streamMatch = (streamType == mStreamType);
if (mSeekBar != null && streamMatch && streamValue != -1) {
final boolean muted = mAudioManager.isStreamMute(mStreamType)
|| streamValue == 0;
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 095189a..67ac811 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -62,7 +62,6 @@
private static class DefaultReporter implements Reporter {
public void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls,
int limit, PrintWriter pw) {
- final int size = Math.min(surfaceControls.size(), limit);
final long now = SystemClock.elapsedRealtime();
final ArrayList<Map.Entry<SurfaceControl, Long>> entries = new ArrayList<>();
for (Map.Entry<SurfaceControl, Long> entry : surfaceControls.entrySet()) {
@@ -71,6 +70,7 @@
// Sort entries by time registered when dumping
// TODO: Or should it sort by name?
entries.sort((o1, o2) -> (int) (o1.getValue() - o2.getValue()));
+ final int size = Math.min(entries.size(), limit);
pw.println("SurfaceControlRegistry");
pw.println("----------------------");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 441636d..f1cde3b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7734,13 +7734,14 @@
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
- final ListenerInfo li = mListenerInfo;
+ final OnLongClickListener listener =
+ mListenerInfo == null ? null : mListenerInfo.mOnLongClickListener;
boolean shouldPerformHapticFeedback = true;
- if (li != null && li.mOnLongClickListener != null) {
- handled = li.mOnLongClickListener.onLongClick(View.this);
+ if (listener != null) {
+ handled = listener.onLongClick(View.this);
if (handled) {
- shouldPerformHapticFeedback =
- li.mOnLongClickListener.onLongClickUseDefaultHapticFeedback(View.this);
+ shouldPerformHapticFeedback = listener.onLongClickUseDefaultHapticFeedback(
+ View.this);
}
}
if (!handled) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 34e6e49..5525336 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2598,6 +2598,11 @@
public int getActionTag() {
return VIEW_GROUP_ACTION_ADD_TAG;
}
+
+ @Override
+ public final void visitUris(@NonNull Consumer<Uri> visitor) {
+ mNestedViews.visitUris(visitor);
+ }
}
/**
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 7ad2a68..8135f9c 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -549,11 +549,6 @@
"task_manager_inform_job_scheduler_of_pending_app_stop";
/**
- * (boolean) Whether to show notification volume control slider separate from ring.
- */
- public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification";
-
- /**
* (boolean) Whether widget provider info would be saved to / loaded from system persistence
* layer as opposed to individual manifests in respective apps.
*/
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index f277635..116c301c 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -367,28 +367,42 @@
* using a single static object.
*/
@VisibleForTesting
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public void startListeningForLatencyTrackerConfigChanges() {
final Context context = ActivityThread.currentApplication();
- if (context != null
- && context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) == PERMISSION_GRANTED) {
- // Post initialization to the background in case we're running on the main thread.
- BackgroundThread.getHandler().post(() -> this.updateProperties(
- DeviceConfig.getProperties(NAMESPACE_LATENCY_TRACKER)));
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_LATENCY_TRACKER,
- BackgroundThread.getExecutor(), mOnPropertiesChangedListener);
- } else {
+ if (context == null) {
if (DEBUG) {
- if (context == null) {
- Log.d(TAG, "No application for " + ActivityThread.currentActivityThread());
- } else {
- synchronized (mLock) {
- Log.d(TAG, "Initialized the LatencyTracker."
- + " (No READ_DEVICE_CONFIG permission to change configs)"
- + " enabled=" + mEnabled + ", package=" + context.getPackageName());
- }
+ Log.d(TAG, "No application for package: " + ActivityThread.currentPackageName());
+ }
+ return;
+ }
+ if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
+ if (DEBUG) {
+ synchronized (mLock) {
+ Log.d(TAG, "Initialized the LatencyTracker."
+ + " (No READ_DEVICE_CONFIG permission to change configs)"
+ + " enabled=" + mEnabled + ", package=" + context.getPackageName());
}
}
+ return;
}
+
+ // Post initialization to the background in case we're running on the main thread.
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ this.updateProperties(
+ DeviceConfig.getProperties(NAMESPACE_LATENCY_TRACKER));
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_LATENCY_TRACKER,
+ BackgroundThread.getExecutor(), mOnPropertiesChangedListener);
+ } catch (SecurityException ex) {
+ // In case of running tests that the main thread passes the check,
+ // but the background thread doesn't have necessary permissions.
+ // Swallow it since it's ok to ignore device config changes in the tests.
+ Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
+ + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
+ + ", package=" + context.getPackageName());
+ }
+ });
}
/**
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index c5b00c9..eba7f58 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -33,6 +33,8 @@
import static android.app.Notification.EXTRA_PEOPLE_LIST;
import static android.app.Notification.EXTRA_PICTURE;
import static android.app.Notification.EXTRA_PICTURE_ICON;
+import static android.app.Notification.EXTRA_SUMMARY_TEXT;
+import static android.app.Notification.EXTRA_TITLE;
import static android.app.Notification.MessagingStyle.Message.KEY_DATA_URI;
import static android.app.Notification.MessagingStyle.Message.KEY_SENDER_PERSON;
import static android.app.Notification.MessagingStyle.Message.KEY_TEXT;
@@ -76,6 +78,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -111,6 +114,9 @@
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
+ // TODO(b/169435530): remove this flag set once resolved.
+ SystemProperties.set("persist.sysui.notification.builder_extras_override",
+ Boolean.toString(false));
}
@Test
@@ -1481,6 +1487,107 @@
Assert.assertEquals(actionWithFreeformRemoteInput, remoteInputActionPair.second);
}
+ // Ensures that extras in a Notification Builder can be updated.
+ @Test
+ public void testExtras_cachedExtrasOverwrittenByUserProvided() {
+ // Sets the flag to new state.
+ // TODO(b/169435530): remove this set value once resolved.
+ SystemProperties.set("persist.sysui.notification.builder_extras_override",
+ Boolean.toString(true));
+ Bundle extras = new Bundle();
+ extras.putCharSequence(EXTRA_TITLE, "test title");
+ extras.putCharSequence(EXTRA_SUMMARY_TEXT, "summary text");
+
+ Notification.Builder builder = new Notification.Builder(mContext, "test id")
+ .addExtras(extras);
+
+ Notification notification = builder.build();
+ assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
+ "test title");
+ assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
+ "summary text");
+
+ extras.putCharSequence(EXTRA_TITLE, "new title");
+ builder.addExtras(extras);
+ notification = builder.build();
+ assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
+ "new title");
+ assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
+ "summary text");
+ }
+
+ // Ensures that extras in a Notification Builder can be updated by an extender.
+ @Test
+ public void testExtras_cachedExtrasOverwrittenByExtender() {
+ // Sets the flag to new state.
+ // TODO(b/169435530): remove this set value once resolved.
+ SystemProperties.set("persist.sysui.notification.builder_extras_override",
+ Boolean.toString(true));
+ Notification.CarExtender extender = new Notification.CarExtender().setColor(1234);
+
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .extend(extender).build();
+
+ extender.setColor(5678);
+
+ Notification.Builder.recoverBuilder(mContext, notification).extend(extender).build();
+
+ Notification.CarExtender recoveredExtender = new Notification.CarExtender(notification);
+ assertThat(recoveredExtender.getColor()).isEqualTo(5678);
+ }
+
+ // Validates pre-flag flip behavior, that extras in a Notification Builder cannot be updated.
+ // TODO(b/169435530): remove this test once resolved.
+ @Test
+ public void testExtras_cachedExtrasOverwrittenByUserProvidedOld() {
+ // Sets the flag to old state.
+ SystemProperties.set("persist.sysui.notification.builder_extras_override",
+ Boolean.toString(false));
+
+ Bundle extras = new Bundle();
+ extras.putCharSequence(EXTRA_TITLE, "test title");
+ extras.putCharSequence(EXTRA_SUMMARY_TEXT, "summary text");
+
+ Notification.Builder builder = new Notification.Builder(mContext, "test id")
+ .addExtras(extras);
+
+ Notification notification = builder.build();
+ assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
+ "test title");
+ assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
+ "summary text");
+
+ extras.putCharSequence(EXTRA_TITLE, "new title");
+ builder.addExtras(extras);
+ notification = builder.build();
+ assertThat(notification.extras.getCharSequence(EXTRA_TITLE).toString()).isEqualTo(
+ "test title");
+ assertThat(notification.extras.getCharSequence(EXTRA_SUMMARY_TEXT).toString()).isEqualTo(
+ "summary text");
+ }
+
+ // Validates pre-flag flip behavior, that extras in a Notification Builder cannot be updated
+ // by an extender.
+ // TODO(b/169435530): remove this test once resolved.
+ @Test
+ public void testExtras_cachedExtrasOverwrittenByExtenderOld() {
+ // Sets the flag to old state.
+ SystemProperties.set("persist.sysui.notification.builder_extras_override",
+ Boolean.toString(false));
+
+ Notification.CarExtender extender = new Notification.CarExtender().setColor(1234);
+
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .extend(extender).build();
+
+ extender.setColor(5678);
+
+ Notification.Builder.recoverBuilder(mContext, notification).extend(extender).build();
+
+ Notification.CarExtender recoveredExtender = new Notification.CarExtender(notification);
+ assertThat(recoveredExtender.getColor()).isEqualTo(1234);
+ }
+
private void assertValid(Notification.Colors c) {
// Assert that all colors are populated
assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 4672226..7879801 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -716,6 +716,30 @@
}
@Test
+ public void visitUris_nestedViews() {
+ final RemoteViews outer = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ final RemoteViews inner = new RemoteViews(mPackage, 33);
+ final Uri imageUriI = Uri.parse("content://inner/image");
+ final Icon icon1 = Icon.createWithContentUri("content://inner/icon1");
+ final Icon icon2 = Icon.createWithContentUri("content://inner/icon2");
+ final Icon icon3 = Icon.createWithContentUri("content://inner/icon3");
+ final Icon icon4 = Icon.createWithContentUri("content://inner/icon4");
+ inner.setImageViewUri(R.id.image, imageUriI);
+ inner.setTextViewCompoundDrawables(R.id.text, icon1, icon2, icon3, icon4);
+
+ outer.addView(R.id.layout, inner);
+
+ Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+ outer.visitUris(visitor);
+ verify(visitor, times(1)).accept(eq(imageUriI));
+ verify(visitor, times(1)).accept(eq(icon1.getUri()));
+ verify(visitor, times(1)).accept(eq(icon2.getUri()));
+ verify(visitor, times(1)).accept(eq(icon3.getUri()));
+ verify(visitor, times(1)).accept(eq(icon4.getUri()));
+ }
+
+ @Test
public void visitUris_separateOrientation() {
final RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test);
final Uri imageUriL = Uri.parse("content://landscape/image");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
index ac6e4c2..53683c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java
@@ -54,14 +54,6 @@
DevicePostureController.OnDevicePostureChangedListener,
DisplayController.OnDisplaysChangedListener {
/**
- * When {@code true}, floating windows like PiP would auto move to the position
- * specified by {@link #PREFER_TOP_HALF_IN_TABLETOP} when in tabletop mode.
- */
- private static final boolean ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
- SystemProperties.getBoolean(
- "persist.wm.debug.enable_move_floating_window_in_tabletop", true);
-
- /**
* Prefer the {@link #PREFERRED_TABLETOP_HALF_TOP} if this flag is enabled,
* {@link #PREFERRED_TABLETOP_HALF_BOTTOM} otherwise.
* See also {@link #getPreferredHalfInTabletopMode()}.
@@ -162,14 +154,6 @@
}
}
- /**
- * @return {@code true} if floating windows like PiP would auto move to the position
- * specified by {@link #getPreferredHalfInTabletopMode()} when in tabletop mode.
- */
- public boolean enableMoveFloatingWindowInTabletop() {
- return ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP;
- }
-
/** @return Preferred half for floating windows like PiP when in tabletop mode. */
@PreferredTabletopHalf
public int getPreferredHalfInTabletopMode() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 753dfa7..a9ccdf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -293,6 +293,9 @@
}
if (mResizingIconView == null) {
+ if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
+ animFinishedCallback.accept(false);
+ }
return;
}
@@ -311,6 +314,9 @@
releaseDecor(finishT);
finishT.apply();
finishT.close();
+ if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
+ animFinishedCallback.accept(true);
+ }
}
});
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 65a12d6..2590cab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -252,13 +252,16 @@
// It can be removed when min_sdk of the app is set to 31 or greater.
@SuppressLint("NewApi")
private List<RemoteAction> getMediaActions() {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ // Cache the PlaybackState since it's a Binder call.
+ final PlaybackState playbackState;
+ if (mMediaController == null
+ || (playbackState = mMediaController.getPlaybackState()) == null) {
return Collections.emptyList();
}
ArrayList<RemoteAction> mediaActions = new ArrayList<>();
- boolean isPlaying = mMediaController.getPlaybackState().isActive();
- long actions = mMediaController.getPlaybackState().getActions();
+ boolean isPlaying = playbackState.isActive();
+ long actions = playbackState.getActions();
// Prev action
mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index bfc1fb9..3c7ce3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -249,11 +249,6 @@
finishTransaction);
}
- // Fade in the fadeout PIP when the fixed rotation is finished.
- if (mPipTransitionState.isInPip() && !mInFixedRotation && mHasFadeOut) {
- fadeExistingPip(true /* show */);
- }
-
return false;
}
@@ -1056,6 +1051,12 @@
.crop(finishTransaction, leash, destBounds)
.round(finishTransaction, leash, isInPip)
.shadow(finishTransaction, leash, isInPip);
+ // Make sure the PiP keeps invisible if it was faded out. If it needs to fade in, that will
+ // be handled by onFixedRotationFinished().
+ if (isInPip && mHasFadeOut) {
+ startTransaction.setAlpha(leash, 0f);
+ finishTransaction.setAlpha(leash, 0f);
+ }
}
/** Hides and shows the existing PIP during fixed rotation transition of other activities. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 63181da..6a861ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -677,7 +677,6 @@
});
mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
- if (!mTabletopModeController.enableMoveFloatingWindowInTabletop()) return;
final String tag = "tabletop-mode";
if (!isInTabletopMode) {
mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 5c9709c..f35eda6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -371,7 +371,8 @@
* Find the background task that match the given component.
*/
@Nullable
- public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName) {
+ public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName,
+ int userId) {
if (componentName == null) {
return null;
}
@@ -383,7 +384,7 @@
if (task.isVisible) {
continue;
}
- if (componentName.equals(task.baseIntent.getComponent())) {
+ if (componentName.equals(task.baseIntent.getComponent()) && userId == task.userId) {
return task;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 34701f1..ea33a1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -723,7 +723,7 @@
// in the background with priority.
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
.map(recentTasks -> recentTasks.findTaskInBackground(
- intent.getIntent().getComponent()))
+ intent.getIntent().getComponent(), userId1))
.orElse(null);
if (taskInfo != null) {
startTask(taskInfo.taskId, position, options);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 14ea86a..d2b0e28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -87,6 +87,14 @@
mStageCoordinator = stageCoordinator;
}
+ private void initTransition(@NonNull IBinder transition,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ mAnimatingTransition = transition;
+ mFinishTransaction = finishTransaction;
+ mFinishCallback = finishCallback;
+ }
+
/** Play animation for enter transition or dismiss transition. */
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -94,9 +102,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull WindowContainerToken topRoot) {
- mFinishCallback = finishCallback;
- mAnimatingTransition = transition;
- mFinishTransaction = finishTransaction;
+ initTransition(transition, finishTransaction, finishCallback);
final TransitSession pendingTransition = getPendingTransition(transition);
if (pendingTransition != null) {
@@ -220,6 +226,45 @@
onFinish(null /* wct */, null /* wctCB */);
}
+ /** Play animation for drag divider dismiss transition. */
+ void playDragDismissAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
+ @NonNull WindowContainerToken topRoot) {
+ initTransition(transition, finishTransaction, finishCallback);
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final SurfaceControl leash = change.getLeash();
+
+ if (toTopRoot.equals(change.getContainer())) {
+ startTransaction.setAlpha(leash, 1.f);
+ startTransaction.show(leash);
+
+ ValueAnimator va = new ValueAnimator();
+ mAnimations.add(va);
+
+ toTopDecor.onResized(startTransaction, animated -> {
+ mAnimations.remove(va);
+ if (animated) {
+ mTransitions.getMainExecutor().execute(() -> {
+ onFinish(null /* wct */, null /* wctCB */);
+ });
+ }
+ });
+ } else if (topRoot.equals(change.getContainer())) {
+ // Ensure it on top of all changes in transition.
+ startTransaction.setLayer(leash, Integer.MAX_VALUE);
+ startTransaction.setAlpha(leash, 1.f);
+ startTransaction.show(leash);
+ }
+ }
+ startTransaction.apply();
+ onFinish(null /* wct */, null /* wctCB */);
+ }
+
/** Play animation for resize transition. */
void playResizeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -227,9 +272,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
- mFinishCallback = finishCallback;
- mAnimatingTransition = transition;
- mFinishTransaction = finishTransaction;
+ initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index bf20567..e0fffff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1328,8 +1328,6 @@
mIsExiting = true;
childrenToTop.resetBounds(wct);
wct.reorder(childrenToTop.mRootTaskInfo.token, true);
- wct.setSmallestScreenWidthDp(childrenToTop.mRootTaskInfo.token,
- SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
}
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* reparentLeafTaskIfRelaunch */);
@@ -1517,6 +1515,10 @@
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
mSplitLayout.update(t);
+ mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
+ getMainStageBounds());
+ mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
+ getSideStageBounds());
setDividerVisibility(true, t);
// Ensure divider surface are re-parented back into the hierarchy at the end of the
// transition. See Transition#buildFinishTransaction for more detail.
@@ -1989,13 +1991,15 @@
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
+ final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
if (!ENABLE_SHELL_TRANSITIONS) {
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, reason);
+ exitSplitScreen(toTopStage, reason);
return;
}
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ toTopStage.resetBounds(wct);
prepareExitSplitScreen(dismissTop, wct);
if (mRootTaskInfo != null) {
wct.setDoNotPip(mRootTaskInfo.token);
@@ -2531,8 +2535,17 @@
shouldAnimate = startPendingEnterAnimation(
mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
+ final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
shouldAnimate = startPendingDismissAnimation(
- mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
+ dismiss, info, startTransaction, finishTransaction);
+ if (shouldAnimate && dismiss.mReason == EXIT_REASON_DRAG_DIVIDER) {
+ final StageTaskListener toTopStage =
+ dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage;
+ mSplitTransitions.playDragDismissAnimation(transition, info, startTransaction,
+ finishTransaction, finishCallback, toTopStage.mRootTaskInfo.token,
+ toTopStage.getSplitDecorManager(), mRootTaskInfo.token);
+ return true;
+ }
} else if (mSplitTransitions.isPendingResize(transition)) {
mSplitTransitions.playResizeAnimation(transition, info, startTransaction,
finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
@@ -2787,6 +2800,10 @@
mSplitTransitions.mPendingDismiss = null;
return false;
}
+ dismissTransition.setFinishedCallback((callbackWct, callbackT) -> {
+ mMainStage.getSplitDecorManager().release(callbackT);
+ mSideStage.getSplitDecorManager().release(callbackT);
+ });
addDividerBarToTransition(info, false /* show */);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index e2e9270..da7d186 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
@@ -201,7 +202,7 @@
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (mRootTaskInfo.taskId == taskInfo.taskId) {
// Inflates split decor view only when the root task is visible.
- if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
+ if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
if (taskInfo.isVisible) {
mSplitDecorManager.inflate(mContext, mRootLeash,
taskInfo.configuration.windowConfiguration.getBounds());
@@ -385,6 +386,7 @@
void resetBounds(WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, null);
wct.setAppBounds(mRootTaskInfo.token, null);
+ wct.setSmallestScreenWidthDp(mRootTaskInfo.token, SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
}
void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d16b497..3b306e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -191,6 +191,12 @@
*/
private static final int SYNC_ALLOWANCE_MS = 120;
+ /**
+ * Keyguard gets a more generous timeout to finish its animations, because we are always holding
+ * a sleep token during occlude/unocclude transitions and we want them to finish playing cleanly
+ */
+ private static final int SYNC_ALLOWANCE_KEYGUARD_MS = 2000;
+
/** For testing only. Disables the force-finish timeout on sync. */
private boolean mDisableForceSync = false;
@@ -673,7 +679,7 @@
// Sleep starts a process of forcing all prior transitions to finish immediately
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Start finish-for-sync track %d", i);
- finishForSync(i, null /* forceFinish */);
+ finishForSync(active, i, null /* forceFinish */);
}
if (hadPreceding) {
return false;
@@ -1021,6 +1027,9 @@
for (int i = 0; i < mPendingTransitions.size(); ++i) {
if (mPendingTransitions.get(i).mToken == token) return true;
}
+ for (int i = 0; i < mReadyDuringSync.size(); ++i) {
+ if (mReadyDuringSync.get(i).mToken == token) return true;
+ }
for (int t = 0; t < mTracks.size(); ++t) {
final Track tr = mTracks.get(t);
for (int i = 0; i < tr.mReadyTransitions.size(); ++i) {
@@ -1107,10 +1116,17 @@
*
* This is then repeated until there are no more pending sleep transitions.
*
+ * @param reason The SLEEP transition that triggered this round of finishes. We will continue
+ * looping round finishing transitions as long as this is still waiting.
* @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
* signal to -- so it will be force-finished if it's still running.
*/
- private void finishForSync(int trackIdx, @Nullable ActiveTransition forceFinish) {
+ private void finishForSync(ActiveTransition reason,
+ int trackIdx, @Nullable ActiveTransition forceFinish) {
+ if (!isTransitionKnown(reason.mToken)) {
+ Log.d(TAG, "finishForSleep: already played sync transition " + reason);
+ return;
+ }
final Track track = mTracks.get(trackIdx);
if (forceFinish != null) {
final Track trk = mTracks.get(forceFinish.getTrack());
@@ -1154,8 +1170,11 @@
if (track.mActiveTransition == playing) {
if (!mDisableForceSync) {
// Give it a short amount of time to process it before forcing.
- mMainExecutor.executeDelayed(() -> finishForSync(trackIdx, playing),
- SYNC_ALLOWANCE_MS);
+ final int tolerance = KeyguardTransitionHandler.handles(playing.mInfo)
+ ? SYNC_ALLOWANCE_KEYGUARD_MS
+ : SYNC_ALLOWANCE_MS;
+ mMainExecutor.executeDelayed(
+ () -> finishForSync(reason, trackIdx, playing), tolerance);
}
break;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 9189d3d..fb17d87 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -223,7 +223,7 @@
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
// Put the same component into a task in the background
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
- doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
+ doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null);
@@ -247,7 +247,7 @@
SPLIT_POSITION_BOTTOM_OR_RIGHT);
// Put the same component into a task in the background
doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
- .findTaskInBackground(any());
+ .findTaskInBackground(any(), anyInt());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index ae69b3d..4e446c6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -44,11 +44,15 @@
static SplitLayout createMockSplitLayout() {
final Rect dividerBounds = new Rect(48, 0, 52, 100);
+ final Rect bounds1 = new Rect(0, 0, 40, 100);
+ final Rect bounds2 = new Rect(60, 0, 100, 100);
final SurfaceControl leash = createMockSurface();
SplitLayout out = mock(SplitLayout.class);
doReturn(dividerBounds).when(out).getDividerBounds();
doReturn(dividerBounds).when(out).getRefDividerBounds();
doReturn(leash).when(out).getDividerLeash();
+ doReturn(bounds1).when(out).getBounds1();
+ doReturn(bounds2).when(out).getBounds2();
return out;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 8038453..60c0e55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -42,6 +42,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -72,6 +73,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.Transitions;
@@ -117,13 +119,13 @@
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider);
+ mIconProvider));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
- mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider);
+ mIconProvider));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
@@ -137,6 +139,8 @@
.setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();
mSideChild = new TestRunningTaskInfoBuilder()
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
+ doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
+ doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 6621ab8..66b6c62 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -68,6 +68,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.sysui.ShellController;
@@ -145,6 +146,8 @@
mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
+ doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
+ doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
}
@Test
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index c4f09ce..f911d35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -111,6 +111,9 @@
public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
public static final int COMPLICATION_TYPE_MEDIA_ENTRY = 8;
+ private static final int SCREENSAVER_HOME_CONTROLS_ENABLED_DEFAULT = 1;
+ private static final int LOCKSCREEN_SHOW_CONTROLS_DEFAULT = 0;
+
private final Context mContext;
private final IDreamManager mDreamManager;
private final DreamInfoComparator mComparator;
@@ -311,8 +314,14 @@
/** Gets whether home controls button is enabled on the dream */
private boolean getHomeControlsEnabled() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, 1) == 1;
+ return Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
+ LOCKSCREEN_SHOW_CONTROLS_DEFAULT) == 1
+ && Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
+ SCREENSAVER_HOME_CONTROLS_ENABLED_DEFAULT) == 1;
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 22ec12d..2edf403 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -28,6 +28,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.provider.Settings;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +85,7 @@
@Test
public void testComplicationsEnabledByDefault() {
+ setControlsEnabledOnLockscreen(true);
assertThat(mBackend.getComplicationsEnabled()).isTrue();
assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
SUPPORTED_DREAM_COMPLICATIONS_LIST);
@@ -91,6 +93,7 @@
@Test
public void testEnableComplicationExplicitly() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
SUPPORTED_DREAM_COMPLICATIONS_LIST);
@@ -99,6 +102,7 @@
@Test
public void testDisableComplications() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
assertThat(mBackend.getEnabledComplications())
.containsExactly(COMPLICATION_TYPE_HOME_CONTROLS);
@@ -107,6 +111,7 @@
@Test
public void testHomeControlsDisabled_ComplicationsEnabled() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
mBackend.setHomeControlsEnabled(false);
// Home controls should not be enabled, only date and time.
@@ -118,6 +123,7 @@
@Test
public void testHomeControlsDisabled_ComplicationsDisabled() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
mBackend.setHomeControlsEnabled(false);
assertThat(mBackend.getEnabledComplications()).isEmpty();
@@ -125,9 +131,9 @@
@Test
public void testHomeControlsEnabled_ComplicationsDisabled() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
mBackend.setHomeControlsEnabled(true);
- // Home controls should not be enabled, only date and time.
final List<Integer> enabledComplications =
Collections.singletonList(COMPLICATION_TYPE_HOME_CONTROLS);
assertThat(mBackend.getEnabledComplications())
@@ -136,9 +142,9 @@
@Test
public void testHomeControlsEnabled_ComplicationsEnabled() {
+ setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
mBackend.setHomeControlsEnabled(true);
- // Home controls should not be enabled, only date and time.
final List<Integer> enabledComplications =
Arrays.asList(
COMPLICATION_TYPE_HOME_CONTROLS,
@@ -148,4 +154,26 @@
assertThat(mBackend.getEnabledComplications())
.containsExactlyElementsIn(enabledComplications);
}
+
+ @Test
+ public void testHomeControlsEnabled_lockscreenDisabled() {
+ setControlsEnabledOnLockscreen(false);
+ mBackend.setComplicationsEnabled(true);
+ mBackend.setHomeControlsEnabled(true);
+ // Home controls should not be enabled, only date and time.
+ final List<Integer> enabledComplications =
+ Arrays.asList(
+ COMPLICATION_TYPE_DATE,
+ COMPLICATION_TYPE_TIME
+ );
+ assertThat(mBackend.getEnabledComplications())
+ .containsExactlyElementsIn(enabledComplications);
+ }
+
+ private void setControlsEnabledOnLockscreen(boolean enabled) {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
+ enabled ? 1 : 0);
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 8dd2c39..465b73e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -272,6 +272,7 @@
color = lockScreenColor,
animate = isAnimationEnabled,
duration = APPEAR_ANIM_DURATION,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
delay = 0,
onAnimationEnd = null
)
@@ -562,7 +563,7 @@
private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
private const val DOZE_ANIM_DURATION: Long = 300
- private const val APPEAR_ANIM_DURATION: Long = 350
+ private const val APPEAR_ANIM_DURATION: Long = 833
private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
private const val COLOR_ANIM_DURATION: Long = 400
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index cad2c16..4b79689 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -96,6 +96,7 @@
<!-- additional offset for clock switch area items -->
<dimen name="small_clock_height">114dp</dimen>
+ <dimen name="small_clock_padding_top">28dp</dimen>
<dimen name="clock_padding_start">28dp</dimen>
<dimen name="below_clock_padding_start">32dp</dimen>
<dimen name="below_clock_padding_end">16dp</dimen>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 1112bcd..9b1fa23 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -85,7 +85,7 @@
android:layout_height="@dimen/volume_ringer_drawer_icon_size"
android:layout_gravity="center"
android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_volume_ringer_mute" />
+ android:src="@drawable/ic_speaker_mute" />
</FrameLayout>
@@ -102,7 +102,7 @@
android:layout_height="@dimen/volume_ringer_drawer_icon_size"
android:layout_gravity="center"
android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_volume_ringer" />
+ android:src="@drawable/ic_speaker_on" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a62dead..8d3ba36 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -716,7 +716,7 @@
<!-- Minimum margin between clock and status bar -->
<dimen name="keyguard_clock_top_margin">18dp</dimen>
<!-- The amount to shift the clocks during a small/large transition -->
- <dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
+ <dimen name="keyguard_clock_switch_y_shift">14dp</dimen>
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- With the large clock, move up slightly from the center -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index e54d473..d9d64ad 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,5 +1,8 @@
package com.android.keyguard;
+import static android.view.View.ALPHA;
+import static android.view.View.TRANSLATION_Y;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -35,11 +38,12 @@
private static final String TAG = "KeyguardClockSwitch";
- private static final long CLOCK_OUT_MILLIS = 150;
- private static final long CLOCK_IN_MILLIS = 200;
- public static final long CLOCK_IN_START_DELAY_MILLIS = CLOCK_OUT_MILLIS / 2;
- private static final long STATUS_AREA_START_DELAY_MILLIS = 50;
- private static final long STATUS_AREA_MOVE_MILLIS = 350;
+ private static final long CLOCK_OUT_MILLIS = 133;
+ private static final long CLOCK_IN_MILLIS = 167;
+ public static final long CLOCK_IN_START_DELAY_MILLIS = 133;
+ private static final long STATUS_AREA_START_DELAY_MILLIS = 0;
+ private static final long STATUS_AREA_MOVE_UP_MILLIS = 967;
+ private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467;
@IntDef({LARGE, SMALL})
@Retention(RetentionPolicy.SOURCE)
@@ -66,6 +70,17 @@
top + targetHeight);
}
+ /** Returns a region for the small clock to position itself, based on the given parent. */
+ public static Rect getSmallClockRegion(ViewGroup parent) {
+ int targetHeight = parent.getResources()
+ .getDimensionPixelSize(R.dimen.small_clock_text_size);
+ return new Rect(
+ parent.getLeft(),
+ parent.getTop(),
+ parent.getRight(),
+ parent.getTop() + targetHeight);
+ }
+
/**
* Frame for small/large clocks
*/
@@ -90,7 +105,7 @@
@VisibleForTesting AnimatorSet mClockInAnim = null;
@VisibleForTesting AnimatorSet mClockOutAnim = null;
- private ObjectAnimator mStatusAreaAnim = null;
+ private AnimatorSet mStatusAreaAnim = null;
private int mClockSwitchYAmount;
@VisibleForTesting boolean mChildrenAreLaidOut = false;
@@ -172,13 +187,8 @@
void updateClockTargetRegions() {
if (mClock != null) {
if (mSmallClockFrame.isLaidOut()) {
- int targetHeight = getResources()
- .getDimensionPixelSize(R.dimen.small_clock_text_size);
- mClock.getSmallClock().getEvents().onTargetRegionChanged(new Rect(
- mSmallClockFrame.getLeft(),
- mSmallClockFrame.getTop(),
- mSmallClockFrame.getRight(),
- mSmallClockFrame.getTop() + targetHeight));
+ Rect targetRegion = getSmallClockRegion(mSmallClockFrame);
+ mClock.getSmallClock().getEvents().onTargetRegionChanged(targetRegion);
}
if (mLargeClockFrame.isLaidOut()) {
@@ -220,39 +230,44 @@
mStatusAreaAnim = null;
View in, out;
- int direction = 1;
- float statusAreaYTranslation;
+ float statusAreaYTranslation, clockInYTranslation, clockOutYTranslation;
if (useLargeClock) {
out = mSmallClockFrame;
in = mLargeClockFrame;
if (indexOfChild(in) == -1) addView(in, 0);
- direction = -1;
statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop()
+ mSmartspaceTopOffset;
+ clockInYTranslation = 0;
+ clockOutYTranslation = 0; // Small clock translation is handled with statusArea
} else {
in = mSmallClockFrame;
out = mLargeClockFrame;
statusAreaYTranslation = 0f;
+ clockInYTranslation = 0f;
+ clockOutYTranslation = mClockSwitchYAmount * -1f;
- // Must remove in order for notifications to appear in the proper place
+ // Must remove in order for notifications to appear in the proper place, ideally this
+ // would happen after the out animation runs, but we can't guarantee that the
+ // nofications won't enter only after the out animation runs.
removeView(out);
}
if (!animate) {
out.setAlpha(0f);
+ out.setTranslationY(clockOutYTranslation);
in.setAlpha(1f);
- in.setVisibility(VISIBLE);
+ in.setTranslationY(clockInYTranslation);
+ in.setVisibility(View.VISIBLE);
mStatusArea.setTranslationY(statusAreaYTranslation);
return;
}
mClockOutAnim = new AnimatorSet();
mClockOutAnim.setDuration(CLOCK_OUT_MILLIS);
- mClockOutAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ mClockOutAnim.setInterpolator(Interpolators.LINEAR);
mClockOutAnim.playTogether(
- ObjectAnimator.ofFloat(out, View.ALPHA, 0f),
- ObjectAnimator.ofFloat(out, View.TRANSLATION_Y, 0,
- direction * -mClockSwitchYAmount));
+ ObjectAnimator.ofFloat(out, ALPHA, 0f),
+ ObjectAnimator.ofFloat(out, TRANSLATION_Y, clockOutYTranslation));
mClockOutAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
mClockOutAnim = null;
@@ -264,8 +279,9 @@
mClockInAnim = new AnimatorSet();
mClockInAnim.setDuration(CLOCK_IN_MILLIS);
mClockInAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- mClockInAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f),
- ObjectAnimator.ofFloat(in, View.TRANSLATION_Y, direction * mClockSwitchYAmount, 0));
+ mClockInAnim.playTogether(
+ ObjectAnimator.ofFloat(in, ALPHA, 1f),
+ ObjectAnimator.ofFloat(in, TRANSLATION_Y, clockInYTranslation));
mClockInAnim.setStartDelay(CLOCK_IN_START_DELAY_MILLIS);
mClockInAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
@@ -273,19 +289,22 @@
}
});
- mClockInAnim.start();
- mClockOutAnim.start();
-
- mStatusAreaAnim = ObjectAnimator.ofFloat(mStatusArea, View.TRANSLATION_Y,
- statusAreaYTranslation);
- mStatusAreaAnim.setStartDelay(useLargeClock ? STATUS_AREA_START_DELAY_MILLIS : 0L);
- mStatusAreaAnim.setDuration(STATUS_AREA_MOVE_MILLIS);
- mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mStatusAreaAnim = new AnimatorSet();
+ mStatusAreaAnim.setStartDelay(STATUS_AREA_START_DELAY_MILLIS);
+ mStatusAreaAnim.setDuration(
+ useLargeClock ? STATUS_AREA_MOVE_UP_MILLIS : STATUS_AREA_MOVE_DOWN_MILLIS);
+ mStatusAreaAnim.setInterpolator(Interpolators.EMPHASIZED);
+ mStatusAreaAnim.playTogether(
+ ObjectAnimator.ofFloat(mStatusArea, TRANSLATION_Y, statusAreaYTranslation),
+ ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation));
mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
mStatusAreaAnim = null;
}
});
+
+ mClockInAnim.start();
+ mClockOutAnim.start();
mStatusAreaAnim.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index f04fdfff..9807b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -122,7 +122,9 @@
if (shouldAnimateIconViewForTransition(lastState, newState)) {
iconView.playAnimation()
}
- LottieColorUtils.applyDynamicColors(context, iconView)
+ if (isSideFps) {
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ }
}
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index ede62ac..a3f34ce 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -68,15 +68,15 @@
var inputTopBound: Int
var headerRightBound = right
var headerTopBounds = top
+ var headerBottomBounds = bottom
val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
val descBottom = if (descriptionView.isGone) subTitleBottom else descriptionView.bottom
if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
inputTopBound = (bottom - credentialInput.height) / 2
inputLeftBound = (right - left) / 2
headerRightBound = inputLeftBound
- headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
-
- if (descriptionView.bottom > bottomInset) {
+ if (descriptionView.bottom > headerBottomBounds) {
+ headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
index a334c1e..0bdc7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
@@ -77,6 +77,10 @@
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
settingsObserver,
UserHandle.myUserId());
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
+ settingsObserver,
+ UserHandle.myUserId());
settingsObserver.onChange(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 27cebc6..efcaa72 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -104,6 +104,16 @@
val SENSITIVE_REVEAL_ANIM =
unreleasedFlag(268005230, "sensitive_reveal_anim", teamfood = true)
+ // TODO(b/280783617): Tracking Bug
+ @Keep
+ @JvmField
+ val BUILDER_EXTRAS_OVERRIDE =
+ sysPropBooleanFlag(
+ 128,
+ "persist.sysui.notification.builder_extras_override",
+ default = false
+ )
+
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -136,7 +146,7 @@
* the digits when the clock moves.
*/
@JvmField
- val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true)
+ val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation")
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
@@ -525,13 +535,6 @@
val ENABLE_PIP_APP_ICON_OVERLAY =
sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = true)
- // TODO(b/272110828): Tracking bug
- @Keep
- @JvmField
- val ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
- sysPropBooleanFlag(
- 1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = true)
-
// TODO(b/273443374): Tracking Bug
@Keep
@JvmField val LOCKSCREEN_LIVE_WALLPAPER =
@@ -615,8 +618,6 @@
unreleasedFlag(1401, "quick_tap_flow_framework", teamfood = false)
// 1500 - chooser aka sharesheet
- // TODO(b/254512507): Tracking Bug
- val CHOOSER_UNBUNDLED = releasedFlag(1500, "chooser_unbundled")
// 1700 - clipboard
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 5d3f5f2..51a29b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -19,7 +19,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
@@ -30,13 +29,11 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionOldType;
import static android.view.WindowManager.TransitionType;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.Service;
@@ -116,6 +113,14 @@
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
final int taskId = taskInfo != null ? change.getTaskInfo().taskId : -1;
+ if (taskId != -1 && change.getParent() != null) {
+ final TransitionInfo.Change parentChange = info.getChange(change.getParent());
+ if (parentChange != null && parentChange.getTaskInfo() != null) {
+ // Only adding the root task as the animation target.
+ continue;
+ }
+ }
+
final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
// wallpapers go into the "below" layer space
info.getChanges().size() - i,
@@ -123,13 +128,6 @@
(change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0,
info, t, leashMap);
- // Use hasAnimatingParent to mark the anything below root task
- if (taskId != -1 && change.getParent() != null) {
- final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- if (parentChange != null && parentChange.getTaskInfo() != null) {
- target.hasAnimatingParent = true;
- }
- }
out.add(target);
}
return out.toArray(new RemoteAnimationTarget[out.size()]);
@@ -173,18 +171,15 @@
wrap(info, true /* wallpapers */, t, mLeashMap);
final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0];
- // Sets the alpha to 0 for the opening root task for fade in animation. And since
- // the fade in animation can only apply on the first opening app, so set alpha to 1
- // for anything else.
- for (RemoteAnimationTarget target : apps) {
- if (target.taskId != -1
- && target.mode == RemoteAnimationTarget.MODE_OPENING
- && !target.hasAnimatingParent) {
- t.setAlpha(target.leash, 0.0f);
- } else {
- t.setAlpha(target.leash, 1.0f);
+ // Set alpha back to 1 for the independent changes because we will be animating
+ // children instead.
+ for (TransitionInfo.Change chg : info.getChanges()) {
+ if (TransitionInfo.isIndependent(chg, info)) {
+ t.setAlpha(chg.getLeash(), 1.f);
}
}
+ initAlphaForAnimationTargets(t, apps);
+ initAlphaForAnimationTargets(t, wallpapers);
t.apply();
synchronized (mFinishCallbacks) {
mFinishCallbacks.put(transition, finishCallback);
@@ -223,6 +218,14 @@
// nothing, we'll just let it finish on its own I guess.
}
}
+
+ private static void initAlphaForAnimationTargets(@NonNull SurfaceControl.Transaction t,
+ @NonNull RemoteAnimationTarget[] targets) {
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode != MODE_OPENING) continue;
+ t.setAlpha(target.leash, 0.f);
+ }
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index f96f337..122e259 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -812,8 +812,8 @@
// Translate up from the bottom.
surfaceBehindMatrix.setTranslate(
- surfaceBehindRemoteAnimationTarget.localBounds.left.toFloat(),
- surfaceBehindRemoteAnimationTarget.localBounds.top.toFloat() +
+ surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
+ surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() +
surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
new file mode 100644
index 0000000..641e20b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class KeyguardClockRepository
+@Inject
+constructor(
+ private val secureSettings: SecureSettings,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ val selectedClockSize: Flow<SettingsClockSize> =
+ secureSettings
+ .observerFlow(
+ names = arrayOf(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
+ userId = UserHandle.USER_SYSTEM,
+ )
+ .onStart { emit(Unit) } // Forces an initial update.
+ .map { getClockSize() }
+
+ private suspend fun getClockSize(): SettingsClockSize {
+ return withContext(backgroundDispatcher) {
+ if (
+ secureSettings.getIntForUser(
+ Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
+ 1,
+ UserHandle.USER_CURRENT
+ ) == 1
+ ) {
+ SettingsClockSize.DYNAMIC
+ } else {
+ SettingsClockSize.SMALL
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
new file mode 100644
index 0000000..98f445c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
+import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates business-logic related to the keyguard clock. */
+@SysUISingleton
+class KeyguardClockInteractor
+@Inject
+constructor(
+ repository: KeyguardClockRepository,
+) {
+ val selectedClockSize: Flow<SettingsClockSize> = repository.selectedClockSize
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SettingsClockSize.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SettingsClockSize.kt
new file mode 100644
index 0000000..c6b0f58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SettingsClockSize.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.shared.model
+
+enum class SettingsClockSize {
+ DYNAMIC,
+ SMALL,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt
new file mode 100644
index 0000000..57c32b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.view.View
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockSmartspaceViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.flow.collect
+
+/** Binder for the small clock view, large clock view and smartspace. */
+object KeyguardPreviewClockSmartspaceViewBinder {
+
+ @JvmStatic
+ fun bind(
+ largeClockHostView: View,
+ smallClockHostView: View,
+ smartspace: View?,
+ viewModel: KeyguardPreviewClockSmartspaceViewModel,
+ ) {
+ largeClockHostView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.isLargeClockVisible.collect { largeClockHostView.isVisible = it }
+ }
+ }
+
+ smallClockHostView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.isSmallClockVisible.collect { smallClockHostView.isVisible = it }
+ }
+ }
+
+ smartspace?.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.smartSpaceTopPadding.collect { smartspace.setTopPadding(it) }
+ }
+ }
+ }
+
+ private fun View.setTopPadding(padding: Int) {
+ setPaddingRelative(paddingStart, padding, paddingEnd, paddingBottom)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 555a09b..4308d84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -22,6 +22,7 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.res.Resources
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.os.Bundle
@@ -33,6 +34,7 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
+import androidx.core.view.isInvisible
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.R
@@ -40,7 +42,10 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockSmartspaceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockSmartspaceViewModel
+import com.android.systemui.plugins.ClockController
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
@@ -60,6 +65,7 @@
@Application private val context: Context,
@Main private val mainDispatcher: CoroutineDispatcher,
@Main private val mainHandler: Handler,
+ private val clockSmartspaceViewModel: KeyguardPreviewClockSmartspaceViewModel,
private val bottomAreaViewModel: KeyguardBottomAreaViewModel,
displayManager: DisplayManager,
private val windowManager: WindowManager,
@@ -79,6 +85,7 @@
KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
false,
)
+ /** [shouldHideClock] here means that we never create and bind the clock views */
private val shouldHideClock: Boolean =
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
@@ -87,7 +94,8 @@
val surfacePackage: SurfaceControlViewHost.SurfacePackage
get() = host.surfacePackage
- private var clockView: View? = null
+ private lateinit var largeClockHostView: FrameLayout
+ private lateinit var smallClockHostView: FrameLayout
private var smartSpaceView: View? = null
private var colorOverride: Int? = null
@@ -126,6 +134,12 @@
if (!shouldHideClock) {
setUpClock(rootView)
+ KeyguardPreviewClockSmartspaceViewBinder.bind(
+ largeClockHostView,
+ smallClockHostView,
+ smartSpaceView,
+ clockSmartspaceViewModel,
+ )
}
rootView.measure(
@@ -205,11 +219,9 @@
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
val topPadding: Int =
- with(context.resources) {
- getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
- getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
- getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
- }
+ KeyguardPreviewClockSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
+ context.resources
+ )
val startPadding: Int =
with(context.resources) {
@@ -284,10 +296,19 @@
}
private fun setUpClock(parentView: ViewGroup) {
+ largeClockHostView = createLargeClockHostView()
+ largeClockHostView.isInvisible = true
+ parentView.addView(largeClockHostView)
+
+ smallClockHostView = createSmallClockHostView(parentView.resources)
+ smallClockHostView.isInvisible = true
+ parentView.addView(smallClockHostView)
+
+ // TODO (b/283465254): Move the listeners to KeyguardClockRepository
val clockChangeListener =
object : ClockRegistry.ClockChangeListener {
override fun onCurrentClockChanged() {
- onClockChanged(parentView)
+ onClockChanged()
}
}
clockRegistry.registerClockChangeListener(clockChangeListener)
@@ -317,62 +338,89 @@
disposables.add(DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) })
val layoutChangeListener =
- object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- if (clockController.clock !is DefaultClockController) {
- clockController.clock
- ?.largeClock
- ?.events
- ?.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(parentView)
- )
- }
+ View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
+ if (clockController.clock !is DefaultClockController) {
+ clockController.clock
+ ?.largeClock
+ ?.events
+ ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
}
}
-
parentView.addOnLayoutChangeListener(layoutChangeListener)
-
disposables.add(
DisposableHandle { parentView.removeOnLayoutChangeListener(layoutChangeListener) }
)
- onClockChanged(parentView)
+ onClockChanged()
}
- private fun onClockChanged(parentView: ViewGroup) {
+ private fun createLargeClockHostView(): FrameLayout {
+ val hostView = FrameLayout(context)
+ hostView.layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ )
+ return hostView
+ }
+
+ private fun createSmallClockHostView(resources: Resources): FrameLayout {
+ val hostView = FrameLayout(context)
+ val layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ resources.getDimensionPixelSize(R.dimen.small_clock_height)
+ )
+ layoutParams.topMargin =
+ KeyguardPreviewClockSmartspaceViewModel.getStatusBarHeight(resources) +
+ resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
+ hostView.layoutParams = layoutParams
+
+ hostView.setPaddingRelative(
+ resources.getDimensionPixelSize(R.dimen.clock_padding_start),
+ 0,
+ 0,
+ 0
+ )
+ hostView.clipChildren = false
+ return hostView
+ }
+
+ private fun onClockChanged() {
val clock = clockRegistry.createCurrentClock()
clockController.clock = clock
colorOverride?.let { clock.events.onSeedColorChanged(it) }
- clock.largeClock.events.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(parentView)
- )
-
- clockView?.let { parentView.removeView(it) }
- clockView =
- clock.largeClock.view.apply {
- if (shouldHighlightSelectedAffordance) {
- alpha = DIM_ALPHA
- }
- parentView.addView(this)
- visibility = View.VISIBLE
- }
+ updateLargeClock(clock)
+ updateSmallClock(clock)
// Hide smart space if the clock has weather display; otherwise show it
hideSmartspace(clock.largeClock.config.hasCustomWeatherDataDisplay)
}
+ private fun updateLargeClock(clock: ClockController) {
+ clock.largeClock.events.onTargetRegionChanged(
+ KeyguardClockSwitch.getLargeClockRegion(largeClockHostView)
+ )
+ if (shouldHighlightSelectedAffordance) {
+ clock.largeClock.view.alpha = DIM_ALPHA
+ }
+ largeClockHostView.removeAllViews()
+ largeClockHostView.addView(clock.largeClock.view)
+ }
+
+ private fun updateSmallClock(clock: ClockController) {
+ clock.smallClock.events.onTargetRegionChanged(
+ KeyguardClockSwitch.getSmallClockRegion(smallClockHostView)
+ )
+ if (shouldHighlightSelectedAffordance) {
+ clock.smallClock.view.alpha = DIM_ALPHA
+ }
+ smallClockHostView.removeAllViews()
+ smallClockHostView.addView(clock.smallClock.view)
+ }
+
companion object {
private const val KEY_HOST_TOKEN = "host_token"
private const val KEY_VIEW_WIDTH = "width"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt
new file mode 100644
index 0000000..00c603b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Resources
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** View model for the small clock view, large clock view and smartspace. */
+class KeyguardPreviewClockSmartspaceViewModel
+@Inject
+constructor(
+ @Application private val context: Context,
+ interactor: KeyguardClockInteractor,
+) {
+
+ val isLargeClockVisible: Flow<Boolean> =
+ interactor.selectedClockSize.map { it == SettingsClockSize.DYNAMIC }
+
+ val isSmallClockVisible: Flow<Boolean> =
+ interactor.selectedClockSize.map { it == SettingsClockSize.SMALL }
+
+ val smartSpaceTopPadding: Flow<Int> =
+ interactor.selectedClockSize.map {
+ when (it) {
+ SettingsClockSize.DYNAMIC -> getLargeClockSmartspaceTopPadding(context.resources)
+ SettingsClockSize.SMALL -> getSmallClockSmartspaceTopPadding(context.resources)
+ }
+ }
+
+ companion object {
+ fun getLargeClockSmartspaceTopPadding(resources: Resources): Int {
+ return with(resources) {
+ getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
+ getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
+ getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
+ }
+ }
+
+ fun getSmallClockSmartspaceTopPadding(resources: Resources): Int {
+ return with(resources) {
+ getStatusBarHeight(this) +
+ getDimensionPixelSize(R.dimen.small_clock_padding_top) +
+ getDimensionPixelSize(R.dimen.small_clock_height)
+ }
+ }
+
+ fun getStatusBarHeight(resource: Resources): Int {
+ var result = 0
+ val resourceId: Int = resource.getIdentifier("status_bar_height", "dimen", "android")
+ if (resourceId > 0) {
+ result = resource.getDimensionPixelSize(resourceId)
+ }
+ return result
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 1469d96..bce3346 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -16,10 +16,12 @@
package com.android.systemui.media.controls.pipeline
+import android.annotation.SuppressLint
import android.app.BroadcastOptions
import android.app.Notification
import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
import android.app.PendingIntent
+import android.app.StatusBarManager
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession
@@ -43,7 +45,6 @@
import android.net.Uri
import android.os.Parcelable
import android.os.Process
-import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -53,7 +54,6 @@
import android.util.Pair as APair
import androidx.media.utils.MediaConstants
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -185,7 +185,6 @@
private val logger: MediaUiEventLogger,
private val smartspaceManager: SmartspaceManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val statusBarService: IStatusBarService,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -230,6 +229,10 @@
private val artworkHeight =
context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
+ @SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
+ private val statusBarManager =
+ context.getSystemService(Context.STATUS_BAR_SERVICE) as StatusBarManager
+
/** Check whether this notification is an RCN */
private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
@@ -257,7 +260,6 @@
mediaFlags: MediaFlags,
logger: MediaUiEventLogger,
smartspaceManager: SmartspaceManager,
- statusBarService: IStatusBarService,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : this(
context,
@@ -283,7 +285,6 @@
logger,
smartspaceManager,
keyguardUpdateMonitor,
- statusBarService,
)
private val appChangeReceiver =
@@ -793,27 +794,12 @@
song = HybridGroupManager.resolveTitle(notif)
}
if (song.isNullOrBlank()) {
- if (mediaFlags.isMediaTitleRequired(sbn.packageName, sbn.user)) {
- // App is required to provide a title: cancel the underlying notification
- try {
- statusBarService.onNotificationError(
- sbn.packageName,
- sbn.tag,
- sbn.id,
- sbn.uid,
- sbn.initialPid,
- MEDIA_TITLE_ERROR_MESSAGE,
- sbn.user.identifier
- )
- } catch (e: RemoteException) {
- Log.e(TAG, "cancelNotification failed: $e")
- }
- // Only add log for media removed if active media is updated with invalid title.
- foregroundExecutor.execute { removeEntry(key, !isNewlyActiveEntry) }
- return
- } else {
- // For apps that don't have the title requirement yet, add a placeholder
- song = context.getString(R.string.controls_media_empty_title, appName)
+ // For apps that don't include a title, log and add a placeholder
+ song = context.getString(R.string.controls_media_empty_title, appName)
+ try {
+ statusBarManager.logBlankMediaTitle(sbn.packageName, sbn.user.identifier)
+ } catch (e: RuntimeException) {
+ Log.e(TAG, "Error reporting blank media title for package ${sbn.packageName}")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 3751c60..9bc66f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -64,9 +64,4 @@
/** Check whether we allow remote media to generate resume controls */
fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
-
- /** Check whether app is required to provide a non-empty media title */
- fun isMediaTitleRequired(packageName: String, user: UserHandle): Boolean {
- return StatusBarManager.isMediaTitleRequiredForApp(packageName, user)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 993c3801..b956207 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -354,7 +354,11 @@
@Override
protected void snapChild(final View animView, final float targetLeft, float velocity) {
- superSnapChild(animView, targetLeft, velocity);
+ if (animView instanceof SwipeableView) {
+ // only perform the snapback animation on views that are swipeable inside the shade.
+ superSnapChild(animView, targetLeft, velocity);
+ }
+
mCallback.onDragCancelled(animView);
if (targetLeft == 0) {
handleMenuCoveredOrDismissed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index a6b2bd8..f26a84b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -30,6 +30,8 @@
import android.view.ViewDebug;
import android.view.WindowInsetsController.Appearance;
+import androidx.annotation.NonNull;
+
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dumpable;
@@ -46,7 +48,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Date;
import javax.inject.Inject;
@@ -57,7 +58,8 @@
public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
private static final String TAG = "LightBarController";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_NAVBAR = Compile.IS_DEBUG;
+ private static final boolean DEBUG_LOGS = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
@@ -113,6 +115,7 @@
private String mLastSetScrimStateLog;
private String mLastNavigationBarAppearanceChangedLog;
+ private StringBuilder mLogStringBuilder = null;
@Inject
public LightBarController(
@@ -193,35 +196,43 @@
final boolean darkForTop = darkForQs || mGlobalActionsVisible;
mNavigationLight =
((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForTop;
- mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
- + " appearance=" + appearance
- + " nbModeChanged=" + nbModeChanged
- + " navigationBarMode=" + navigationBarMode
- + " navbarColorManagedByIme=" + navbarColorManagedByIme
- + " mHasLightNavigationBar=" + mHasLightNavigationBar
- + " ignoreScrimForce=" + ignoreScrimForce
- + " darkForScrim=" + darkForScrim
- + " lightForScrim=" + lightForScrim
- + " darkForQs=" + darkForQs
- + " darkForTop=" + darkForTop
- + " mNavigationLight=" + mNavigationLight
- + " last=" + last
- + " timestamp=" + new Date();
- if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ if (DEBUG_NAVBAR) {
+ mLastNavigationBarAppearanceChangedLog = getLogStringBuilder()
+ .append("onNavigationBarAppearanceChanged()")
+ .append(" appearance=").append(appearance)
+ .append(" nbModeChanged=").append(nbModeChanged)
+ .append(" navigationBarMode=").append(navigationBarMode)
+ .append(" navbarColorManagedByIme=").append(navbarColorManagedByIme)
+ .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar)
+ .append(" ignoreScrimForce=").append(ignoreScrimForce)
+ .append(" darkForScrim=").append(darkForScrim)
+ .append(" lightForScrim=").append(lightForScrim)
+ .append(" darkForQs=").append(darkForQs)
+ .append(" darkForTop=").append(darkForTop)
+ .append(" mNavigationLight=").append(mNavigationLight)
+ .append(" last=").append(last)
+ .append(" timestamp=").append(System.currentTimeMillis())
+ .toString();
+ if (DEBUG_LOGS) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ }
} else {
mNavigationLight = mHasLightNavigationBar
&& (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
&& !mQsCustomizing;
- mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
- + " appearance=" + appearance
- + " nbModeChanged=" + nbModeChanged
- + " navigationBarMode=" + navigationBarMode
- + " navbarColorManagedByIme=" + navbarColorManagedByIme
- + " mHasLightNavigationBar=" + mHasLightNavigationBar
- + " mNavigationLight=" + mNavigationLight
- + " last=" + last
- + " timestamp=" + new Date();
- if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ if (DEBUG_NAVBAR) {
+ mLastNavigationBarAppearanceChangedLog = getLogStringBuilder()
+ .append("onNavigationBarAppearanceChanged()")
+ .append(" appearance=").append(appearance)
+ .append(" nbModeChanged=").append(nbModeChanged)
+ .append(" navigationBarMode=").append(navigationBarMode)
+ .append(" navbarColorManagedByIme=").append(navbarColorManagedByIme)
+ .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar)
+ .append(" mNavigationLight=").append(mNavigationLight)
+ .append(" last=").append(last)
+ .append(" timestamp=").append(System.currentTimeMillis())
+ .toString();
+ if (DEBUG_LOGS) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ }
}
if (mNavigationLight != last) {
updateNavigation();
@@ -319,18 +330,22 @@
} else {
if (mForceLightForScrim != forceLightForScrimLast) reevaluate();
}
- mLastSetScrimStateLog = "setScrimState()"
- + " scrimState=" + scrimState
- + " scrimBehindAlpha=" + scrimBehindAlpha
- + " scrimInFrontColor=" + scrimInFrontColor
- + " forceForScrim=" + forceForScrim
- + " scrimColorIsLight=" + scrimColorIsLight
- + " mHasLightNavigationBar=" + mHasLightNavigationBar
- + " mBouncerVisible=" + mBouncerVisible
- + " mForceDarkForScrim=" + mForceDarkForScrim
- + " mForceLightForScrim=" + mForceLightForScrim
- + " timestamp=" + new Date();
- if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
+ if (DEBUG_NAVBAR) {
+ mLastSetScrimStateLog = getLogStringBuilder()
+ .append("setScrimState()")
+ .append(" scrimState=").append(scrimState)
+ .append(" scrimBehindAlpha=").append(scrimBehindAlpha)
+ .append(" scrimInFrontColor=").append(scrimInFrontColor)
+ .append(" forceForScrim=").append(forceForScrim)
+ .append(" scrimColorIsLight=").append(scrimColorIsLight)
+ .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar)
+ .append(" mBouncerVisible=").append(mBouncerVisible)
+ .append(" mForceDarkForScrim=").append(mForceDarkForScrim)
+ .append(" mForceLightForScrim=").append(mForceLightForScrim)
+ .append(" timestamp=").append(System.currentTimeMillis())
+ .toString();
+ if (DEBUG_LOGS) Log.d(TAG, mLastSetScrimStateLog);
+ }
} else {
boolean forceDarkForScrimLast = mForceDarkForScrim;
// For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
@@ -344,17 +359,30 @@
if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
reevaluate();
}
- mLastSetScrimStateLog = "setScrimState()"
- + " scrimState=" + scrimState
- + " scrimBehindAlpha=" + scrimBehindAlpha
- + " scrimInFrontColor=" + scrimInFrontColor
- + " mHasLightNavigationBar=" + mHasLightNavigationBar
- + " mForceDarkForScrim=" + mForceDarkForScrim
- + " timestamp=" + new Date();
- if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
+ if (DEBUG_NAVBAR) {
+ mLastSetScrimStateLog = getLogStringBuilder()
+ .append("setScrimState()")
+ .append(" scrimState=").append(scrimState)
+ .append(" scrimBehindAlpha=").append(scrimBehindAlpha)
+ .append(" scrimInFrontColor=").append(scrimInFrontColor)
+ .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar)
+ .append(" mForceDarkForScrim=").append(mForceDarkForScrim)
+ .append(" timestamp=").append(System.currentTimeMillis())
+ .toString();
+ if (DEBUG_LOGS) Log.d(TAG, mLastSetScrimStateLog);
+ }
}
}
+ @NonNull
+ private StringBuilder getLogStringBuilder() {
+ if (mLogStringBuilder == null) {
+ mLogStringBuilder = new StringBuilder();
+ }
+ mLogStringBuilder.setLength(0);
+ return mLogStringBuilder;
+ }
+
private static boolean isLight(int appearance, int barMode, int flag) {
final boolean isTransparentBar = (barMode == MODE_TRANSPARENT
|| barMode == MODE_LIGHTS_OUT_TRANSPARENT);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 5ba02fa..b24a692 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -73,7 +73,6 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -113,7 +112,6 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.RotationPolicy;
@@ -133,15 +131,11 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.RoundedCornerProgressDrawable;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -198,9 +192,6 @@
private ViewGroup mDialogRowsView;
private ViewGroup mRinger;
- private DeviceConfigProxy mDeviceConfigProxy;
- private Executor mExecutor;
-
/**
* Container for the top part of the dialog, which contains the ringer, the ringer drawer, the
* volume rows, and the ellipsis button. This does not include the live caption button.
@@ -290,14 +281,12 @@
private BackgroundBlurDrawable mDialogRowsViewBackground;
private final InteractionJankMonitor mInteractionJankMonitor;
- private boolean mSeparateNotification;
-
private int mWindowGravity;
@VisibleForTesting
- int mVolumeRingerIconDrawableId;
+ final int mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
@VisibleForTesting
- int mVolumeRingerMuteIconDrawableId;
+ final int mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
private int mOriginalGravity;
private final DevicePostureController.Callback mDevicePostureControllerCallback;
@@ -315,8 +304,6 @@
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor,
- DeviceConfigProxy deviceConfigProxy,
- Executor executor,
CsdWarningDialog.Factory csdWarningDialogFactory,
DevicePostureController devicePostureController,
Looper looper,
@@ -374,12 +361,6 @@
} else {
mDevicePostureControllerCallback = null;
}
-
- mDeviceConfigProxy = deviceConfigProxy;
- mExecutor = executor;
- mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
- updateRingerModeIconSet();
}
/**
@@ -401,44 +382,6 @@
return mWindowGravity;
}
- /**
- * If ringer and notification are the same stream (T and earlier), use notification-like bell
- * icon set.
- * If ringer and notification are separated, then use generic speaker icons.
- */
- private void updateRingerModeIconSet() {
- if (mSeparateNotification) {
- mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
- mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
- } else {
- mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer;
- mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute;
- }
-
- if (mRingerDrawerMuteIcon != null) {
- mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
- }
- if (mRingerDrawerNormalIcon != null) {
- mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
- }
- }
-
- /**
- * Change icon for ring stream (not ringer mode icon)
- */
- private void updateRingRowIcon() {
- Optional<VolumeRow> volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING)
- .findFirst();
- if (volumeRow.isPresent()) {
- VolumeRow volRow = volumeRow.get();
- volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume
- : R.drawable.ic_volume_ringer;
- volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off
- : R.drawable.ic_volume_ringer_mute;
- volRow.setIcon(volRow.iconRes, mContext.getTheme());
- }
- }
-
@Override
public void onUiModeChanged() {
mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
@@ -454,9 +397,6 @@
mConfigurationController.addCallback(this);
- mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mExecutor, this::onDeviceConfigChange);
-
if (mDevicePostureController != null) {
mDevicePostureController.addCallback(mDevicePostureControllerCallback);
}
@@ -464,31 +404,15 @@
@Override
public void destroy() {
+ Log.d(TAG, "destroy() called");
mController.removeCallback(mControllerCallbackH);
mHandler.removeCallbacksAndMessages(null);
mConfigurationController.removeCallback(this);
- mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
if (mDevicePostureController != null) {
mDevicePostureController.removeCallback(mDevicePostureControllerCallback);
}
}
- /**
- * Update ringer mode icon based on the config
- */
- private void onDeviceConfigChange(DeviceConfig.Properties properties) {
- Set<String> changeSet = properties.getKeyset();
- if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
- boolean newVal = properties.getBoolean(
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
- if (newVal != mSeparateNotification) {
- mSeparateNotification = newVal;
- updateRingerModeIconSet();
- updateRingRowIcon();
- }
- }
- }
-
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo internalInsetsInfo) {
// Set touchable region insets on the root dialog view. This tells WindowManager that
@@ -542,6 +466,7 @@
}
private void initDialog(int lockTaskModeState) {
+ Log.d(TAG, "initDialog: called!");
mDialog = new CustomDialog(mContext);
initDimens();
@@ -699,7 +624,12 @@
mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
- updateRingerModeIconSet();
+ if (mRingerDrawerMuteIcon != null) {
+ mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ }
+ if (mRingerDrawerNormalIcon != null) {
+ mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+ }
setupRingerDrawer();
@@ -724,13 +654,10 @@
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
- if (mSeparateNotification) {
- addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
- R.drawable.ic_ring_volume_off, true, false);
- } else {
- addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer,
- R.drawable.ic_volume_ringer, true, false);
- }
+
+ addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
+ R.drawable.ic_ring_volume_off, true, false);
+
addRow(STREAM_ALARM,
R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
@@ -1344,7 +1271,7 @@
}
protected void tryToRemoveCaptionsTooltip() {
- if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null) {
+ if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null && mDialog != null) {
ViewGroup container = mDialog.findViewById(R.id.volume_dialog_container);
container.removeView(mODICaptionsTooltipView);
mODICaptionsTooltipView = null;
@@ -1551,8 +1478,16 @@
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
- if (mIsAnimatingDismiss) {
- Log.d(TAG, "dismissH: isAnimatingDismiss");
+
+ boolean showingStateInconsistent = !mShowing && mDialog != null && mDialog.isShowing();
+ // If incorrectly assuming dialog is not showing, continue and make the state consistent.
+ if (showingStateInconsistent) {
+ Log.d(TAG, "dismissH: volume dialog possible in inconsistent state:"
+ + "mShowing=" + mShowing + ", mDialog==null?" + (mDialog == null));
+ }
+ if (mIsAnimatingDismiss && !showingStateInconsistent) {
+ Log.d(TAG, "dismissH: skipping dismiss because isAnimatingDismiss is true"
+ + " and showingStateInconsistent is false");
Trace.endSection();
return;
}
@@ -1570,8 +1505,12 @@
.setDuration(mDialogHideAnimationDurationMs)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
- mController.notifyVisible(false);
- mDialog.dismiss();
+ if (mController != null) {
+ mController.notifyVisible(false);
+ }
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
tryToRemoveCaptionsTooltip();
mIsAnimatingDismiss = false;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index bb04f82..aa4ee54 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -21,7 +21,6 @@
import android.os.Looper;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
@@ -31,7 +30,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -42,8 +40,6 @@
import dagger.Module;
import dagger.Provides;
-import java.util.concurrent.Executor;
-
/** Dagger Module for code in the volume package. */
@Module
public interface VolumeModule {
@@ -63,8 +59,6 @@
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor,
- DeviceConfigProxy deviceConfigProxy,
- @Main Executor executor,
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager) {
@@ -78,8 +72,6 @@
volumePanelFactory,
activityStarter,
interactionJankMonitor,
- deviceConfigProxy,
- executor,
csdFactory,
devicePostureController,
Looper.getMainLooper(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 3bcefcf..56698e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -41,7 +41,6 @@
import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.R
@@ -133,7 +132,6 @@
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var smartspaceManager: SmartspaceManager
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock lateinit var statusBarService: IStatusBarService
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@@ -197,7 +195,6 @@
logger = logger,
smartspaceManager = smartspaceManager,
keyguardUpdateMonitor = keyguardUpdateMonitor,
- statusBarService = statusBarService,
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -522,143 +519,12 @@
}
@Test
- fun testOnNotificationAdded_emptyTitle_isRequired_notLoaded() {
- // When the manager has a notification with an empty title, and the app is required
- // to include a non-empty title
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is not added and we report a notification error
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationAdded_blankTitle_isRequired_notLoaded() {
- // When the manager has a notification with a blank title, and the app is required
- // to include a non-empty title
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is not added and we report a notification error
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationUpdated_invalidTitle_isRequired_logMediaRemoved() {
- // When the app is required to provide a non-blank title, and updates a previously valid
- // title to an empty one
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- addNotificationAndLoad()
- val data = mediaDataCaptor.value
-
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
-
- reset(listener)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is removed
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
- }
-
- @Test
- fun testOnNotificationAdded_emptyTitle_notRequired_hasPlaceholder() {
+ fun testOnNotificationAdded_emptyTitle_hasPlaceholder() {
// When the manager has a notification with an empty title, and the app is not
// required to include a non-empty title
val mockPackageManager = mock(PackageManager::class.java)
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(false)
whenever(controller.metadata)
.thenReturn(
metadataBuilder
@@ -684,13 +550,12 @@
}
@Test
- fun testOnNotificationAdded_blankTitle_notRequired_hasPlaceholder() {
+ fun testOnNotificationAdded_blankTitle_hasPlaceholder() {
// GIVEN that the manager has a notification with a blank title, and the app is not
// required to include a non-empty title
val mockPackageManager = mock(PackageManager::class.java)
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(false)
whenever(controller.metadata)
.thenReturn(
metadataBuilder
@@ -722,7 +587,6 @@
val mockPackageManager = mock(PackageManager::class.java)
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
whenever(controller.metadata)
.thenReturn(
metadataBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
index 2eca78a..e92368d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import android.view.LayoutInflater
import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.TextAnimator
@@ -64,8 +65,8 @@
color = 200,
strokeWidth = -1F,
animate = false,
- duration = 350L,
- interpolator = null,
+ duration = 833L,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
delay = 0L,
onAnimationEnd = null
)
@@ -98,8 +99,8 @@
color = 200,
strokeWidth = -1F,
animate = true,
- duration = 350L,
- interpolator = null,
+ duration = 833L,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
delay = 0L,
onAnimationEnd = null
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 551499e..7632d01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -392,23 +392,32 @@
@Test
public void testSnapchild_targetIsZero() {
- doNothing().when(mSwipeHelper).superSnapChild(mView, 0, 0);
- mSwipeHelper.snapChild(mView, 0, 0);
+ doNothing().when(mSwipeHelper).superSnapChild(mNotificationRow, 0, 0);
+ mSwipeHelper.snapChild(mNotificationRow, 0, 0);
- verify(mCallback, times(1)).onDragCancelled(mView);
- verify(mSwipeHelper, times(1)).superSnapChild(mView, 0, 0);
+ verify(mCallback, times(1)).onDragCancelled(mNotificationRow);
+ verify(mSwipeHelper, times(1)).superSnapChild(mNotificationRow, 0, 0);
verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed();
}
@Test
public void testSnapchild_targetNotZero() {
+ doNothing().when(mSwipeHelper).superSnapChild(mNotificationRow, 10, 0);
+ mSwipeHelper.snapChild(mNotificationRow, 10, 0);
+
+ verify(mCallback, times(1)).onDragCancelled(mNotificationRow);
+ verify(mSwipeHelper, times(1)).superSnapChild(mNotificationRow, 10, 0);
+ verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed();
+ }
+
+ @Test
+ public void testSnapchild_targetNotSwipeable() {
doNothing().when(mSwipeHelper).superSnapChild(mView, 10, 0);
mSwipeHelper.snapChild(mView, 10, 0);
- verify(mCallback, times(1)).onDragCancelled(mView);
- verify(mSwipeHelper, times(1)).superSnapChild(mView, 10, 0);
- verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed();
+ verify(mCallback).onDragCancelled(mView);
+ verify(mSwipeHelper, never()).superSnapChild(mView, 10, 0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 45a37cf..8f725be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -35,7 +35,6 @@
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.SystemClock;
-import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Gravity;
@@ -47,7 +46,6 @@
import androidx.test.filters.SmallTest;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -62,9 +60,6 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
-import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import org.junit.After;
import org.junit.Before;
@@ -88,8 +83,6 @@
View mDrawerVibrate;
View mDrawerMute;
View mDrawerNormal;
- private DeviceConfigProxyFake mDeviceConfigProxy;
- private FakeExecutor mExecutor;
private TestableLooper mTestableLooper;
private ConfigurationController mConfigurationController;
private int mOriginalOrientation;
@@ -131,8 +124,6 @@
getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
mTestableLooper = TestableLooper.get(this);
- mDeviceConfigProxy = new DeviceConfigProxyFake();
- mExecutor = new FakeExecutor(new FakeSystemClock());
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
@@ -151,8 +142,6 @@
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
- mDeviceConfigProxy,
- mExecutor,
mCsdWarningDialogFactory,
mPostureController,
mTestableLooper.getLooper(),
@@ -173,9 +162,6 @@
VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
-
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
}
private State createShellState() {
@@ -351,13 +337,8 @@
* API does not exist. So we do the next best thing; we check the cached icon id.
*/
@Test
- public void notificationVolumeSeparated_theRingerIconChanges() {
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
-
- mExecutor.runAllReady(); // for the config change to take effect
-
- // assert icon is new based on res id
+ public void notificationVolumeSeparated_theRingerIconChangesToSpeakerIcon() {
+ // already separated. assert icon is new based on res id
assertEquals(mDialog.mVolumeRingerIconDrawableId,
R.drawable.ic_speaker_on);
assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
@@ -365,17 +346,6 @@
}
@Test
- public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() {
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
-
- mExecutor.runAllReady();
-
- assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer);
- assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute);
- }
-
- @Test
public void testDialogDismissAnimation_notifyVisibleIsNotCalledBeforeAnimation() {
mDialog.dismissH(DISMISS_REASON_UNKNOWN);
// notifyVisible(false) should not be called immediately but only after the dismiss
@@ -408,8 +378,6 @@
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
- mDeviceConfigProxy,
- mExecutor,
mCsdWarningDialogFactory,
devicePostureController,
mTestableLooper.getLooper(),
@@ -447,8 +415,6 @@
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
- mDeviceConfigProxy,
- mExecutor,
mCsdWarningDialogFactory,
devicePostureController,
mTestableLooper.getLooper(),
@@ -485,8 +451,6 @@
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
- mDeviceConfigProxy,
- mExecutor,
mCsdWarningDialogFactory,
devicePostureController,
mTestableLooper.getLooper(),
@@ -525,8 +489,6 @@
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
- mDeviceConfigProxy,
- mExecutor,
mCsdWarningDialogFactory,
mPostureController,
mTestableLooper.getLooper(),
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 1e5f187..85a0185 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -837,7 +837,7 @@
*/
@GuardedBy("mService")
void enqueueOomAdjTargetLocked(ProcessRecord app) {
- if (app != null) {
+ if (app != null && app.mState.getMaxAdj() > FOREGROUND_APP_ADJ) {
mPendingProcessSet.add(app);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 355981a..d0b6cdc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -45,7 +45,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -167,7 +166,6 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.System;
import android.service.notification.ZenModeConfig;
@@ -187,10 +185,8 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -252,7 +248,6 @@
AudioSystemAdapter.OnVolRangeInitRequestListener {
private static final String TAG = "AS.AudioService";
- private static final boolean CONFIG_DEFAULT_VAL = false;
private final AudioSystemAdapter mAudioSystem;
private final SystemServerAdapter mSystemServer;
@@ -309,7 +304,7 @@
* indicates whether STREAM_NOTIFICATION is aliased to STREAM_RING
* not final due to test method, see {@link #setNotifAliasRingForTest(boolean)}.
*/
- private boolean mNotifAliasRing;
+ private boolean mNotifAliasRing = false;
/**
* Test method to temporarily override whether STREAM_NOTIFICATION is aliased to STREAM_RING,
@@ -1057,13 +1052,6 @@
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
- mNotifAliasRing = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
-
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- ActivityThread.currentApplication().getMainExecutor(),
- this::onDeviceConfigChange);
-
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -1157,6 +1145,11 @@
MAX_STREAM_VOLUME[AudioSystem.STREAM_SYSTEM];
}
+ int minAssistantVolume = SystemProperties.getInt("ro.config.assistant_vol_min", -1);
+ if (minAssistantVolume != -1) {
+ MIN_STREAM_VOLUME[AudioSystem.STREAM_ASSISTANT] = minAssistantVolume;
+ }
+
// Read following properties to configure max volume (number of steps) and default volume
// for STREAM_NOTIFICATION and STREAM_RING:
// config_audio_notif_vol_default
@@ -1277,22 +1270,6 @@
}
/**
- * Separating notification volume from ring is NOT of aliasing the corresponding streams
- * @param properties
- */
- private void onDeviceConfigChange(DeviceConfig.Properties properties) {
- Set<String> changeSet = properties.getKeyset();
- if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
- boolean newNotifAliasRing = !properties.getBoolean(
- SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
- if (mNotifAliasRing != newNotifAliasRing) {
- mNotifAliasRing = newNotifAliasRing;
- updateStreamVolumeAlias(true, TAG);
- }
- }
- }
-
- /**
* Called by handling of MSG_INIT_STREAMS_VOLUMES
*/
private void onInitStreamsAndVolumes() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index a486d16..694dfd2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -70,9 +70,11 @@
// Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible
// for that the queue will wait indefinitely until the field is cleared.
- if (clientMonitor instanceof StopUserClient<?> && !success) {
- Slog.w(getTag(),
- "StopUserClient failed(), is the HAL stuck? Clearing mStopUserClient");
+ if (clientMonitor instanceof StopUserClient<?>) {
+ if (!success) {
+ Slog.w(getTag(), "StopUserClient failed(), is the HAL stuck? "
+ + "Clearing mStopUserClient");
+ }
mStopUserClient = null;
}
if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index ffbf4e1..2ad41c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -89,7 +89,7 @@
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@NonNull private final Supplier<AidlSession> mLazySession;
- @Nullable private AidlSession mCurrentSession;
+ @Nullable AidlSession mCurrentSession;
@VisibleForTesting
public static class HalSessionCallback extends ISessionCallback.Stub {
@@ -486,7 +486,7 @@
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext, AidlSession session) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -549,6 +549,14 @@
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
}
+ Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ biometricContext, null);
+ }
+
@NonNull Supplier<AidlSession> getLazySession() {
return mLazySession;
}
@@ -557,7 +565,7 @@
return mSensorProperties;
}
- @Nullable AidlSession getSessionForUser(int userId) {
+ @VisibleForTesting @Nullable AidlSession getSessionForUser(int userId) {
if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
return mCurrentSession;
} else {
@@ -641,6 +649,8 @@
BiometricsProtoEnums.MODALITY_FACE,
BiometricsProtoEnums.ISSUE_HAL_DEATH,
-1 /* sensorId */);
+ } else if (client != null) {
+ client.cancel();
}
mScheduler.recordCrashState();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index c0dde72..56b85ce 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -90,7 +90,7 @@
@NonNull private final LockoutCache mLockoutCache;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
- @Nullable private AidlSession mCurrentSession;
+ @Nullable AidlSession mCurrentSession;
@NonNull private final Supplier<AidlSession> mLazySession;
@VisibleForTesting
@@ -439,7 +439,7 @@
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext, AidlSession session) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -501,6 +501,16 @@
});
mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+ mCurrentSession = session;
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext, null);
}
@NonNull Supplier<AidlSession> getLazySession() {
@@ -599,6 +609,8 @@
BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ISSUE_HAL_DEATH,
-1 /* sensorId */);
+ } else if (client != null) {
+ client.cancel();
}
mScheduler.recordCrashState();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 20f0697..2e62ef4 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -889,22 +889,31 @@
}
- private void migrateOldDataAfterSystemReady() {
- // Migrate the FRP credential to the persistent data block
+ @VisibleForTesting
+ void migrateOldDataAfterSystemReady() {
+ // Write the FRP persistent data block if needed.
+ //
+ // The original purpose of this code was to write the FRP block for the first time, when
+ // upgrading from Android 8.1 or earlier which didn't use the FRP block. This code has
+ // since been repurposed to also fix the "bad" (non-forwards-compatible) FRP block written
+ // by Android 14 Beta 2. For this reason, the database key used here has been renamed from
+ // "migrated_frp" to "migrated_frp2" to cause migrateFrpCredential() to run again on devices
+ // where it had run before.
if (LockPatternUtils.frpCredentialEnabled(mContext)
- && !getBoolean("migrated_frp", false, 0)) {
+ && !getBoolean("migrated_frp2", false, 0)) {
migrateFrpCredential();
- setBoolean("migrated_frp", true, 0);
+ setBoolean("migrated_frp2", true, 0);
}
}
/**
- * Migrate the credential for the FRP credential owner user if the following are satisfied:
- * - the user has a secure credential
- * - the FRP credential is not set up
+ * Write the FRP persistent data block if the following are satisfied:
+ * - the user who owns the FRP credential has a nonempty credential
+ * - the FRP persistent data block doesn't exist or uses the "bad" format from Android 14 Beta 2
*/
private void migrateFrpCredential() {
- if (mStorage.readPersistentDataBlock() != PersistentData.NONE) {
+ PersistentData data = mStorage.readPersistentDataBlock();
+ if (data != PersistentData.NONE && !data.isBadFormatFromAndroid14Beta()) {
return;
}
for (UserInfo userInfo : mUserManager.getUsers()) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 731ecad..2fa637e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -606,6 +606,11 @@
this.payload = payload;
}
+ public boolean isBadFormatFromAndroid14Beta() {
+ return (this.type == TYPE_SP_GATEKEEPER || this.type == TYPE_SP_WEAVER)
+ && SyntheticPasswordManager.PasswordData.isBadFormatFromAndroid14Beta(this.payload);
+ }
+
public static PersistentData fromBytes(byte[] frpData) {
if (frpData == null || frpData.length == 0) {
return NONE;
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 65e7a00..66f862a 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -370,6 +370,15 @@
return result;
}
+ /**
+ * Returns true if the given serialized PasswordData begins with the value 2 as a short.
+ * This detects the "bad" (non-forwards-compatible) PasswordData format that was temporarily
+ * used during development of Android 14. For more details, see fromBytes() below.
+ */
+ public static boolean isBadFormatFromAndroid14Beta(byte[] data) {
+ return data != null && data.length >= 2 && data[0] == 0 && data[1] == 2;
+ }
+
public static PasswordData fromBytes(byte[] data) {
PasswordData result = new PasswordData();
ByteBuffer buffer = ByteBuffer.allocate(data.length);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9add537..a079875 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -82,6 +82,7 @@
import android.os.IInterface;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -2514,6 +2515,7 @@
* Propagate a wake event to the wallpaper engine(s).
*/
public void notifyWakingUp(int x, int y, @NonNull Bundle extras) {
+ checkCallerIsSystemOrSystemUi();
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
for (WallpaperData data : getActiveWallpapers()) {
@@ -2551,6 +2553,7 @@
* Propagate a sleep event to the wallpaper engine(s).
*/
public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) {
+ checkCallerIsSystemOrSystemUi();
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
for (WallpaperData data : getActiveWallpapers()) {
@@ -3684,6 +3687,14 @@
mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
}
+ /** Check that the caller is either system_server or systemui */
+ private void checkCallerIsSystemOrSystemUi() {
+ if (Binder.getCallingUid() != Process.myUid() && mContext.checkCallingPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE) != PERMISSION_GRANTED) {
+ throw new SecurityException("Access denied: only system processes can call this");
+ }
+ }
+
/**
* Certain user types do not support wallpapers (e.g. managed profiles). The check is
* implemented through through the OP_WRITE_WALLPAPER AppOp.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c763cfa..0ce794f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -382,11 +382,7 @@
// Add FLAG_ABOVE_TRANSIENT_LAUNCH to the tree of transient-hide tasks,
// so ChangeInfo#hasChanged() can return true to report the transition info.
for (int i = mChanges.size() - 1; i >= 0; --i) {
- final WindowContainer<?> wc = mChanges.keyAt(i);
- if (wc.asTaskFragment() == null && wc.asActivityRecord() == null) continue;
- if (isInTransientHide(wc)) {
- mChanges.valueAt(i).mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
- }
+ updateTransientFlags(mChanges.valueAt(i));
}
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
@@ -581,7 +577,9 @@
for (WindowContainer<?> curr = getAnimatableParent(wc);
curr != null && !mChanges.containsKey(curr);
curr = getAnimatableParent(curr)) {
- mChanges.put(curr, new ChangeInfo(curr));
+ final ChangeInfo info = new ChangeInfo(curr);
+ updateTransientFlags(info);
+ mChanges.put(curr, info);
if (isReadyGroup(curr)) {
mReadyTracker.addGroup(curr);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
@@ -600,6 +598,7 @@
ChangeInfo info = mChanges.get(wc);
if (info == null) {
info = new ChangeInfo(wc);
+ updateTransientFlags(info);
mChanges.put(wc, info);
}
mParticipants.add(wc);
@@ -615,6 +614,14 @@
}
}
+ private void updateTransientFlags(@NonNull ChangeInfo info) {
+ final WindowContainer<?> wc = info.mContainer;
+ // Only look at tasks, taskfragments, or activities
+ if (wc.asTaskFragment() == null && wc.asActivityRecord() == null) return;
+ if (!isInTransientHide(wc)) return;
+ info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
+ }
+
private void recordDisplay(DisplayContent dc) {
if (dc == null || mTargetDisplays.contains(dc)) return;
mTargetDisplays.add(dc);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 25bd9bc..be9f52e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -17,12 +17,14 @@
package com.android.server.biometrics.sensors.face.aidl;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,6 +43,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -82,6 +85,10 @@
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
FaceProvider mFaceProvider;
+ @Mock
+ BaseClientMonitor mClientMonitor;
+ @Mock
+ AidlSession mCurrentSession;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -161,6 +168,39 @@
assertNull(sensor.getSessionForUser(USER_ID));
}
+ @Test
+ public void onBinderDied_cancelNonInterruptableClient() {
+ mLooper.dispatchAll();
+
+ when(mCurrentSession.getUserId()).thenReturn(USER_ID);
+ when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID);
+ when(mClientMonitor.isInterruptable()).thenReturn(false);
+
+ final SensorProps sensorProps = new SensorProps();
+ sensorProps.commonProps = new CommonProps();
+ sensorProps.commonProps.sensorId = 1;
+ final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+ sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.sensorType, sensorProps.supportsDetectInteraction,
+ sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
+ final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
+ internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession);
+ mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
+ sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
+ USER_ID, mHalCallback);
+
+ mScheduler.scheduleClientMonitor(mClientMonitor);
+
+ assertNotNull(mScheduler.getCurrentClient());
+
+ sensor.onBinderDied();
+
+ verify(mClientMonitor).cancel();
+ assertNull(sensor.getSessionForUser(USER_ID));
+ assertNull(mScheduler.getCurrentClient());
+ }
+
private void verifyNotLocked() {
assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 0c13466..15d7601 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -17,17 +17,23 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.face.SensorProps;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -37,11 +43,13 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.junit.Before;
import org.junit.Test;
@@ -76,6 +84,14 @@
private BiometricContext mBiometricContext;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ FingerprintProvider mFingerprintProvider;
+ @Mock
+ GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ @Mock
+ private AidlSession mCurrentSession;
+ @Mock
+ private BaseClientMonitor mClientMonitor;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -130,6 +146,40 @@
verifyNotLocked();
}
+ @Test
+ public void onBinderDied_cancelNonInterruptableClient() {
+ mLooper.dispatchAll();
+
+ when(mCurrentSession.getUserId()).thenReturn(USER_ID);
+ when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID);
+ when(mClientMonitor.isInterruptable()).thenReturn(false);
+
+ final SensorProps sensorProps = new SensorProps();
+ sensorProps.commonProps = new CommonProps();
+ sensorProps.commonProps.sensorId = 1;
+ final FingerprintSensorPropertiesInternal internalProp = new
+ FingerprintSensorPropertiesInternal(
+ sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.sensorType, false /* resetLockoutRequiresHardwareAuthToken */);
+ final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext,
+ null /* handler */, internalProp, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession);
+ mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
+ sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
+ USER_ID, mHalCallback);
+
+ mScheduler.scheduleClientMonitor(mClientMonitor);
+
+ assertNotNull(mScheduler.getCurrentClient());
+
+ sensor.onBinderDied();
+
+ verify(mClientMonitor).cancel();
+ assertNull(sensor.getSessionForUser(USER_ID));
+ assertNull(mScheduler.getCurrentClient());
+ }
+
private void verifyNotLocked() {
assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
index 2b49b8a..a242cde 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
@@ -23,6 +23,8 @@
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
@@ -38,8 +40,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.nio.ByteBuffer;
-/** Test setting a lockscreen credential and then verify it under USER_FRP */
+/** Tests that involve the Factory Reset Protection (FRP) credential. */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -148,4 +151,68 @@
mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
.getResponseCode());
}
+
+ // The FRP block that gets written by the current version of Android must still be accepted by
+ // old versions of Android. This test tries to detect non-forward-compatible changes in
+ // PasswordData#toBytes(), which would break that.
+ @Test
+ public void testFrpBlock_isForwardsCompatible() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ PersistentData data = mStorage.readPersistentDataBlock();
+ ByteBuffer buffer = ByteBuffer.wrap(data.payload);
+
+ final int credentialType = buffer.getInt();
+ assertEquals(CREDENTIAL_TYPE_PIN, credentialType);
+
+ final byte scryptLogN = buffer.get();
+ assertTrue(scryptLogN >= 0);
+
+ final byte scryptLogR = buffer.get();
+ assertTrue(scryptLogR >= 0);
+
+ final byte scryptLogP = buffer.get();
+ assertTrue(scryptLogP >= 0);
+
+ final int saltLength = buffer.getInt();
+ assertTrue(saltLength > 0);
+ final byte[] salt = new byte[saltLength];
+ buffer.get(salt);
+
+ final int passwordHandleLength = buffer.getInt();
+ assertTrue(passwordHandleLength > 0);
+ final byte[] passwordHandle = new byte[passwordHandleLength];
+ buffer.get(passwordHandle);
+ }
+
+ @Test
+ public void testFrpBlock_inBadAndroid14FormatIsAutomaticallyFixed() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+
+ // Write a "bad" FRP block with PasswordData beginning with the bytes [0, 2].
+ byte[] badPasswordData = new byte[] {
+ 0, 2, /* version 2 */
+ 0, 3, /* CREDENTIAL_TYPE_PIN */
+ 11, /* scryptLogN */
+ 22, /* scryptLogR */
+ 33, /* scryptLogP */
+ 0, 0, 0, 5, /* salt.length */
+ 1, 2, -1, -2, 55, /* salt */
+ 0, 0, 0, 6, /* passwordHandle.length */
+ 2, 3, -2, -3, 44, 1, /* passwordHandle */
+ 0, 0, 0, 6, /* pinLength */
+ };
+ mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_GATEKEEPER, PRIMARY_USER_ID, 0,
+ badPasswordData);
+
+ // Execute the code that should fix the FRP block.
+ assertFalse(mStorage.getBoolean("migrated_frp2", false, 0));
+ mService.migrateOldDataAfterSystemReady();
+ assertTrue(mStorage.getBoolean("migrated_frp2", false, 0));
+
+ // Verify that the FRP block has been fixed.
+ PersistentData data = mStorage.readPersistentDataBlock();
+ assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
+ ByteBuffer buffer = ByteBuffer.wrap(data.payload);
+ assertEquals(CREDENTIAL_TYPE_PIN, buffer.getInt());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 067feae..ce0347d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -638,6 +638,7 @@
2, 3, -2, -3, 44, 1, /* passwordHandle */
0, 0, 0, 6, /* pinLength */
};
+ assertFalse(PasswordData.isBadFormatFromAndroid14Beta(serialized));
PasswordData deserialized = PasswordData.fromBytes(serialized);
assertEquals(11, deserialized.scryptLogN);
@@ -690,6 +691,7 @@
2, 3, -2, -3, 44, 1, /* passwordHandle */
0, 0, 0, 6, /* pinLength */
};
+ assertTrue(PasswordData.isBadFormatFromAndroid14Beta(serialized));
PasswordData deserialized = PasswordData.fromBytes(serialized);
assertEquals(11, deserialized.scryptLogN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 27677e1..0e627b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -88,7 +88,6 @@
private static final Multimap<Class<?>, String> KNOWN_BAD =
ImmutableMultimap.<Class<?>, String>builder()
.put(Notification.Builder.class, "setPublicVersion") // b/276294099
- .putAll(RemoteViews.class, "addView", "addStableView") // b/277740082
.put(RemoteViews.class, "setIcon") // b/281018094
.put(Notification.WearableExtender.class, "addAction") // TODO: b/281044385
.put(Person.Builder.class, "setUri") // TODO: b/281044385