diff options
211 files changed, 3797 insertions, 1057 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 4df648cb7009..9a16390baf10 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1298,7 +1298,7 @@ package android { field public static final int shortcutLongLabel = 16844074; // 0x101052a field public static final int shortcutShortLabel = 16844073; // 0x1010529 field public static final int shouldDisableView = 16843246; // 0x10101ee - field public static final int shouldUseDefaultUnfoldTransition; + field public static final int shouldUseDefaultDeviceStateChangeTransition; field public static final int showAsAction = 16843481; // 0x10102d9 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 @@ -6927,7 +6927,7 @@ package android.app { method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public CharSequence loadLabel(android.content.pm.PackageManager); method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager); - method public boolean shouldUseDefaultUnfoldTransition(); + method public boolean shouldUseDefaultDeviceStateChangeTransition(); method public boolean supportsMultipleDisplays(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR; @@ -39001,6 +39001,13 @@ package android.service.textservice { package android.service.voice { + public final class VisibleActivityInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.service.voice.VoiceInteractionSession.ActivityId getActivityId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.VisibleActivityInfo> CREATOR; + } + public class VoiceInteractionService extends android.app.Service { ctor public VoiceInteractionService(); method public int getDisabledShowContext(); @@ -39062,6 +39069,7 @@ package android.service.voice { method public void onTaskStarted(android.content.Intent, int); method public void onTrimMemory(int); method public final void performDirectAction(@NonNull android.app.DirectAction, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>); + method public final void registerVisibleActivityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback); method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>); method public void setContentView(android.view.View); method public void setDisabledShowContext(int); @@ -39071,6 +39079,7 @@ package android.service.voice { method public void show(android.os.Bundle, int); method public void startAssistantActivity(android.content.Intent); method public void startVoiceActivity(android.content.Intent); + method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback); field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4 @@ -39144,6 +39153,11 @@ package android.service.voice { method public boolean isActive(); } + public static interface VoiceInteractionSession.VisibleActivityCallback { + method public default void onInvisible(@NonNull android.service.voice.VoiceInteractionSession.ActivityId); + method public default void onVisible(@NonNull android.service.voice.VisibleActivityInfo); + } + public abstract class VoiceInteractionSessionService extends android.app.Service { ctor public VoiceInteractionSessionService(); method public android.os.IBinder onBind(android.content.Intent); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2ecf088fb5d0..d484100373af 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2391,6 +2391,10 @@ package android.service.voice { method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]); } + public final class VisibleActivityInfo implements android.os.Parcelable { + ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder); + } + } package android.service.watchdog { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 70bb132c7668..f8c8aa32a26e 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -652,4 +652,23 @@ public abstract class ActivityManagerInternal { public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options); + + /** + * Sets the provider to communicate between voice interaction manager service and + * ActivityManagerService. + */ + public abstract void setVoiceInteractionManagerProvider( + @Nullable VoiceInteractionManagerProvider provider); + + /** + * Provides the interface to communicate between voice interaction manager service and + * ActivityManagerService. + */ + public interface VoiceInteractionManagerProvider { + /** + * Notifies the service when a high-level activity event has been changed, for example, + * an activity was resumed or stopped. + */ + void notifyActivityEventChanged(); + } } diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index 4dff4e0e84d6..c552cb62749b 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -81,7 +81,7 @@ public final class WallpaperInfo implements Parcelable { final int mContextDescriptionResource; final boolean mShowMetadataInPreview; final boolean mSupportsAmbientMode; - final boolean mShouldUseDefaultUnfoldTransition; + final boolean mShouldUseDefaultDeviceStateChangeTransition; final String mSettingsSliceUri; final boolean mSupportMultipleDisplays; @@ -146,9 +146,9 @@ public final class WallpaperInfo implements Parcelable { mSupportsAmbientMode = sa.getBoolean( com.android.internal.R.styleable.Wallpaper_supportsAmbientMode, false); - mShouldUseDefaultUnfoldTransition = sa.getBoolean( - com.android.internal.R.styleable.Wallpaper_shouldUseDefaultUnfoldTransition, - true); + mShouldUseDefaultDeviceStateChangeTransition = sa.getBoolean( + com.android.internal.R.styleable + .Wallpaper_shouldUseDefaultDeviceStateChangeTransition, true); mSettingsSliceUri = sa.getString( com.android.internal.R.styleable.Wallpaper_settingsSliceUri); mSupportMultipleDisplays = sa.getBoolean( @@ -175,7 +175,7 @@ public final class WallpaperInfo implements Parcelable { mSupportsAmbientMode = source.readInt() != 0; mSettingsSliceUri = source.readString(); mSupportMultipleDisplays = source.readInt() != 0; - mShouldUseDefaultUnfoldTransition = source.readInt() != 0; + mShouldUseDefaultDeviceStateChangeTransition = source.readInt() != 0; mService = ResolveInfo.CREATOR.createFromParcel(source); } @@ -399,23 +399,25 @@ public final class WallpaperInfo implements Parcelable { } /** - * Returns whether this wallpaper should receive default zooming updates when unfolding. - * If set to false the wallpaper will not receive zoom events when folding or unfolding - * a foldable device, so it can implement its own unfold transition. + * Returns whether this wallpaper should receive default zooming updates when the device + * changes its state (e.g. when folding or unfolding a foldable device). + * If set to false the wallpaper will not receive zoom events when changing the device state, + * so it can implement its own transition instead. * <p> * This corresponds to the value {@link - * android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition} in the XML description - * of the wallpaper. + * android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition} in the + * XML description of the wallpaper. * <p> * The default value is {@code true}. * - * @see android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition - * @return {@code true} if wallpaper should receive default fold/unfold transition updates + * @see android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition + * @return {@code true} if wallpaper should receive default device state change + * transition updates * - * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition + * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition */ - public boolean shouldUseDefaultUnfoldTransition() { - return mShouldUseDefaultUnfoldTransition; + public boolean shouldUseDefaultDeviceStateChangeTransition() { + return mShouldUseDefaultDeviceStateChangeTransition; } public void dump(Printer pw, String prefix) { @@ -448,7 +450,7 @@ public final class WallpaperInfo implements Parcelable { dest.writeInt(mSupportsAmbientMode ? 1 : 0); dest.writeString(mSettingsSliceUri); dest.writeInt(mSupportMultipleDisplays ? 1 : 0); - dest.writeInt(mShouldUseDefaultUnfoldTransition ? 1 : 0); + dest.writeInt(mShouldUseDefaultDeviceStateChangeTransition ? 1 : 0); mService.writeToParcel(dest, flags); } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 73961ff373dd..d8f20391098c 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1288,6 +1288,23 @@ public final class DisplayManager { */ String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS = "fixed_refresh_rate_high_ambient_brightness_thresholds"; + + /** + * Key for refresh rate when the device is in high brightness mode for sunlight visility. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_defaultRefreshRateInHbmSunlight + */ + String KEY_REFRESH_RATE_IN_HBM_SUNLIGHT = "refresh_rate_in_hbm_sunlight"; + + /** + * Key for refresh rate when the device is in high brightness mode for HDR. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_defaultRefreshRateInHbmHdr + */ + String KEY_REFRESH_RATE_IN_HBM_HDR = "refresh_rate_in_hbm_hdr"; + /** * Key for default peak refresh rate * diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl index c142a53e047e..59f1e8eed89c 100644 --- a/core/java/android/service/voice/IVoiceInteractionSession.aidl +++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl @@ -22,6 +22,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.os.IBinder; +import android.service.voice.VisibleActivityInfo; import com.android.internal.app.IVoiceInteractionSessionShowCallback; @@ -39,4 +40,5 @@ oneway interface IVoiceInteractionSession { void closeSystemDialogs(); void onLockscreenShown(); void destroy(); + void updateVisibleActivityInfo(in VisibleActivityInfo visibleActivityInfo, int type); } diff --git a/core/java/android/service/voice/VisibleActivityInfo.aidl b/core/java/android/service/voice/VisibleActivityInfo.aidl new file mode 100644 index 000000000000..34bd57c15456 --- /dev/null +++ b/core/java/android/service/voice/VisibleActivityInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.voice; + +parcelable VisibleActivityInfo; diff --git a/core/java/android/service/voice/VisibleActivityInfo.java b/core/java/android/service/voice/VisibleActivityInfo.java new file mode 100644 index 000000000000..139544c76a50 --- /dev/null +++ b/core/java/android/service/voice/VisibleActivityInfo.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.voice; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; +import android.os.CancellationSignal; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * The class is used to represent a visible activity information. The system provides this to + * services that need to know {@link android.service.voice.VoiceInteractionSession.ActivityId}. + */ +@DataClass( + genConstructor = false, + genEqualsHashCode = true, + genHiddenConstDefs = false, + genGetters = false, + genToString = true +) +public final class VisibleActivityInfo implements Parcelable { + + /** + * Indicates that it is a new visible activity. + * + * @hide + */ + public static final int TYPE_ACTIVITY_ADDED = 1; + + /** + * Indicates that it has become a invisible activity. + * + * @hide + */ + public static final int TYPE_ACTIVITY_REMOVED = 2; + + /** + * The identifier of the task this activity is in. + */ + private final int mTaskId; + + /** + * Token for targeting this activity for assist purposes. + */ + @NonNull + private final IBinder mAssistToken; + + /** @hide */ + @TestApi + public VisibleActivityInfo( + int taskId, + @NonNull IBinder assistToken) { + Objects.requireNonNull(assistToken); + mTaskId = taskId; + mAssistToken = assistToken; + } + + /** + * Returns the {@link android.service.voice.VoiceInteractionSession.ActivityId} of this + * visible activity which can be used to interact with an activity, for example through + * {@link VoiceInteractionSession#requestDirectActions(VoiceInteractionSession.ActivityId, + * CancellationSignal, Executor, Consumer)}. + */ + public @NonNull VoiceInteractionSession.ActivityId getActivityId() { + return new VoiceInteractionSession.ActivityId(mTaskId, mAssistToken); + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "VisibleActivityInfo { " + + "taskId = " + mTaskId + ", " + + "assistToken = " + mAssistToken + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(VisibleActivityInfo other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + VisibleActivityInfo that = (VisibleActivityInfo) o; + //noinspection PointlessBooleanExpression + return true + && mTaskId == that.mTaskId + && Objects.equals(mAssistToken, that.mAssistToken); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mTaskId; + _hash = 31 * _hash + Objects.hashCode(mAssistToken); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mTaskId); + dest.writeStrongBinder(mAssistToken); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ VisibleActivityInfo(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int taskId = in.readInt(); + IBinder assistToken = (IBinder) in.readStrongBinder(); + + this.mTaskId = taskId; + this.mAssistToken = assistToken; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAssistToken); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<VisibleActivityInfo> CREATOR + = new Parcelable.Creator<VisibleActivityInfo>() { + @Override + public VisibleActivityInfo[] newArray(int size) { + return new VisibleActivityInfo[size]; + } + + @Override + public VisibleActivityInfo createFromParcel(@NonNull Parcel in) { + return new VisibleActivityInfo(in); + } + }; + + @DataClass.Generated( + time = 1632383555284L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java", + inputSignatures = "public static final int TYPE_ACTIVITY_ADDED\npublic static final int TYPE_ACTIVITY_REMOVED\nprivate final int mTaskId\nprivate final @android.annotation.NonNull android.os.IBinder mAssistToken\npublic @android.annotation.NonNull android.service.voice.VoiceInteractionSession.ActivityId getActivityId()\nclass VisibleActivityInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true, genHiddenConstDefs=false, genGetters=false, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java index c048286545c3..c80640910bc2 100644 --- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java +++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java @@ -22,7 +22,6 @@ import android.os.IBinder; import com.android.internal.annotations.Immutable; - /** * @hide * Private interface to the VoiceInteractionManagerService for use by ActivityManagerService. diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 725e20f2a74d..9db856a8762a 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -74,9 +74,11 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -177,6 +179,10 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall ICancellationSignal mKillCallback; + private final Map<VisibleActivityCallback, Executor> mVisibleActivityCallbacks = + new ArrayMap<>(); + private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>(); + final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { @Override public IVoiceInteractorRequest startConfirmation(String callingPackage, @@ -352,6 +358,13 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall public void destroy() { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY)); } + + @Override + public void updateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) { + mHandlerCaller.sendMessage( + mHandlerCaller.obtainMessageIO(MSG_UPDATE_VISIBLE_ACTIVITY_INFO, type, + visibleActivityInfo)); + } }; /** @@ -843,6 +856,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall static final int MSG_SHOW = 106; static final int MSG_HIDE = 107; static final int MSG_ON_LOCKSCREEN_SHOWN = 108; + static final int MSG_UPDATE_VISIBLE_ACTIVITY_INFO = 109; + static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110; + static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111; class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback { @Override @@ -928,6 +944,27 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall if (DEBUG) Log.d(TAG, "onLockscreenShown"); onLockscreenShown(); break; + case MSG_UPDATE_VISIBLE_ACTIVITY_INFO: + if (DEBUG) { + Log.d(TAG, "doUpdateVisibleActivityInfo: visibleActivityInfo=" + msg.obj + + " type=" + msg.arg1); + } + doUpdateVisibleActivityInfo((VisibleActivityInfo) msg.obj, msg.arg1); + break; + case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK: + if (DEBUG) { + Log.d(TAG, "doRegisterVisibleActivityCallback"); + } + args = (SomeArgs) msg.obj; + doRegisterVisibleActivityCallback((Executor) args.arg1, + (VisibleActivityCallback) args.arg2); + break; + case MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK: + if (DEBUG) { + Log.d(TAG, "doUnregisterVisibleActivityCallback"); + } + doUnregisterVisibleActivityCallback((VisibleActivityCallback) msg.obj); + break; } if (args != null) { args.recycle(); @@ -1122,6 +1159,86 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } } + private void doUpdateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) { + + if (mVisibleActivityCallbacks.isEmpty()) { + return; + } + + switch (type) { + case VisibleActivityInfo.TYPE_ACTIVITY_ADDED: + informVisibleActivityChanged(visibleActivityInfo, type); + mVisibleActivityInfos.add(visibleActivityInfo); + break; + case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED: + informVisibleActivityChanged(visibleActivityInfo, type); + mVisibleActivityInfos.remove(visibleActivityInfo); + break; + } + } + + private void doRegisterVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull VisibleActivityCallback callback) { + if (mVisibleActivityCallbacks.containsKey(callback)) { + if (DEBUG) { + Log.d(TAG, "doRegisterVisibleActivityCallback: callback has registered"); + } + return; + } + + int preCallbackCount = mVisibleActivityCallbacks.size(); + mVisibleActivityCallbacks.put(callback, executor); + + if (preCallbackCount == 0) { + try { + mSystemService.startListeningVisibleActivityChanged(mToken); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } else { + for (int i = 0; i < mVisibleActivityInfos.size(); i++) { + final VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfos.get(i); + executor.execute(() -> callback.onVisible(visibleActivityInfo)); + } + } + } + + private void doUnregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) { + mVisibleActivityCallbacks.remove(callback); + + if (mVisibleActivityCallbacks.size() == 0) { + mVisibleActivityInfos.clear(); + try { + mSystemService.stopListeningVisibleActivityChanged(mToken); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + private void informVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) { + for (Map.Entry<VisibleActivityCallback, Executor> e : + mVisibleActivityCallbacks.entrySet()) { + final Executor executor = e.getValue(); + final VisibleActivityCallback visibleActivityCallback = e.getKey(); + + switch (type) { + case VisibleActivityInfo.TYPE_ACTIVITY_ADDED: + Binder.withCleanCallingIdentity(() -> { + executor.execute( + () -> visibleActivityCallback.onVisible(visibleActivityInfo)); + }); + break; + case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED: + Binder.withCleanCallingIdentity(() -> { + executor.execute(() -> visibleActivityCallback.onInvisible( + visibleActivityInfo.getActivityId())); + }); + break; + } + } + } + void ensureWindowCreated() { if (mInitialized) { return; @@ -1926,6 +2043,45 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** + * Registers a callback that will be notified when visible activities have been changed. + * + * @param executor The handler to receive the callback. + * @param callback The callback to receive the response. + * + * @throws IllegalStateException if calling this method before onCreate(). + */ + public final void registerVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull VisibleActivityCallback callback) { + if (DEBUG) { + Log.d(TAG, "registerVisibleActivityCallback"); + } + if (mToken == null) { + throw new IllegalStateException("Can't call before onCreate()"); + } + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + mHandlerCaller.sendMessage( + mHandlerCaller.obtainMessageOO(MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK, executor, + callback)); + } + + /** + * Unregisters the callback. + * + * @param callback The callback to receive the response. + */ + public final void unregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) { + if (DEBUG) { + Log.d(TAG, "unregisterVisibleActivityCallback"); + } + Objects.requireNonNull(callback); + + mHandlerCaller.sendMessage( + mHandlerCaller.obtainMessageO(MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK, callback)); + } + + /** * Print the Service's state into the given stream. This gets invoked by * {@link VoiceInteractionSessionService} when its Service * {@link android.app.Service#dump} method is called. @@ -1975,6 +2131,17 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** + * Callback interface for receiving visible activity changes used for assistant usage. + */ + public interface VisibleActivityCallback { + /** Callback to inform that an activity has become visible. */ + default void onVisible(@NonNull VisibleActivityInfo activityInfo) {} + + /** Callback to inform that a visible activity has gone. */ + default void onInvisible(@NonNull ActivityId activityId) {} + } + + /** * Represents assist state captured when this session was started. * It contains the various assist data objects and a reference to * the source activity. diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index 971e16185815..aecde4415117 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -165,6 +165,10 @@ public final class MathUtils { return start + (stop - start) * amount; } + public static float lerp(int start, int stop, float amount) { + return lerp((float) start, (float) stop, amount); + } + /** * Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link * #lerp}{@code (a, b, s)} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index afdb755179a6..b729c9fb01b1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20889,13 +20889,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED; } + notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); + mAttachInfo = null; if (mOverlay != null) { mOverlay.getOverlayView().dispatchDetachedFromWindow(); } notifyEnterOrExitForAutoFillIfNeeded(false); - notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 20888f75b55f..c45b27a8a2c2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2864,8 +2864,13 @@ public final class ViewRootImpl implements ViewParent, } } + final boolean surfaceControlChanged = + (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) + == RELAYOUT_RES_SURFACE_CHANGED; + if (mSurfaceControl.isValid()) { - updateOpacity(mWindowAttributes, dragResizing); + updateOpacity(mWindowAttributes, dragResizing, + surfaceControlChanged /*forceUpdate */); } if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString() @@ -2900,9 +2905,7 @@ public final class ViewRootImpl implements ViewParent, // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new // SurfaceControl. surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId() - || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) - == RELAYOUT_RES_SURFACE_CHANGED) - && mSurface.isValid(); + || surfaceControlChanged) && mSurface.isValid(); if (surfaceReplaced) { mSurfaceSequenceId++; } @@ -7895,7 +7898,8 @@ public final class ViewRootImpl implements ViewParent, return relayoutResult; } - private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing) { + private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing, + boolean forceUpdate) { boolean opaque = false; if (!PixelFormat.formatHasAlpha(params.format) @@ -7911,7 +7915,7 @@ public final class ViewRootImpl implements ViewParent, opaque = true; } - if (mIsSurfaceOpaque == opaque) { + if (!forceUpdate && mIsSurfaceOpaque == opaque) { return; } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 81e2f3ca98ec..b1d618eff40a 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -258,6 +258,8 @@ public abstract class Animation implements Cloneable { setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL)); + setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0)); + setDetachWallpaper( a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); setShowWallpaper( @@ -630,15 +632,16 @@ public abstract class Animation implements Cloneable { } /** - * Set background behind an animation. + * Set background behind animation. * - * @param bg The background color. If 0, no background. + * @param bg The background color. If 0, no background. Currently must + * be black, with any desired alpha level. * * @deprecated None of window animations are running with background color. */ @Deprecated public void setBackgroundColor(@ColorInt int bg) { - mBackgroundColor = bg; + // The background color is not needed any more, do nothing. } /** @@ -800,7 +803,7 @@ public abstract class Animation implements Cloneable { @Deprecated @ColorInt public int getBackgroundColor() { - return mBackgroundColor; + return 0; } /** diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 42d77cd09689..e6f103e6d53b 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2154,6 +2154,7 @@ public final class InputMethodManager { * @hide */ public boolean requestImeShow(IBinder windowToken) { + checkFocus(); synchronized (mH) { final View servedView = getServedViewLocked(); if (servedView == null || servedView.getWindowToken() != windowToken) { diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index c8a4425409e8..998526209c72 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -272,4 +272,14 @@ interface IVoiceInteractionManagerService { void triggerHardwareRecognitionEventForTest( in SoundTrigger.KeyphraseRecognitionEvent event, in IHotwordRecognitionStatusCallback callback); + + /** + * Starts to listen the status of visible activity. + */ + void startListeningVisibleActivityChanged(in IBinder token); + + /** + * Stops to listen the status of visible activity. + */ + void stopListeningVisibleActivityChanged(in IBinder token); } diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 776a70545df4..3915b0e01e7f 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -215,6 +215,9 @@ public class WifiPowerCalculator extends PowerCalculator { + "ms tx=" + txTime + "ms power=" + formatCharge( powerDurationAndTraffic.powerMah)); } + } else { + powerDurationAndTraffic.durationMs = 0; + powerDurationAndTraffic.powerMah = 0; } } else { final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 53eb2b7e87bf..7b80f606c99e 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2010,7 +2010,7 @@ <string name="app_category_image" msgid="7307840291864213007">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਚਿੱਤਰ"</string> <string name="app_category_social" msgid="2278269325488344054">"ਸਮਾਜਕ ਅਤੇ ਸੰਚਾਰ"</string> <string name="app_category_news" msgid="1172762719574964544">"ਖਬਰਾਂ ਅਤੇ ਰਸਾਲੇ"</string> - <string name="app_category_maps" msgid="6395725487922533156">"ਨਕਸ਼ੇ ਅਤੇ ਆਵਾਗੌਣ"</string> + <string name="app_category_maps" msgid="6395725487922533156">"Maps ਅਤੇ ਨੈਵੀਗੇਸ਼ਨ"</string> <string name="app_category_productivity" msgid="1844422703029557883">"ਉਤਪਾਦਕਤਾ"</string> <string name="app_category_accessibility" msgid="6643521607848547683">"ਪਹੁੰਚਯੋਗਤਾ"</string> <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ਡੀਵਾਈਸ ਸਟੋਰੇਜ"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 680fbd2b7a24..a75ef3d9487c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1043,7 +1043,7 @@ <string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string> <string name="search_hint" msgid="455364685740251925">"సెర్చ్ చేయండి..."</string> <string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string> - <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string> + <string name="searchview_description_query" msgid="7430242366971716338">"సెర్చ్ క్వెరీ"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string> <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ సెర్చ్"</string> @@ -1412,7 +1412,7 @@ <string name="ext_media_new_notification_message" msgid="6095403121990786986">"సెటప్ చేయడానికి నొక్కండి"</string> <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"సెటప్ చేయడానికి ఎంచుకోండి"</string> <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"మీరు పరికరాన్ని తిరిగి ఫార్మాట్ చేయాల్సి ఉంటుంది. తొలగించడానికి ట్యాప్ చేయండి"</string> - <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు మరియు మీడియాను బదిలీ చేయడానికి"</string> + <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు, మీడియాను బదిలీ చేయడానికి"</string> <string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"మీడియా ఫైల్స్ను బ్రౌజ్ చేయండి"</string> <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"<xliff:g id="NAME">%s</xliff:g>తో సమస్య ఉంది"</string> <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> పని చేయటం లేదు"</string> @@ -1524,7 +1524,7 @@ <string name="progress_erasing" msgid="6891435992721028004">"షేర్ చేసిన నిల్వను తొలగిస్తోంది…"</string> <string name="share" msgid="4157615043345227321">"షేర్"</string> <string name="find" msgid="5015737188624767706">"కనుగొనండి"</string> - <string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string> + <string name="websearch" msgid="5624340204512793290">"వెబ్ సెర్చ్"</string> <string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string> <string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string> <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి లొకేషన్ రిక్వెస్ట్"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 9d90dd480203..53396407904a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6822,8 +6822,9 @@ content for the duration of the animation. --> <enum name="bottom" value="-1" /> </attr> - <!-- Special background behind animation. Only for use with task animations. - If 0, the default, there is no background. --> + <!-- Special background behind animation. Only for use with window + animations. Can only be a color, and only black. If 0, the + default, there is no background. --> <attr name="background" /> <!-- Special option for window animations: if this window is on top of a wallpaper, don't animate the wallpaper with it. --> @@ -7736,7 +7737,7 @@ <!-- Name of a method on the Context used to inflate the menu that will be called when the item is clicked. - {@deprecated Menu actually traverses the Context hierarchy looking for the + {@deprecated Menu actually traverses the Context hierarchy looking for the relevant method, which is fragile (an intermediate ContextWrapper adding a same-named method would change behavior) and restricts bytecode optimizers such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} --> @@ -8370,16 +8371,18 @@ @hide @SystemApi --> <attr name="supportsAmbientMode" format="boolean" /> - <!-- Indicates that this wallpaper service should receive zoom updates when unfolding. + <!-- Indicates that this wallpaper service should receive zoom transition updates when + changing the device state (e.g. when folding or unfolding a foldable device). When this value is set to true {@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates - when folding or unfolding a foldable device. Wallpapers receive zoom updates using + before or after changing the device state. Wallpapers receive zoom updates using {@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and - zoom rendering should be handled manually. Default value is true. - When set to false wallpapers can implement custom folding/unfolding behavior - by listening to {@link android.hardware.Sensor#TYPE_HINGE_ANGLE}. - Corresponds to {@link android.app.WallpaperInfo#shouldUseDefaultUnfoldTransition()} --> - <attr name="shouldUseDefaultUnfoldTransition" format="boolean" /> + zoom rendering should be handled manually. Zoom updates are delivered only when + {@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed. + Default value is true. + Corresponds to + {@link android.app.WallpaperInfo#shouldUseDefaultDeviceStateChangeTransition()} --> + <attr name="shouldUseDefaultDeviceStateChangeTransition" format="boolean" /> <!-- Uri that specifies a settings Slice for this wallpaper. --> <attr name="settingsSliceUri" format="string"/> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cbe0eef6408b..689d75b9cfea 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4533,6 +4533,13 @@ If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> <integer name="config_fixedRefreshRateInHighZone">0</integer> + <!-- Default refresh rate while the device has high brightness mode enabled for Sunlight. + This value overrides values from DisplayDeviceConfig --> + <integer name="config_defaultRefreshRateInHbmSunlight">0</integer> + + <!-- Default refresh rate while the device has high brightness mode enabled for HDR. --> + <integer name="config_defaultRefreshRateInHbmHdr">0</integer> + <!-- The type of the light sensor to be used by the display framework for things like auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. --> <string name="config_displayLightSensorType" translatable="false" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7d489049d112..e17daf09e4da 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3221,7 +3221,7 @@ <eat-comment /> <staging-public-group type="attr" first-id="0x01ff0000"> - <public name="shouldUseDefaultUnfoldTransition" /> + <public name="shouldUseDefaultDeviceStateChangeTransition" /> </staging-public-group> <staging-public-group type="id" first-id="0x01fe0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fbc629e2dc81..4e8d915ca295 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3964,6 +3964,8 @@ <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> + <java-symbol type="integer" name="config_defaultRefreshRateInHbmSunlight" /> + <java-symbol type="integer" name="config_defaultRefreshRateInHbmHdr" /> <!-- For fixed refresh rate displays in high brightness--> <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java index 1e64782b161f..11a79b2f4a27 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java @@ -121,9 +121,11 @@ class TaskFragmentAnimationSpec { * the second one is for the end leash. */ Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) { + // Both start bounds and end bounds are in screen coordinates. We will post translate + // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate final Rect startBounds = target.startBounds; final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); - final Rect endBounds = target.localBounds; + final Rect endBounds = target.screenSpaceBounds; float scaleX = ((float) startBounds.width()) / endBounds.width(); float scaleY = ((float) startBounds.height()) / endBounds.height(); // Start leash is a child of the end leash. Reverse the scale so that the start leash won't diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index e511bffad247..3ab062499845 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -43,6 +43,7 @@ import android.util.Slog; import android.view.Surface; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -64,7 +65,8 @@ import java.io.PrintWriter; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements RemoteCallable<OneHandedController> { +public class OneHandedController implements RemoteCallable<OneHandedController>, + DisplayChangeController.OnDisplayChangingListener { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -106,19 +108,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; private OneHandedUiEventLogger mOneHandedUiEventLogger; - /** - * Handle rotation based on OnDisplayChangingListener callback - */ - private final DisplayChangeController.OnDisplayChangingListener mRotationController = - (display, fromRotation, toRotation, wct) -> { - if (!isInitialized()) { - return; - } - mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct); - mOneHandedUiEventLogger.writeEvent( - OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); - }; - private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = new DisplayController.OnDisplaysChangedListener() { @Override @@ -296,7 +285,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> getObserver(this::onSwipeToNotificationEnabledChanged); mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged); - mDisplayController.addDisplayChangingController(mRotationController); + mDisplayController.addDisplayChangingController(this); setupCallback(); registerSettingObservers(mUserId); setupTimeoutListener(); @@ -745,6 +734,27 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } /** + * Handles rotation based on OnDisplayChangingListener callback + */ + @Override + public void onRotateDisplay(int displayId, int fromRotation, int toRotation, + WindowContainerTransaction wct) { + if (!isInitialized()) { + return; + } + + if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver(), + mUserId) || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver(), mUserId)) { + return; + } + + mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct); + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); + } + + /** * The interface for calls from outside the Shell, within the host process. */ @ExternalThread diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index c2bbd9e99bac..1b2f4768110b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -16,8 +16,6 @@ package com.android.wm.shell.onehanded; -import static android.os.UserHandle.myUserId; - import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT; import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER; @@ -186,20 +184,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { if (mDisplayLayout.rotation() == toRotation) { return; } - - if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(context.getContentResolver(), - myUserId())) { - return; - } - mDisplayLayout.rotateTo(context.getResources(), toRotation); updateDisplayBounds(); - - if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - context.getContentResolver(), myUserId())) { - // If current settings is swipe notification, skip finishOffset. - return; - } finishOffset(0, TRANSITION_DIRECTION_EXIT); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 911fe0753845..0a3a84923053 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -42,6 +42,7 @@ import android.util.ArrayMap; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; @@ -332,6 +333,58 @@ public class OneHandedControllerTest extends OneHandedTestCase { } @Test + public void testOneHandedEnabledRotation90ShouldHandleRotate() { + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + false); + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, handlerWCT); + + verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext), + eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); + } + + @Test + public void testOneHandedDisabledRotation90ShouldNotHandleRotate() { + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(false); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + false); + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, handlerWCT); + + verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext), + eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); + } + + @Test + public void testSwipeToNotificationEnabledRotation90ShouldNotHandleRotate() { + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + true); + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, handlerWCT); + + verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext), + eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); + } + + @Test + public void testSwipeToNotificationDisabledRotation90ShouldHandleRotate() { + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(true); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + false); + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, handlerWCT); + + verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext), + eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); + } + + @Test public void testStateActive_shortcutRequestActivate_skipActions() { when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 2c81c971f7a6..48145d2331ee 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -243,13 +243,12 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // the corresponding SkImageFilter each time. // See b/193145089 and b/197263715 if (!Properties::enableRenderEffectCache) { + snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot(); if (imageFilter) { auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter, subset, clipBounds.roundOut(), &srcBounds, &offset); - } else { - snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot(); } } else { const auto snapshotResult = renderNode->updateSnapshotIfRequired( diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml index cf0e0f6b506e..50e6f3b7dac5 100644 --- a/packages/PrintSpooler/res/values-te/strings.xml +++ b/packages/PrintSpooler/res/values-te/strings.xml @@ -50,8 +50,8 @@ <string name="search" msgid="5421724265322228497">"సెర్చ్"</string> <string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్లు"</string> <string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string> - <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"శోధన పెట్టె చూపబడింది"</string> - <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"శోధన పెట్టె దాచబడింది"</string> + <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"సెర్చ్ బాక్స్ చూపబడింది"</string> + <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"సెర్చ్ బాక్స్ దాచబడింది"</string> <string name="print_add_printer" msgid="1088656468360653455">"ప్రింటర్ను జోడించు"</string> <string name="print_select_printer" msgid="7388760939873368698">"ప్రింటర్ను ఎంచుకోండి"</string> <string name="print_forget_printer" msgid="5035287497291910766">"ప్రింటర్ను విస్మరించు"</string> diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index 8216edf872f3..fe7988f7e500 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -43,6 +43,7 @@ public class FooterPreference extends Preference { View.OnClickListener mLearnMoreListener; private CharSequence mContentDescription; private CharSequence mLearnMoreContentDescription; + private FooterLearnMoreSpan mLearnMoreSpan; public FooterPreference(Context context, AttributeSet attrs) { super(context, attrs, R.attr.footerPreferenceStyle); @@ -68,7 +69,11 @@ public class FooterPreference extends Preference { if (learnMore != null && mLearnMoreListener != null) { learnMore.setVisibility(View.VISIBLE); SpannableString learnMoreText = new SpannableString(learnMore.getText()); - learnMoreText.setSpan(new FooterLearnMoreSpan(mLearnMoreListener), 0, + if (mLearnMoreSpan != null) { + learnMoreText.removeSpan(mLearnMoreSpan); + } + mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener); + learnMoreText.setSpan(mLearnMoreSpan, 0, learnMoreText.length(), 0); learnMore.setText(learnMoreText); if (!TextUtils.isEmpty(mLearnMoreContentDescription)) { diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml index 54145d60fb32..eecb4bff16ae 100644 --- a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml +++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml @@ -34,17 +34,21 @@ android:orientation="vertical"> <ImageView + android:id="@+id/background_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:scaleType="centerInside" + android:layout_gravity="center" + android:adjustViewBounds="true" android:src="@drawable/protection_background"/> <com.airbnb.lottie.LottieAnimationView android:id="@+id/lottie_view" - android:adjustViewBounds="true" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" /> + android:layout_gravity="center" + android:maxWidth="@dimen/settingslib_illustration_width" + android:maxHeight="@dimen/settingslib_illustration_height" + android:adjustViewBounds="true"/> <FrameLayout android:id="@+id/middleground_layout" diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index 1f80a3e04093..468a97630e19 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -17,6 +17,7 @@ package com.android.settingslib.widget; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Animatable; import android.graphics.drawable.Animatable2; @@ -50,7 +51,9 @@ public class IllustrationPreference extends Preference { private static final String TAG = "IllustrationPreference"; private static final boolean IS_ENABLED_LOTTIE_ADAPTIVE_COLOR = false; + private static final int SIZE_UNSPECIFIED = -1; + private int mMaxHeight = SIZE_UNSPECIFIED; private int mImageResId; private boolean mIsAutoScale; private Uri mImageUri; @@ -98,6 +101,8 @@ public class IllustrationPreference extends Preference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); + final ImageView backgroundView = + (ImageView) holder.findViewById(R.id.background_view); final FrameLayout middleGroundLayout = (FrameLayout) holder.findViewById(R.id.middleground_layout); final LottieAnimationView illustrationView = @@ -115,6 +120,7 @@ public class IllustrationPreference extends Preference { illustrationFrame.setLayoutParams(lp); handleImageWithAnimation(illustrationView); + handleImageFrameMaxHeight(backgroundView, illustrationView); if (mIsAutoScale) { illustrationView.setScaleType(mIsAutoScale @@ -220,6 +226,19 @@ public class IllustrationPreference extends Preference { return mImageUri; } + /** + * Sets the maximum height of the views, still use the specific one if the maximum height was + * larger than the specific height from XML. + * + * @param maxHeight the maximum height of the frame views in terms of pixels. + */ + public void setMaxHeight(int maxHeight) { + if (maxHeight != mMaxHeight) { + mMaxHeight = maxHeight; + notifyChanged(); + } + } + private void resetImageResourceCache() { mImageDrawable = null; mImageUri = null; @@ -274,6 +293,23 @@ public class IllustrationPreference extends Preference { } } + private void handleImageFrameMaxHeight(ImageView backgroundView, ImageView illustrationView) { + if (mMaxHeight == SIZE_UNSPECIFIED) { + return; + } + + final Resources res = backgroundView.getResources(); + final int frameWidth = res.getDimensionPixelSize(R.dimen.settingslib_illustration_width); + final int frameHeight = res.getDimensionPixelSize(R.dimen.settingslib_illustration_height); + final int restrictedMaxHeight = Math.min(mMaxHeight, frameHeight); + backgroundView.setMaxHeight(restrictedMaxHeight); + illustrationView.setMaxHeight(restrictedMaxHeight); + + // Ensures the illustration view size is smaller than or equal to the background view size. + final float aspectRatio = (float) frameWidth / frameHeight; + illustrationView.setMaxWidth((int) (restrictedMaxHeight * aspectRatio)); + } + private void startAnimation(Drawable drawable) { if (!(drawable instanceof Animatable)) { return; diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java index a0c8663a8271..ea5105bd9e0d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java @@ -319,23 +319,11 @@ public class SettingsInjector { @Override public boolean onPreferenceClick(Preference preference) { - // Activity to start if they click on the preference. Must start in new task to ensure - // that "android.settings.LOCATION_SOURCE_SETTINGS" brings user back to - // Settings > Location. + // Activity to start if they click on the preference. Intent settingIntent = new Intent(); settingIntent.setClassName(mInfo.packageName, mInfo.settingsActivity); + // No flags set to ensure the activity is launched within the same settings task. logPreferenceClick(settingIntent); - // Sometimes the user may navigate back to "Settings" and launch another different - // injected setting after one injected setting has been launched. - // - // FLAG_ACTIVITY_CLEAR_TOP allows multiple Activities to stack on each other. When - // "back" button is clicked, the user will navigate through all the injected settings - // launched before. Such behavior could be quite confusing sometimes. - // - // In order to avoid such confusion, we use FLAG_ACTIVITY_CLEAR_TASK, which always clear - // up all existing injected settings and make sure that "back" button always brings the - // user back to "Settings" directly. - settingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mContext.startActivityAsUser(settingIntent, mInfo.mUserHandle); return true; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java index 9e3312ae2ddf..29549d9a7fa7 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java @@ -55,6 +55,7 @@ public class IllustrationPreferenceTest { @Mock private ViewGroup mRootView; private Uri mImageUri; + private ImageView mBackgroundView; private LottieAnimationView mAnimationView; private IllustrationPreference mPreference; private PreferenceViewHolder mViewHolder; @@ -66,6 +67,7 @@ public class IllustrationPreferenceTest { MockitoAnnotations.initMocks(this); mImageUri = new Uri.Builder().build(); + mBackgroundView = new ImageView(mContext); mAnimationView = spy(new LottieAnimationView(mContext)); mMiddleGroundLayout = new FrameLayout(mContext); final FrameLayout illustrationFrame = new FrameLayout(mContext); @@ -73,6 +75,7 @@ public class IllustrationPreferenceTest { new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout); + doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view); doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view); doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame); mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView)); @@ -155,4 +158,32 @@ public class IllustrationPreferenceTest { verify(mAnimationView).setFailureListener(any()); } + + @Test + public void setMaxHeight_smallerThanRestrictedHeight_matchResult() { + final int restrictedHeight = + mContext.getResources().getDimensionPixelSize( + R.dimen.settingslib_illustration_height); + final int maxHeight = restrictedHeight - 200; + + mPreference.setMaxHeight(maxHeight); + mPreference.onBindViewHolder(mViewHolder); + + assertThat(mBackgroundView.getMaxHeight()).isEqualTo(maxHeight); + assertThat(mAnimationView.getMaxHeight()).isEqualTo(maxHeight); + } + + @Test + public void setMaxHeight_largerThanRestrictedHeight_specificHeight() { + final int restrictedHeight = + mContext.getResources().getDimensionPixelSize( + R.dimen.settingslib_illustration_height); + final int maxHeight = restrictedHeight + 200; + + mPreference.setMaxHeight(maxHeight); + mPreference.onBindViewHolder(mViewHolder); + + assertThat(mBackgroundView.getMaxHeight()).isEqualTo(restrictedHeight); + assertThat(mAnimationView.getMaxHeight()).isEqualTo(restrictedHeight); + } } diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml new file mode 100644 index 000000000000..c6b87d38f7da --- /dev/null +++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- The enter animation of the host dialog is a translation of 0px that lasts 500ms so that the --> +<!-- host dialog is directly visible but the dim background still takes 500ms to fade in. --> +<translate xmlns:android="http://schemas.android.com/apk/res/android" + android:fromXDelta="0" + android:toXDelta="0" + android:duration="500" />
\ No newline at end of file diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml new file mode 100644 index 000000000000..a0f441eaeed4 --- /dev/null +++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<alpha + xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:interpolator/decelerate_cubic" + android:duration="150" + android:fromAlpha="1.0" + android:toAlpha="0.0" />
\ No newline at end of file diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml new file mode 100644 index 000000000000..ef60a248f79a --- /dev/null +++ b/packages/SystemUI/animation/res/values/ids.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <item type="id" name="launch_animation_running"/> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/animation/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml new file mode 100644 index 000000000000..ad06c9192bc3 --- /dev/null +++ b/packages/SystemUI/animation/res/values/styles.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <style name="HostDialogTheme"> + <item name="android:windowAnimationStyle">@style/Animation.HostDialog</item> + <item name="android:windowIsFloating">false</item> + <item name="android:backgroundDimEnabled">true</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + </style> + + <style name="Animation.HostDialog" parent="@android:style/Animation"> + <item name="android:windowEnterAnimation">@anim/launch_host_dialog_enter</item> + <item name="android:windowExitAnimation">@anim/launch_host_dialog_exit</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 9c1e12923b43..702060338359 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -1,23 +1,31 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.animation -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ValueAnimator import android.app.ActivityManager import android.app.ActivityTaskManager import android.app.PendingIntent import android.app.TaskInfo -import android.content.Context import android.graphics.Matrix -import android.graphics.PorterDuff -import android.graphics.PorterDuffXfermode import android.graphics.Rect import android.graphics.RectF -import android.graphics.drawable.GradientDrawable import android.os.Looper import android.os.RemoteException import android.util.Log -import android.util.MathUtils import android.view.IRemoteAnimationFinishedCallback import android.view.IRemoteAnimationRunner import android.view.RemoteAnimationAdapter @@ -26,7 +34,6 @@ import android.view.SyncRtSurfaceTransactionApplier import android.view.View import android.view.ViewGroup import android.view.WindowManager -import android.view.animation.AnimationUtils import android.view.animation.PathInterpolator import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils @@ -38,52 +45,23 @@ private const val TAG = "ActivityLaunchAnimator" * A class that allows activities to be started in a seamless way from a view that is transforming * nicely into the starting window. */ -class ActivityLaunchAnimator( - private val callback: Callback, - context: Context -) { +class ActivityLaunchAnimator(private val launchAnimator: LaunchAnimator) { companion object { - private const val DEBUG = false - const val ANIMATION_DURATION = 500L - private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L - private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L - private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT private const val ANIMATION_DURATION_NAV_FADE_IN = 266L private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L private const val ANIMATION_DELAY_NAV_FADE_IN = - ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN + LaunchAnimator.ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN private const val LAUNCH_TIMEOUT = 1000L - @JvmField val CONTENT_FADE_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f) - private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f) private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f) private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f) - - private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC) - - /** - * Given the [linearProgress] of a launch animation, return the linear progress of the - * sub-animation starting [delay] ms after the launch animation and that lasts [duration]. - */ - @JvmStatic - fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float { - return MathUtils.constrain( - (linearProgress * ANIMATION_DURATION - delay) / duration, - 0.0f, - 1.0f - ) - } } - /** The interpolator used for the width, height, Y position and corner radius. */ - private val animationInterpolator = AnimationUtils.loadInterpolator(context, - R.interpolator.launch_animation_interpolator_y) - - /** The interpolator used for the X position. */ - private val animationInterpolatorX = AnimationUtils.loadInterpolator(context, - R.interpolator.launch_animation_interpolator_x) - - private val cornerRadii = FloatArray(8) + /** + * The callback of this animator. This should be set before any call to + * [start(Pending)IntentWithAnimation]. + */ + var callback: Callback? = null /** * Start an intent and animate the opening window. The intent will be started by running @@ -119,6 +97,8 @@ class ActivityLaunchAnimator( return } + val callback = this.callback ?: throw IllegalStateException( + "ActivityLaunchAnimator.callback must be set before using this animator") val runner = Runner(controller) val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen @@ -126,9 +106,9 @@ class ActivityLaunchAnimator( // keyguard with the animation val animationAdapter = if (!hideKeyguardWithAnimation) { RemoteAnimationAdapter( - runner, - ANIMATION_DURATION, - ANIMATION_DURATION - 150 /* statusBarTransitionDelay */ + runner, + LaunchAnimator.ANIMATION_DURATION, + LaunchAnimator.ANIMATION_DURATION - 150 /* statusBarTransitionDelay */ ) } else { null @@ -150,10 +130,10 @@ class ActivityLaunchAnimator( // Only animate if the app is not already on top and will be opened, unless we are on the // keyguard. val willAnimate = - launchResult == ActivityManager.START_TASK_TO_FRONT || - launchResult == ActivityManager.START_SUCCESS || - (launchResult == ActivityManager.START_DELIVERED_TO_TOP && - hideKeyguardWithAnimation) + launchResult == ActivityManager.START_TASK_TO_FRONT || + launchResult == ActivityManager.START_SUCCESS || + (launchResult == ActivityManager.START_DELIVERED_TO_TOP && + hideKeyguardWithAnimation) Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " + "hideKeyguardWithAnimation=$hideKeyguardWithAnimation") @@ -234,7 +214,7 @@ class ActivityLaunchAnimator( * * Note that all callbacks (onXXX methods) are all called on the main thread. */ - interface Controller { + interface Controller : LaunchAnimator.Controller { companion object { /** * Return a [Controller] that will animate and expand [view] into the opening window. @@ -259,53 +239,12 @@ class ActivityLaunchAnimator( } /** - * The container in which the view that started the intent will be animating together with - * the opening window. - * - * This will be used to: - * - Get the associated [Context]. - * - Compute whether we are expanding fully above the current window. - * - Apply surface transactions in sync with RenderThread. - * - * This container can be changed to force this [Controller] to animate the expanding view - * inside a different location, for instance to ensure correct layering during the - * animation. - */ - var launchContainer: ViewGroup - - /** - * Return the [State] of the view that will be animated. We will animate from this state to - * the final window state. - * - * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the - * animation. - */ - fun createAnimatorState(): State - - /** * The intent was started. If [willAnimate] is false, nothing else will happen and the * animation will not be started. */ fun onIntentStarted(willAnimate: Boolean) {} /** - * The animation started. This is typically used to initialize any additional resource - * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding - * fully above the [root view][getRootView]. - */ - fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {} - - /** The animation made progress and the expandable view [state] should be updated. */ - fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {} - - /** - * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was - * called previously. This is typically used to clean up the resources initialized when the - * animation was started. - */ - fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {} - - /** * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called * before the cancellation. @@ -313,66 +252,11 @@ class ActivityLaunchAnimator( fun onLaunchAnimationCancelled() {} } - /** The state of an expandable view during an [ActivityLaunchAnimator] animation. */ - open class State( - /** The position of the view in screen space coordinates. */ - var top: Int, - var bottom: Int, - var left: Int, - var right: Int, - - var topCornerRadius: Float = 0f, - var bottomCornerRadius: Float = 0f - ) { - private val startTop = top - private val startBottom = bottom - private val startLeft = left - private val startRight = right - private val startWidth = width - private val startHeight = height - val startCenterX = centerX - val startCenterY = centerY - - val width: Int - get() = right - left - - val height: Int - get() = bottom - top - - open val topChange: Int - get() = top - startTop - - open val bottomChange: Int - get() = bottom - startBottom - - val leftChange: Int - get() = left - startLeft - - val rightChange: Int - get() = right - startRight - - val widthRatio: Float - get() = width.toFloat() / startWidth - - val heightRatio: Float - get() = height.toFloat() / startHeight - - val centerX: Float - get() = left + width / 2f - - val centerY: Float - get() = top + height / 2f - - /** Whether the expanded view should be visible or hidden. */ - var visible: Boolean = true - } - @VisibleForTesting inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() { private val launchContainer = controller.launchContainer private val context = launchContainer.context private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer) - private var animator: ValueAnimator? = null private val matrix = Matrix() private val invertMatrix = Matrix() @@ -380,6 +264,7 @@ class ActivityLaunchAnimator( private var windowCropF = RectF() private var timedOut = false private var cancelled = false + private var animation: LaunchAnimator.Animation? = null // A timeout to cancel the remote animation if it is not started within X milliseconds after // the intent was started. @@ -429,7 +314,7 @@ class ActivityLaunchAnimator( nonApps: Array<out RemoteAnimationTarget>?, iCallback: IRemoteAnimationFinishedCallback? ) { - if (DEBUG) { + if (LaunchAnimator.DEBUG) { Log.d(TAG, "Remote animation started") } @@ -449,36 +334,20 @@ class ActivityLaunchAnimator( it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR } - // Start state. - val state = controller.createAnimatorState() - - val startTop = state.top - val startBottom = state.bottom - val startLeft = state.left - val startRight = state.right - val startXCenter = (startLeft + startRight) / 2f - val startWidth = startRight - startLeft - - val startTopCornerRadius = state.topCornerRadius - val startBottomCornerRadius = state.bottomCornerRadius - - // End state. val windowBounds = window.screenSpaceBounds - val endTop = windowBounds.top - val endBottom = windowBounds.bottom - val endLeft = windowBounds.left - val endRight = windowBounds.right - val endXCenter = (endLeft + endRight) / 2f - val endWidth = endRight - endLeft - - // TODO(b/184121838): Ensure that we are launching on the same screen. - val rootViewLocation = launchContainer.locationOnScreen - val isExpandingFullyAbove = endTop <= rootViewLocation[1] && - endBottom >= rootViewLocation[1] + launchContainer.height && - endLeft <= rootViewLocation[0] && - endRight >= rootViewLocation[0] + launchContainer.width - - // TODO(b/184121838): We should somehow get the top and bottom radius of the window. + val endState = LaunchAnimator.State( + top = windowBounds.top, + bottom = windowBounds.bottom, + left = windowBounds.left, + right = windowBounds.right + ) + val callback = this@ActivityLaunchAnimator.callback!! + val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo) + + // TODO(b/184121838): We should somehow get the top and bottom radius of the window + // instead of recomputing isExpandingFullyAbove here. + val isExpandingFullyAbove = + launchAnimator.isExpandingFullyAbove(controller.launchContainer, endState) val endRadius = if (isExpandingFullyAbove) { // Most of the time, expanding fully above the root view means expanding in full // screen. @@ -488,97 +357,40 @@ class ActivityLaunchAnimator( // a radius of 0. 0f } + endState.topCornerRadius = endRadius + endState.bottomCornerRadius = endRadius - // We add an extra layer with the same color as the app splash screen background color, - // which is usually the same color of the app background. We first fade in this layer - // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the - // launch container and reveal the opening window. - val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo) - val windowBackgroundLayer = GradientDrawable().apply { - setColor(windowBackgroundColor) - alpha = 0 - } - - // Update state. - val animator = ValueAnimator.ofFloat(0f, 1f) - this.animator = animator - animator.duration = ANIMATION_DURATION - animator.interpolator = Interpolators.LINEAR - - val launchContainerOverlay = launchContainer.overlay - animator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { - if (DEBUG) { - Log.d(TAG, "Animation started") - } - + // We animate the opening window and delegate the view expansion to [this.controller]. + val delegate = this.controller + val controller = object : LaunchAnimator.Controller by delegate { + override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { callback.setBlursDisabledForAppLaunch(true) - controller.onLaunchAnimationStart(isExpandingFullyAbove) - - // Add the drawable to the launch container overlay. Overlays always draw - // drawables after views, so we know that it will be drawn above any view added - // by the controller. - launchContainerOverlay.add(windowBackgroundLayer) + delegate.onLaunchAnimationStart(isExpandingFullyAbove) } - override fun onAnimationEnd(animation: Animator?) { - if (DEBUG) { - Log.d(TAG, "Animation ended") - } - + override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { callback.setBlursDisabledForAppLaunch(false) iCallback?.invoke() - controller.onLaunchAnimationEnd(isExpandingFullyAbove) - launchContainerOverlay.remove(windowBackgroundLayer) + delegate.onLaunchAnimationEnd(isExpandingFullyAbove) } - }) - animator.addUpdateListener { animation -> - if (cancelled) { - return@addUpdateListener + override fun onLaunchAnimationProgress( + state: LaunchAnimator.State, + progress: Float, + linearProgress: Float + ) { + applyStateToWindow(window, state) + navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } + delegate.onLaunchAnimationProgress(state, progress, linearProgress) } - - val linearProgress = animation.animatedFraction - val progress = animationInterpolator.getInterpolation(linearProgress) - val xProgress = animationInterpolatorX.getInterpolation(linearProgress) - val xCenter = MathUtils.lerp(startXCenter, endXCenter, xProgress) - val halfWidth = lerp(startWidth, endWidth, progress) / 2 - - state.top = lerp(startTop, endTop, progress).roundToInt() - state.bottom = lerp(startBottom, endBottom, progress).roundToInt() - state.left = (xCenter - halfWidth).roundToInt() - state.right = (xCenter + halfWidth).roundToInt() - - state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress) - state.bottomCornerRadius = - MathUtils.lerp(startBottomCornerRadius, endRadius, progress) - - // The expanding view can/should be hidden once it is completely coverred by the - // windowBackgroundLayer. - state.visible = - getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1 - - applyStateToWindow(window, state) - applyStateToWindowBackgroundLayer(windowBackgroundLayer, state, linearProgress) - navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } - - // If we started expanding the view, we make it 1 pixel smaller on all sides to - // avoid artefacts on the corners caused by anti-aliasing of the view background and - // the window background layer. - if (state.top != startTop && state.left != startLeft && - state.bottom != startBottom && state.right != startRight) { - state.top += 1 - state.left += 1 - state.right -= 1 - state.bottom -= 1 - } - controller.onLaunchAnimationProgress(state, progress, linearProgress) } - animator.start() + // We draw a hole when the additional layer is fading out to reveal the opening window. + animation = launchAnimator.startAnimation( + controller, endState, windowBackgroundColor, drawHole = true) } - private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) { + private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) { val screenBounds = window.screenSpaceBounds val centerX = (screenBounds.left + screenBounds.right) / 2f val centerY = (screenBounds.top + screenBounds.bottom) / 2f @@ -632,48 +444,13 @@ class ActivityLaunchAnimator( transactionApplier.scheduleApply(params) } - private fun applyStateToWindowBackgroundLayer( - drawable: GradientDrawable, - state: State, - linearProgress: Float - ) { - // Update position. - drawable.setBounds(state.left, state.top, state.right, state.bottom) - - // Update radius. - cornerRadii[0] = state.topCornerRadius - cornerRadii[1] = state.topCornerRadius - cornerRadii[2] = state.topCornerRadius - cornerRadii[3] = state.topCornerRadius - cornerRadii[4] = state.bottomCornerRadius - cornerRadii[5] = state.bottomCornerRadius - cornerRadii[6] = state.bottomCornerRadius - cornerRadii[7] = state.bottomCornerRadius - drawable.cornerRadii = cornerRadii - - // We first fade in the background layer to hide the expanding view, then fade it out - // with SRC mode to draw a hole punch in the status bar and reveal the opening window. - val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) - if (fadeInProgress < 1) { - val alpha = CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(fadeInProgress) - drawable.alpha = (alpha * 0xFF).roundToInt() - drawable.setXfermode(null) - } else { - val fadeOutProgress = getProgress(linearProgress, - ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW) - val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress) - drawable.alpha = (alpha * 0xFF).roundToInt() - drawable.setXfermode(SRC_MODE) - } - } - private fun applyStateToNavigationBar( navigationBar: RemoteAnimationTarget, - state: State, + state: LaunchAnimator.State, linearProgress: Float ) { - val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN, - ANIMATION_DURATION_NAV_FADE_OUT) + val fadeInProgress = LaunchAnimator.getProgress(linearProgress, + ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT) val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash) if (fadeInProgress > 0) { @@ -682,13 +459,13 @@ class ActivityLaunchAnimator( 0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat()) windowCrop.set(state.left, 0, state.right, state.height) params - .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress)) - .withMatrix(matrix) - .withWindowCrop(windowCrop) - .withVisibility(true) + .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress)) + .withMatrix(matrix) + .withWindowCrop(windowCrop) + .withVisibility(true) } else { - val fadeOutProgress = getProgress(linearProgress, 0, - ANIMATION_DURATION_NAV_FADE_OUT) + val fadeOutProgress = LaunchAnimator.getProgress(linearProgress, 0, + ANIMATION_DURATION_NAV_FADE_OUT) params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress)) } @@ -714,7 +491,7 @@ class ActivityLaunchAnimator( cancelled = true removeTimeout() context.mainExecutor.execute { - animator?.cancel() + animation?.cancel() controller.onLaunchAnimationCancelled() } } @@ -726,9 +503,5 @@ class ActivityLaunchAnimator( e.printStackTrace() } } - - private fun lerp(start: Int, stop: Int, amount: Float): Float { - return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount) - } } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt index d4be25382395..258ca6bdf79b 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.animation /** diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt new file mode 100644 index 000000000000..c2b36089d0a7 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.animation + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Looper +import android.util.Log +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver +import android.view.WindowManager +import android.widget.FrameLayout + +private const val TAG = "DialogLaunchAnimator" + +/** + * A class that allows dialogs to be started in a seamless way from a view that is transforming + * nicely into the starting dialog. + * + * Important: Don't forget to call [DialogLaunchAnimator.onDozeAmountChanged] when the doze amount + * changes to gracefully handle dialogs fading out when the device is dozing. + */ +class DialogLaunchAnimator( + private val context: Context, + private val launchAnimator: LaunchAnimator, + private val hostDialogProvider: HostDialogProvider +) { + private companion object { + private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running + } + + // TODO(b/201264644): Remove this set. + private val currentAnimations = hashSetOf<DialogLaunchAnimation>() + + /** + * Show [dialog] by expanding it from [view]. + * + * Caveats: When calling this function, the dialog content view will actually be stolen and + * attached to a different dialog (and thus a different window) which means that the actual + * dialog window will never be drawn. Moreover, unless [dialog] is a [ListenableDialog], you + * must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually + * dismiss, hide or show the dialog. + */ + fun showFromView(dialog: Dialog, view: View): Dialog { + if (Looper.myLooper() != Looper.getMainLooper()) { + throw IllegalStateException( + "showFromView must be called from the main thread and dialog must be created in " + + "the main thread") + } + + // Make sure we don't run the launch animation from the same view twice at the same time. + if (view.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) { + Log.e(TAG, "Not running dialog launch animation as there is already one running") + dialog.show() + return dialog + } + + view.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) + + val launchAnimation = DialogLaunchAnimation( + context, launchAnimator, hostDialogProvider, view, + onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog) + val hostDialog = launchAnimation.hostDialog + currentAnimations.add(launchAnimation) + + // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the + // host dialog. + if (dialog is ListenableDialog) { + dialog.addListener(object : DialogListener { + override fun onDismiss() { + dialog.removeListener(this) + hostDialog.dismiss() + } + + override fun onHide() { + if (launchAnimation.ignoreNextCallToHide) { + launchAnimation.ignoreNextCallToHide = false + return + } + + hostDialog.hide() + } + + override fun onShow() { + hostDialog.show() + + // We don't actually want to show the original dialog, so hide it. + launchAnimation.ignoreNextCallToHide = true + dialog.hide() + } + }) + } + + launchAnimation.start() + return hostDialog + } + + /** Notify the current doze amount, to ensure that dialogs fade out when dozing. */ + // TODO(b/193634619): Replace this by some mandatory constructor parameter to make sure that we + // don't forget to call this when the doze amount changes. + fun onDozeAmountChanged(amount: Float) { + currentAnimations.forEach { it.onDozeAmountChanged(amount) } + } + + /** + * Ensure that all dialogs currently shown won't animate into their touch surface when + * dismissed. + * + * This is a temporary API meant to be called right before we both dismiss a dialog and start + * an activity, which currently does not look good if we animate the dialog into the touch + * surface at the same time as the activity starts. + * + * TODO(b/193634619): Remove this function and animate dialog into opening activity instead. + */ + fun disableAllCurrentDialogsExitAnimations() { + currentAnimations.forEach { it.exitAnimationDisabled = true } + } +} + +interface HostDialogProvider { + /** + * Create a host dialog that will be used to host a launch animation. This host dialog must: + * 1. call [onCreateCallback] in its onCreate() method, e.g. right after calling + * super.onCreate(). + * 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing + * logic should instead be done inside the lambda passed to [dismissOverride], which will + * be called after the exit animation. + * + * See SystemUIHostDialogProvider for an example of implementation. + */ + fun createHostDialog( + context: Context, + theme: Int, + onCreateCallback: () -> Unit, + dismissOverride: (() -> Unit) -> Unit + ): Dialog +} + +/** A dialog to/from which we can add/remove listeners. */ +interface ListenableDialog { + /** Add [listener] to the listeners. */ + fun addListener(listener: DialogListener) + + /** Remove [listener] from the listeners. */ + fun removeListener(listener: DialogListener) +} + +interface DialogListener { + /** Called when this dialog dismiss() is called. */ + fun onDismiss() + + /** Called when this dialog hide() is called. */ + fun onHide() + + /** Called when this dialog show() is called. */ + fun onShow() +} + +private class DialogLaunchAnimation( + private val context: Context, + private val launchAnimator: LaunchAnimator, + hostDialogProvider: HostDialogProvider, + + /** The view that triggered the dialog after being tapped. */ + private val touchSurface: View, + + /** + * A callback that will be called with this [DialogLaunchAnimation] after the dialog was + * dismissed and the exit animation is done. + */ + private val onDialogDismissed: (DialogLaunchAnimation) -> Unit, + + /** The original dialog whose content will be shown and animate in/out in [hostDialog]. */ + private val originalDialog: Dialog +) { + /** + * The fullscreen dialog to which we will add the content view [originalDialogView] of + * [originalDialog]. + */ + val hostDialog = hostDialogProvider.createHostDialog( + context, R.style.HostDialogTheme, this::onHostDialogCreated, this::onHostDialogDismissed) + + /** The root content view of [hostDialog]. */ + private val hostDialogRoot = FrameLayout(context) + + /** + * The content view of [originalDialog], which will be stolen from that dialog and added to + * [hostDialogRoot]. + */ + private var originalDialogView: View? = null + + /** + * The background color of [originalDialogView], taking into consideration the [originalDialog] + * window background color. + */ + private var originalDialogBackgroundColor = Color.BLACK + + /** + * Whether we are currently launching/showing the dialog by animating it from [touchSurface]. + */ + private var isLaunching = true + + /** Whether we are currently dismissing/hiding the dialog by animating into [touchSurface]. */ + private var isDismissing = false + + private var dismissRequested = false + private var drawHostDialog = false + var ignoreNextCallToHide = false + var exitAnimationDisabled = false + + fun start() { + // Show the host (fullscreen) dialog, to which we will add the stolen dialog view. + hostDialog.show() + + // Steal the dialog view. We do that by showing it but preventing it from drawing, then + // hiding it as soon as its content is available. + stealOriginalDialogContentView(then = this::showDialogFromView) + } + + private fun onHostDialogCreated() { + // Make the dialog fullscreen with a transparent background. + hostDialog.setContentView( + hostDialogRoot, + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + ) + + val window = hostDialog.window + ?: throw IllegalStateException("There is no window associated to the host dialog") + window.setBackgroundDrawableResource(android.R.color.transparent) + window.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT + ) + + // Prevent the host dialog from drawing until the animation starts. + hostDialogRoot.viewTreeObserver.addOnPreDrawListener( + object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + if (drawHostDialog) { + hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this) + return true + } + + return false + } + } + ) + } + + /** Get the content view of [originalDialog] and pass it to [then]. */ + private fun stealOriginalDialogContentView(then: (View) -> Unit) { + // The original dialog content view will be attached to android.R.id.content when the dialog + // is shown, so we show the dialog and add an observer to get the view but also prevents the + // original dialog from being drawn. + val androidContent = originalDialog.findViewById<ViewGroup>(android.R.id.content) + ?: throw IllegalStateException("Dialog does not have any android.R.id.content view") + + androidContent.viewTreeObserver.addOnPreDrawListener( + object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + if (androidContent.childCount == 1) { + androidContent.viewTreeObserver.removeOnPreDrawListener(this) + + // Hide the animated dialog. Because of the dialog listener set up + // earlier, this would also hide the host dialog, but in this case we + // need to keep the host dialog visible. + ignoreNextCallToHide = true + originalDialog.hide() + + then(androidContent.getChildAt(0)) + return false + } + + // Never draw the original dialog content. + return false + } + }) + originalDialog.show() + } + + private fun showDialogFromView(dialogView: View) { + // Save the dialog view for later as we will need it for the close animation. + this.originalDialogView = dialogView + + // Close the dialog when clicking outside of it. + hostDialogRoot.setOnClickListener { hostDialog.dismiss() } + dialogView.isClickable = true + + // Set the background of the window dialog to the dialog itself. + // TODO(b/193634619): Support dialog windows without background. + // TODO(b/193634619): Support dialog whose background comes from the content view instead of + // the window. + val typedArray = + originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window) + val backgroundRes = + typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0) + typedArray.recycle() + if (backgroundRes == 0) { + throw IllegalStateException("Dialogs with no backgrounds on window are not supported") + } + + dialogView.setBackgroundResource(backgroundRes) + originalDialogBackgroundColor = + GhostedViewLaunchAnimatorController.findGradientDrawable(dialogView.background!!) + ?.color + ?.defaultColor ?: Color.BLACK + + // Add the dialog view to the host (fullscreen) dialog and make it invisible to make sure + // it's not drawn yet. + (dialogView.parent as? ViewGroup)?.removeView(dialogView) + hostDialogRoot.addView( + dialogView, + + // We give it the size of its original dialog window. + FrameLayout.LayoutParams( + originalDialog.window.attributes.width, + originalDialog.window.attributes.height, + Gravity.CENTER + ) + ) + dialogView.visibility = View.INVISIBLE + + // Start the animation when the dialog is laid out in the center of the host dialog. + dialogView.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { + override fun onLayoutChange( + view: View, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + dialogView.removeOnLayoutChangeListener(this) + startAnimation( + isLaunching = true, + onLaunchAnimationStart = { drawHostDialog = true }, + onLaunchAnimationEnd = { + touchSurface.setTag(R.id.launch_animation_running, null) + + // We hide the touch surface when the dialog is showing. We will make this + // view visible again when dismissing the dialog. + // TODO(b/193634619): Provide an easy way for views to check if they should + // be hidden because of a dialog launch so that they don't override this + // visibility when updating/refreshing itself. + touchSurface.visibility = View.INVISIBLE + + isLaunching = false + + // dismiss was called during the animation, dismiss again now to actually + // dismiss. + if (dismissRequested) { + hostDialog.dismiss() + } + } + ) + } + }) + } + + private fun onHostDialogDismissed(actualDismiss: () -> Unit) { + if (Looper.myLooper() != Looper.getMainLooper()) { + context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) } + return + } + + // TODO(b/193634619): Support interrupting the launch animation in the middle. + if (isLaunching) { + dismissRequested = true + return + } + + if (isDismissing) { + return + } + + isDismissing = true + hideDialogIntoView { instantDismiss: Boolean -> + if (instantDismiss) { + originalDialog.hide() + hostDialog.hide() + } + + originalDialog.dismiss() + actualDismiss() + } + } + + /** + * Hide the dialog into the touch surface and call [dismissDialogs] when the animation is done + * (passing instantDismiss=true) or if it's skipped (passing instantDismiss=false) to actually + * dismiss the dialogs. + */ + private fun hideDialogIntoView(dismissDialogs: (Boolean) -> Unit) { + if (!shouldAnimateDialogIntoView()) { + Log.i(TAG, "Skipping animation of dialog into the touch surface") + + // If the view is invisible it's probably because of us, so we make it visible again. + if (touchSurface.visibility == View.INVISIBLE) { + touchSurface.visibility = View.VISIBLE + } + + dismissDialogs(false /* instantDismiss */) + onDialogDismissed(this@DialogLaunchAnimation) + return + } + + startAnimation( + isLaunching = false, + onLaunchAnimationStart = { + // Remove the dim background as soon as we start the animation. + hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + }, + onLaunchAnimationEnd = { + touchSurface.visibility = View.VISIBLE + originalDialogView!!.visibility = View.INVISIBLE + dismissDialogs(true /* instantDismiss */) + onDialogDismissed(this@DialogLaunchAnimation) + } + ) + } + + private fun startAnimation( + isLaunching: Boolean, + onLaunchAnimationStart: () -> Unit = {}, + onLaunchAnimationEnd: () -> Unit = {} + ) { + val dialogView = this.originalDialogView!! + + // Create 2 ghost controllers to animate both the dialog and the touch surface in the host + // dialog. + val startView = if (isLaunching) touchSurface else dialogView + val endView = if (isLaunching) dialogView else touchSurface + val startViewController = GhostedViewLaunchAnimatorController(startView) + val endViewController = GhostedViewLaunchAnimatorController(endView) + startViewController.launchContainer = hostDialogRoot + endViewController.launchContainer = hostDialogRoot + + val endState = endViewController.createAnimatorState() + val controller = object : LaunchAnimator.Controller { + override var launchContainer: ViewGroup + get() = startViewController.launchContainer + set(value) { + startViewController.launchContainer = value + endViewController.launchContainer = value + } + + override fun createAnimatorState(): LaunchAnimator.State { + return startViewController.createAnimatorState() + } + + override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { + startViewController.onLaunchAnimationStart(isExpandingFullyAbove) + endViewController.onLaunchAnimationStart(isExpandingFullyAbove) + + onLaunchAnimationStart() + } + + override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { + startViewController.onLaunchAnimationEnd(isExpandingFullyAbove) + endViewController.onLaunchAnimationEnd(isExpandingFullyAbove) + + onLaunchAnimationEnd() + } + + override fun onLaunchAnimationProgress( + state: LaunchAnimator.State, + progress: Float, + linearProgress: Float + ) { + startViewController.onLaunchAnimationProgress(state, progress, linearProgress) + + // The end view is visible only iff the starting view is not visible. + state.visible = !state.visible + endViewController.onLaunchAnimationProgress(state, progress, linearProgress) + + // If the dialog content is complex, its dimension might change during the launch + // animation. The animation end position might also change during the exit + // animation, for instance when locking the phone when the dialog is open. Therefore + // we update the end state to the new position/size. Usually the dialog dimension or + // position will change in the early frames, so changing the end state shouldn't + // really be noticeable. + endViewController.fillGhostedViewState(endState) + } + } + + launchAnimator.startAnimation(controller, endState, originalDialogBackgroundColor) + } + + private fun shouldAnimateDialogIntoView(): Boolean { + if (exitAnimationDisabled) { + return false + } + + // The touch surface should be invisible by now, if it's not then something else changed its + // visibility and we probably don't want to run the animation. + if (touchSurface.visibility != View.INVISIBLE) { + return false + } + + // If the touch surface is not attached or one of its ancestors is not visible, then we + // don't run the animation either. + if (!touchSurface.isAttachedToWindow) { + return false + } + + return (touchSurface.parent as? View)?.isShown ?: true + } + + internal fun onDozeAmountChanged(amount: Float) { + val alpha = Interpolators.ALPHA_OUT.getInterpolation(1 - amount) + val decorView = this.hostDialog.window?.decorView ?: return + if (decorView.hasOverlappingRendering() && alpha > 0.0f && + alpha < 1.0f && decorView.layerType != View.LAYER_TYPE_HARDWARE) { + decorView.setLayerType(View.LAYER_TYPE_HARDWARE, null) + } + decorView.alpha = alpha + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index b4ffb3f6cf4e..f7e0d588407f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -1,7 +1,24 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.animation import android.graphics.Canvas import android.graphics.ColorFilter +import android.graphics.Insets import android.graphics.Matrix import android.graphics.PixelFormat import android.graphics.Rect @@ -42,6 +59,7 @@ open class GhostedViewLaunchAnimatorController( override var launchContainer = ghostedView.rootView as ViewGroup private val launchContainerOverlay: ViewGroupOverlay get() = launchContainer.overlay + private val launchContainerLocation = IntArray(2) /** The ghost view that is drawn and animated instead of the ghosted view. */ private var ghostView: GhostView? = null @@ -59,8 +77,12 @@ open class GhostedViewLaunchAnimatorController( * [backgroundView]. */ private var backgroundDrawable: WrappedDrawable? = null + private val backgroundInsets by lazy { getBackground()?.opticalInsets ?: Insets.NONE } private var startBackgroundAlpha: Int = 0xFF + private val ghostedViewLocation = IntArray(2) + private val ghostedViewState = LaunchAnimator.State() + /** * Return the background of the [ghostedView]. This background will be used to draw the * background of the background view that is expanding up to the final animation position. This @@ -103,16 +125,24 @@ open class GhostedViewLaunchAnimatorController( return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius } - override fun createAnimatorState(): ActivityLaunchAnimator.State { - val location = ghostedView.locationOnScreen - return ActivityLaunchAnimator.State( - top = location[1], - bottom = location[1] + ghostedView.height, - left = location[0], - right = location[0] + ghostedView.width, + override fun createAnimatorState(): LaunchAnimator.State { + val state = LaunchAnimator.State( topCornerRadius = getCurrentTopCornerRadius(), bottomCornerRadius = getCurrentBottomCornerRadius() ) + fillGhostedViewState(state) + return state + } + + fun fillGhostedViewState(state: LaunchAnimator.State) { + // For the animation we are interested in the area that has a non transparent background, + // so we have to take the optical insets into account. + ghostedView.getLocationOnScreen(ghostedViewLocation) + val insets = backgroundInsets + state.top = ghostedViewLocation[1] + insets.top + state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom + state.left = ghostedViewLocation[0] + insets.left + state.right = ghostedViewLocation[0] + ghostedView.width - insets.right } override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { @@ -144,7 +174,7 @@ open class GhostedViewLaunchAnimatorController( } override fun onLaunchAnimationProgress( - state: ActivityLaunchAnimator.State, + state: LaunchAnimator.State, progress: Float, linearProgress: Float ) { @@ -162,19 +192,47 @@ open class GhostedViewLaunchAnimatorController( return } - val scale = min(state.widthRatio, state.heightRatio) - ghostViewMatrix.setValues(initialGhostViewMatrixValues) - ghostViewMatrix.postScale(scale, scale, state.startCenterX, state.startCenterY) + // The ghost and backgrounds views were made invisible earlier. That can for instance happen + // when animating a dialog into a view. + if (ghostView.visibility == View.INVISIBLE) { + ghostView.visibility = View.VISIBLE + backgroundView.visibility = View.VISIBLE + } + + fillGhostedViewState(ghostedViewState) + val leftChange = state.left - ghostedViewState.left + val rightChange = state.right - ghostedViewState.right + val topChange = state.top - ghostedViewState.top + val bottomChange = state.bottom - ghostedViewState.bottom + + val widthRatio = state.width.toFloat() / ghostedViewState.width + val heightRatio = state.height.toFloat() / ghostedViewState.height + val scale = min(widthRatio, heightRatio) + + launchContainer.getLocationOnScreen(launchContainerLocation) + GhostView.calculateMatrix(ghostedView, launchContainer, ghostViewMatrix) + ghostViewMatrix.postScale( + scale, scale, + ghostedViewState.centerX - launchContainerLocation[0], + ghostedViewState.centerY - launchContainerLocation[1] + ) ghostViewMatrix.postTranslate( - (state.leftChange + state.rightChange) / 2f, - (state.topChange + state.bottomChange) / 2f + (leftChange + rightChange) / 2f, + (topChange + bottomChange) / 2f ) ghostView.animationMatrix = ghostViewMatrix - backgroundView.top = state.top - backgroundView.bottom = state.bottom - backgroundView.left = state.left - backgroundView.right = state.right + // We need to take into account the background insets for the background position. + val insets = backgroundInsets + val topWithInsets = state.top - insets.top + val leftWithInsets = state.left - insets.left + val rightWithInsets = state.right + insets.right + val bottomWithInsets = state.bottom + insets.bottom + + backgroundView.top = topWithInsets - launchContainerLocation[1] + backgroundView.bottom = bottomWithInsets - launchContainerLocation[1] + backgroundView.left = leftWithInsets - launchContainerLocation[0] + backgroundView.right = rightWithInsets - launchContainerLocation[0] val backgroundDrawable = backgroundDrawable!! backgroundDrawable.wrapped?.let { @@ -207,7 +265,7 @@ open class GhostedViewLaunchAnimatorController( * [drawable] is a [LayerDrawable], this will return the first layer that is a * [GradientDrawable]. */ - private fun findGradientDrawable(drawable: Drawable): GradientDrawable? { + fun findGradientDrawable(drawable: Drawable): GradientDrawable? { if (drawable is GradientDrawable) { return drawable } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java index 659b9fee8656..27658824933a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java @@ -16,6 +16,7 @@ package com.android.systemui.animation; +import android.graphics.Path; import android.util.MathUtils; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; @@ -29,7 +30,97 @@ import android.view.animation.PathInterpolator; * Utility class to receive interpolators from */ public class Interpolators { - public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + + /* + * ============================================================================================ + * Emphasized interpolators. + * ============================================================================================ + */ + + /** + * The default emphasized interpolator. Used for hero / emphasized movement of content. + */ + public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); + + /** + * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that + * is disappearing e.g. when moving off screen. + */ + public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( + 0.3f, 0f, 0.8f, 0.15f); + + /** + * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that + * is appearing e.g. when coming from off screen + */ + public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( + 0.05f, 0.7f, 0.1f, 1f); + + + /* + * ============================================================================================ + * Standard interpolators. + * ============================================================================================ + */ + + /** + * The standard interpolator that should be used on every normal animation + */ + public static final Interpolator STANDARD = new PathInterpolator( + 0.2f, 0f, 0f, 1f); + + /** + * The standard accelerating interpolator that should be used on every regular movement of + * content that is disappearing e.g. when moving off screen. + */ + public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator( + 0.3f, 0f, 1f, 1f); + + /** + * The standard decelerating interpolator that should be used on every regular movement of + * content that is appearing e.g. when coming from off screen. + */ + public static final Interpolator STANDARD_DECELERATE = new PathInterpolator( + 0f, 0f, 0f, 1f); + + /* + * ============================================================================================ + * Legacy + * ============================================================================================ + */ + + /** + * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. + */ + public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + + /** + * The default legacy accelerating interpolator as defined in Material 1. + * Also known as FAST_OUT_LINEAR_IN. + */ + public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f); + + /** + * The default legacy decelerating interpolator as defined in Material 1. + * Also known as LINEAR_OUT_SLOW_IN. + */ + public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f); + + /** + * Linear interpolator. Often used if the interpolator is for different properties who need + * different interpolations. + */ + public static final Interpolator LINEAR = new LinearInterpolator(); + + /* + * ============================================================================================ + * Custom interpolators + * ============================================================================================ + */ + + public static final Interpolator FAST_OUT_SLOW_IN = LEGACY; + public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE; + public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE; /** * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t @@ -37,12 +128,9 @@ public class Interpolators { */ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE = new PathInterpolator(0.8f, 0f, 0.6f, 1f); - public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); - public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f); public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); - public static final Interpolator LINEAR = new LinearInterpolator(); public static final Interpolator ACCELERATE = new AccelerateInterpolator(); public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); @@ -72,6 +160,12 @@ public class Interpolators { public static final Interpolator TOUCH_RESPONSE_REVERSE = new PathInterpolator(0.9f, 0f, 0.7f, 1f); + /* + * ============================================================================================ + * Functions / Utilities + * ============================================================================================ + */ + /** * Calculate the amount of overshoot using an exponential falloff function with desired * properties, where the overshoot smoothly transitions at the 1.0f boundary into the @@ -122,4 +216,14 @@ public class Interpolators { return (float) (1f - 0.5f * (1f - Math.cos(3.14159f * oneMinusFrac * oneMinusFrac))); } } + + // Create the default emphasized interpolator + private static PathInterpolator createEmphasizedInterpolator() { + Path path = new Path(); + // Doing the same as fast_out_extra_slow_in + path.moveTo(0f, 0f); + path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f); + path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f); + return new PathInterpolator(path); + } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt new file mode 100644 index 000000000000..3bf6c5ebd091 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.animation + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.graphics.drawable.GradientDrawable +import android.util.Log +import android.util.MathUtils +import android.view.View +import android.view.ViewGroup +import android.view.animation.AnimationUtils +import android.view.animation.PathInterpolator +import kotlin.math.roundToInt + +private const val TAG = "LaunchAnimator" + +/** A base class to animate a window launch (activity or dialog) from a view . */ +class LaunchAnimator @JvmOverloads constructor( + context: Context, + private val isForTesting: Boolean = false +) { + companion object { + internal const val DEBUG = false + const val ANIMATION_DURATION = 500L + private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L + private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L + private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT + + private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f) + private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC) + + /** + * Given the [linearProgress] of a launch animation, return the linear progress of the + * sub-animation starting [delay] ms after the launch animation and that lasts [duration]. + */ + @JvmStatic + fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float { + return MathUtils.constrain( + (linearProgress * ANIMATION_DURATION - delay) / duration, + 0.0f, + 1.0f + ) + } + } + + /** The interpolator used for the width, height, Y position and corner radius. */ + private val animationInterpolator = AnimationUtils.loadInterpolator(context, + R.interpolator.launch_animation_interpolator_y) + + /** The interpolator used for the X position. */ + private val animationInterpolatorX = AnimationUtils.loadInterpolator(context, + R.interpolator.launch_animation_interpolator_x) + + private val launchContainerLocation = IntArray(2) + private val cornerRadii = FloatArray(8) + + /** + * A controller that takes care of applying the animation to an expanding view. + * + * Note that all callbacks (onXXX methods) are all called on the main thread. + */ + interface Controller { + /** + * The container in which the view that started the animation will be animating together + * with the opening window. + * + * This will be used to: + * - Get the associated [Context]. + * - Compute whether we are expanding fully above the launch container. + * - Apply surface transactions in sync with RenderThread when animating an activity + * launch. + * + * This container can be changed to force this [Controller] to animate the expanding view + * inside a different location, for instance to ensure correct layering during the + * animation. + */ + var launchContainer: ViewGroup + + /** + * Return the [State] of the view that will be animated. We will animate from this state to + * the final window state. + * + * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the + * animation. + */ + fun createAnimatorState(): State + + /** + * The animation started. This is typically used to initialize any additional resource + * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding + * fully above the [launchContainer]. + */ + fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {} + + /** The animation made progress and the expandable view [state] should be updated. */ + fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {} + + /** + * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was + * called previously. This is typically used to clean up the resources initialized when the + * animation was started. + */ + fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {} + } + + /** The state of an expandable view during a [LaunchAnimator] animation. */ + open class State( + /** The position of the view in screen space coordinates. */ + var top: Int = 0, + var bottom: Int = 0, + var left: Int = 0, + var right: Int = 0, + + var topCornerRadius: Float = 0f, + var bottomCornerRadius: Float = 0f + ) { + private val startTop = top + + val width: Int + get() = right - left + + val height: Int + get() = bottom - top + + open val topChange: Int + get() = top - startTop + + val centerX: Float + get() = left + width / 2f + + val centerY: Float + get() = top + height / 2f + + /** Whether the expanding view should be visible or hidden. */ + var visible: Boolean = true + } + + interface Animation { + /** Cancel the animation. */ + fun cancel() + } + + /** + * Start a launch animation controlled by [controller] towards [endState]. An intermediary + * layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and + * should be the same background color as the opening (or closing) window. If [drawHole] is + * true, then this intermediary layer will be drawn with SRC blending mode while it fades out. + * + * TODO(b/184121838): Remove [drawHole] and instead make the StatusBar draw this hole instead. + */ + fun startAnimation( + controller: Controller, + endState: State, + windowBackgroundColor: Int, + drawHole: Boolean = false + ): Animation { + val state = controller.createAnimatorState() + + // Start state. + val startTop = state.top + val startBottom = state.bottom + val startLeft = state.left + val startRight = state.right + val startCenterX = (startLeft + startRight) / 2f + val startWidth = startRight - startLeft + val startTopCornerRadius = state.topCornerRadius + val startBottomCornerRadius = state.bottomCornerRadius + + // End state. + var endTop = endState.top + var endBottom = endState.bottom + var endLeft = endState.left + var endRight = endState.right + var endCenterX = (endLeft + endRight) / 2f + var endWidth = endRight - endLeft + val endTopCornerRadius = endState.topCornerRadius + val endBottomCornerRadius = endState.bottomCornerRadius + + fun maybeUpdateEndState() { + if (endTop != endState.top || endBottom != endState.bottom || + endLeft != endState.left || endRight != endState.right) { + endTop = endState.top + endBottom = endState.bottom + endLeft = endState.left + endRight = endState.right + endCenterX = (endLeft + endRight) / 2f + endWidth = endRight - endLeft + } + } + + val launchContainer = controller.launchContainer + val isExpandingFullyAbove = isExpandingFullyAbove(launchContainer, endState) + + // We add an extra layer with the same color as the dialog/app splash screen background + // color, which is usually the same color of the app background. We first fade in this layer + // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the + // launch container and reveal the opening window. + val windowBackgroundLayer = GradientDrawable().apply { + setColor(windowBackgroundColor) + alpha = 0 + } + + // Update state. + val animator = ValueAnimator.ofFloat(0f, 1f) + animator.duration = if (isForTesting) 0 else ANIMATION_DURATION + animator.interpolator = Interpolators.LINEAR + + val launchContainerOverlay = launchContainer.overlay + var cancelled = false + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { + if (DEBUG) { + Log.d(TAG, "Animation started") + } + controller.onLaunchAnimationStart(isExpandingFullyAbove) + + // Add the drawable to the launch container overlay. Overlays always draw + // drawables after views, so we know that it will be drawn above any view added + // by the controller. + launchContainerOverlay.add(windowBackgroundLayer) + } + + override fun onAnimationEnd(animation: Animator?) { + if (DEBUG) { + Log.d(TAG, "Animation ended") + } + controller.onLaunchAnimationEnd(isExpandingFullyAbove) + launchContainerOverlay.remove(windowBackgroundLayer) + } + }) + + animator.addUpdateListener { animation -> + if (cancelled) { + // TODO(b/184121838): Cancel the animator directly instead of just skipping the + // update. + return@addUpdateListener + } + + maybeUpdateEndState() + + // TODO(b/184121838): Use reverse interpolators to get the same path/arc as the non + // reversed animation. + val linearProgress = animation.animatedFraction + val progress = animationInterpolator.getInterpolation(linearProgress) + val xProgress = animationInterpolatorX.getInterpolation(linearProgress) + + val xCenter = MathUtils.lerp(startCenterX, endCenterX, xProgress) + val halfWidth = MathUtils.lerp(startWidth, endWidth, progress) / 2f + + state.top = MathUtils.lerp(startTop, endTop, progress).roundToInt() + state.bottom = MathUtils.lerp(startBottom, endBottom, progress).roundToInt() + state.left = (xCenter - halfWidth).roundToInt() + state.right = (xCenter + halfWidth).roundToInt() + + state.topCornerRadius = + MathUtils.lerp(startTopCornerRadius, endTopCornerRadius, progress) + state.bottomCornerRadius = + MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress) + + // The expanding view can/should be hidden once it is completely covered by the opening + // window. + state.visible = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1 + + applyStateToWindowBackgroundLayer( + windowBackgroundLayer, + state, + linearProgress, + launchContainer, + drawHole + ) + controller.onLaunchAnimationProgress(state, progress, linearProgress) + } + + animator.start() + return object : Animation { + override fun cancel() { + cancelled = true + animator.cancel() + } + } + } + + /** Return whether we are expanding fully above the [launchContainer]. */ + internal fun isExpandingFullyAbove(launchContainer: View, endState: State): Boolean { + launchContainer.getLocationOnScreen(launchContainerLocation) + return endState.top <= launchContainerLocation[1] && + endState.bottom >= launchContainerLocation[1] + launchContainer.height && + endState.left <= launchContainerLocation[0] && + endState.right >= launchContainerLocation[0] + launchContainer.width + } + + private fun applyStateToWindowBackgroundLayer( + drawable: GradientDrawable, + state: State, + linearProgress: Float, + launchContainer: View, + drawHole: Boolean + ) { + // Update position. + launchContainer.getLocationOnScreen(launchContainerLocation) + drawable.setBounds( + state.left - launchContainerLocation[0], + state.top - launchContainerLocation[1], + state.right - launchContainerLocation[0], + state.bottom - launchContainerLocation[1] + ) + + // Update radius. + cornerRadii[0] = state.topCornerRadius + cornerRadii[1] = state.topCornerRadius + cornerRadii[2] = state.topCornerRadius + cornerRadii[3] = state.topCornerRadius + cornerRadii[4] = state.bottomCornerRadius + cornerRadii[5] = state.bottomCornerRadius + cornerRadii[6] = state.bottomCornerRadius + cornerRadii[7] = state.bottomCornerRadius + drawable.cornerRadii = cornerRadii + + // We first fade in the background layer to hide the expanding view, then fade it out + // with SRC mode to draw a hole punch in the status bar and reveal the opening window. + val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) + if (fadeInProgress < 1) { + val alpha = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(fadeInProgress) + drawable.alpha = (alpha * 0xFF).roundToInt() + } else { + val fadeOutProgress = getProgress( + linearProgress, ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW) + val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress) + drawable.alpha = (alpha * 0xFF).roundToInt() + + if (drawHole) { + drawable.setXfermode(SRC_MODE) + } + } + } +} diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml index dd35dd91f5d6..1eec8204a7f2 100644 --- a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml +++ b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml @@ -20,6 +20,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:fillColor="?android:attr/colorBackground" + android:fillColor="?android:attr/textColorPrimaryInverse" android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/> </vector> diff --git a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml index b844515f1088..2ad5e54eb92a 100644 --- a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml +++ b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml @@ -19,7 +19,8 @@ android:viewportHeight="36" android:viewportWidth="36" android:width="36sp"> - <path android:fillColor="?android:attr/colorBackground" + + <path android:fillColor="?android:attr/textColorPrimaryInverse" android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59, 13.41zM26,12v12h2V12H26z"/> </vector> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 624ee9f51b2a..6fd83c55b656 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -17,7 +17,8 @@ */ --> -<resources> +<resources + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <!-- Keyguard PIN pad styles --> <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView"> <item name="android:textSize">@dimen/kg_status_line_font_size</item> @@ -58,11 +59,11 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="NumPadKey.Delete"> - <item name="android:colorControlNormal">?android:attr/textColorSecondary</item> + <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item> <item name="android:src">@drawable/ic_backspace_24dp</item> </style> <style name="NumPadKey.Enter"> - <item name="android:colorControlNormal">?android:attr/textColorSecondary</item> + <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item> <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> </style> <style name="Widget.TextView.NumPadKey.Klondike" diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml deleted file mode 100644 index 3ceb0f6ac06a..000000000000 --- a/packages/SystemUI/res/drawable/media_output_dialog_background.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<inset xmlns:android="http://schemas.android.com/apk/res/android"> - <shape android:shape="rectangle"> - <corners android:radius="8dp" /> - <solid android:color="?android:attr/colorBackground" /> - </shape> -</inset> diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index 918635d666fa..c1d7308be5a8 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -19,7 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/internet_connectivity_dialog" - android:layout_width="@dimen/internet_dialog_list_max_width" + android:layout_width="@dimen/large_dialog_width" android:layout_height="@dimen/internet_dialog_list_max_height" android:background="@drawable/internet_dialog_rounded_top_corner_background" android:orientation="vertical"> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index d996cee4b39e..b33889469f48 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -18,7 +18,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/media_output_dialog" - android:layout_width="match_parent" + android:layout_width="@dimen/large_dialog_width" android:layout_height="wrap_content" android:orientation="vertical"> diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 566cd25e86a5..b546a9cbe90e 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -134,6 +134,7 @@ android:background="@drawable/qs_media_light_source" android:forceHasOverlappingRendering="false"> <LinearLayout + android:id="@+id/media_seamless_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minHeight="@dimen/qs_seamless_height" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 5cd99f29bbab..f589498c377a 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontsluit om netwerke te bekyk"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string> <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 2f485ada1186..e94dc688ac7c 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"አውታረ መረቦችን ለመመልከት ይክፈቱ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi ለአሁን በራስ-ሰር አይገናኝም"</string> <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 81a7f7c29ff2..14753dabb464 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1199,6 +1199,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"فتح القفل لعرض الشبكات"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"لن يتم الاتصال بشبكة Wi-Fi تلقائيًا في الوقت الحالي."</string> <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index d73fd7357c5d..a50d8c10dd3f 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটৱর্ক চাবলৈ আনলক কৰক"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এতিয়া ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string> <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index f7746e8b6397..e0ef1eca3282 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Şəbəkələrə baxmaq üçün kilidi açın"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hələlik avtomatik qoşulmayacaq"</string> <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 72efc72a36f2..31f8014e8b6c 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da biste videli mreže"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi trenutno ne može da se automatski poveže"</string> <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index f7c20ab54895..b296aa63eedb 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблакіраваць для прагляду сетак"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Аўтаматычнае падключэнне да Wi-Fi адсутнічае"</string> <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index b4164a003a1a..84c22912a322 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Отключване с цел преглед на мрежите"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Засега Wi-Fi няма да се свързва автоматично"</string> <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 92da7de7286e..93ac0404fe87 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -1175,6 +1175,8 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটওয়ার্ক দেখার জন্য আনলক করুন"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string> + <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) --> + <skip /> <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index e913d56be38b..c8588ecd4cdd 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da vidite mreže"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index be384e372993..cfe70c2a22c9 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueja per veure xarxes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Per ara la Wi‑Fi no es connectarà automàticament"</string> <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 5be30774348c..3d648cb03309 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sítě uvidíte po odemknutí"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se prozatím nebude připojovat automaticky"</string> <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 2c7ffe5154b1..eb715aa8e7ca 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås op for at se netværk"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Ingen automatisk forbindelse til Wi-Fi i øjeblikket"</string> <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 9c39acdd56f2..b8154923bd63 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Entsperren, um Netzwerke anzuzeigen"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Zurzeit wird keine automatische WLAN-Verbindung hergestellt"</string> <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 8705ad094e86..c6cc7f0d4212 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ξεκλειδώστε για προβολή δικτύων"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Δεν θα γίνεται προς το παρόν αυτόματη σύνδεση Wi-Fi."</string> <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index a2bcfd1a41eb..e1006fc83780 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string> <string name="see_all_networks" msgid="3773666844913168122">"See all"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 1cc3cd37a8e5..66b44d5ecf8b 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string> <string name="see_all_networks" msgid="3773666844913168122">"See all"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index a2bcfd1a41eb..e1006fc83780 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string> <string name="see_all_networks" msgid="3773666844913168122">"See all"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index a2bcfd1a41eb..e1006fc83780 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string> <string name="see_all_networks" msgid="3773666844913168122">"See all"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 71a1c2fb5f34..9af7322e8507 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string> <string name="see_all_networks" msgid="3773666844913168122">"See all"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 0454e73be3d8..8d53bb99f3e0 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver las redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora, el Wi-Fi no se conectará automáticamente"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 0751362138bf..c0c12fa7ddc8 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora no se conectará automáticamente a redes Wi-Fi"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 16920069b3db..c3cb5520db83 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Võrkude vaatamiseks avage"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi-ühendust ei looda praegu automaatselt"</string> <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 8055cfec1994..8df329a899e3 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -845,7 +845,7 @@ <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Arakatzailea"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Kontaktuak"</string> <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Posta"</string> - <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS mezuak"</string> + <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMSak"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musika"</string> <string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string> @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sareak ikusteko, desblokeatu pantaila"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Oraingoz ez da automatikoki konektatuko wifira"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 74f23ac3bbd8..ae308b7ed434 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"برای مشاهده شبکهها، قفل صفحه را باز کنید"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"فعلاً Wi-Fi بهطور خودکار متصل نمیشود"</string> <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index f1144072f0d3..ee0cdb65a83d 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Avaa lukitus nähdäksesi verkot"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ei toistaiseksi yhdistä automaattisesti"</string> <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 35ed848a17a3..7fcbcfb6eb0b 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouillez l\'écran pour afficher les réseaux Wi-Fi"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi impossible pour le moment"</string> <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 514c434be920..74e55fc90687 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouiller pour afficher les réseaux"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi désactivée pour le moment"</string> <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1eba1ed4d301..3bcfcc8e6a88 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea a pantalla para ver as redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"De momento, a wifi non se conectará automaticamente"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 2c4e6e5627fb..79281156e00b 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1175,6 +1175,8 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"વાઇ-ફાઇ નેટવર્ક જોવા માટે અનલૉક કરો"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string> + <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) --> + <skip /> <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 3eb45ea8ee82..edd7fc6799bf 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"वाई-फ़ाई नेटवर्क देखने के लिए, स्क्रीन को अनलॉक करें"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"फ़िलहाल, वाई-फ़ाई अपने-आप कनेक्ट नहीं होगा"</string> <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 97dbf591a84f..b066191b021a 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte za prikaz mreža"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se zasad neće automatski povezivati"</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index ddff2c20cb9c..5880cf0abb02 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Zárolás feloldása a hálózatok megtekintéséhez"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A Wi-Fi-re történő csatlakozás jelenleg nem automatikus"</string> <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 3100b9c72000..ad3cf25467d9 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ապակողպեք՝ ցանցերը դիտելու համար"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-ն ավտոմատ չի միանա"</string> <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 80f6ed4d4ed7..c42b143709c1 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat jaringan"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan otomatis terhubung untuk saat ini"</string> <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 2732b71dca14..469228065c8d 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Taktu úr lás til að skoða netkerfi"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tengist ekki sjálfkrafa eins og er"</string> <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 9410c99ad54c..1d96598be708 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sblocca per visualizzare le reti"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connessione automatica rete Wi-Fi non attiva al momento"</string> <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index f6b74c389305..8fd2bb9abf22 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"צריך לבטל את הנעילה כדי להציג את הרשתות"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string> <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index aa6aa3c7f374..3134f73605c1 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ネットワークを表示するにはロック解除してください"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi に自動接続しません"</string> <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index ac604701827d..c4c73c5cb912 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"განბლოკვა ქსელების სანახავად"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ინტერნეტს დროებით ავტომატურად არ დაუკავშირდება"</string> <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index fd2bcc7df5a2..bccc0ab7d937 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Желілерді көру үшін құлыпты ашыңыз."</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Әзірше Wi-Fi автоматты түрде қосылмайды."</string> <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 035319ef682b..7d79023c1c61 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ដោះសោដើម្បីមើលបណ្ដាញ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិនអាចភ្ជាប់បណ្ដាញបានទេ"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string> <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 1d8fbb3fe777..0a28735aadd1 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -1175,6 +1175,8 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string> + <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) --> + <skip /> <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 751bfd8a8e32..3a8657ba30a6 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"네트워크를 보려면 잠금 해제하세요"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"지금은 Wi-Fi가 자동으로 연결되지 않습니다."</string> <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 343457226160..9f242f34b300 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Тармактарды көрүү үчүн кулпусун ачыңыз"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi азырынча автоматтык түрдө туташпайт"</string> <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index b603ca6ac06e..afd914d738ae 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ປົດລັອກເພື່ອເບິ່ງເຄືອຂ່າຍ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດສຳລັບຕອນນີ້"</string> <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 657c95b79534..233610f00cd0 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Atrakinkite, kad peržiūrėtumėte visus tinklus"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"„Wi-Fi“ šiuo metu nebus prijungtas automatiškai"</string> <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index f5927db7c26c..83e6d6e514ea 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lai skatītu tīklus, atbloķējiet"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi savienojums īslaicīgi netiks izveidots automātiski."</string> <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 396fab5d1444..e00108a7b9fb 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Отклучете за да се прикажат мрежите"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi нема да се поврзува автоматски засега"</string> <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 6d92e8a46b5e..49beea2dcb32 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"നെറ്റ്വർക്കുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്വർക്കുകൾ തിരയുന്നു…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"വൈഫൈ ഇപ്പോൾ സ്വയമേവ കണക്റ്റ് ചെയ്യില്ല"</string> <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 3968d52a84f4..f6dea18f0417 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Сүлжээг харахын тулд түгжээг тайлах"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-г одоогоор автоматаар холбохгүй"</string> <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 69c829c496c8..6156cf592def 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्क पाहण्यासाठी स्क्रीन अनलॉक करा"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्ट करता आले नाही"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"सध्या वाय-फाय ऑटो-कनेक्ट होणार नाही"</string> <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index ebe0094d3392..9dde6b667bc5 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat rangkaian"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan disambungkan secara automatik buat masa ini"</string> <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 42ed54c91ac4..50fb26c7a297 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ကွန်ရက်များကြည့်ရန် ဖွင့်ပါ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က ယခု အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string> <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 3337aa1daeef..d4eef721d76e 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås opp for å se nettverk"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi kobles ikke til automatisk inntil videre"</string> <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index a7113f3c0081..ce1787c26897 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्कहरू हेर्न आफ्नो स्क्रिन अनलक गर्नुहोस्"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"केही समयका लागि Wi-Fi स्वतः कनेक्ट हुँदैन"</string> <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index ea9822980b07..ad1403e664df 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontgrendel het scherm om netwerken te bekijken"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string> <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 1ea599f12c20..e5c2a2b61825 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ନେଟୱାର୍କଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ବର୍ତ୍ତମାନ ପାଇଁ ୱାଇ-ଫାଇ ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string> <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 06364eaabe1f..48750cf95731 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ਫ਼ਿਲਹਾਲ ਵਾਈ-ਫਾਈ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string> <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index ae4b5d60727b..3635876b04da 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odblokuj, by wyświetlić sieci"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nie będzie na razie włączać się automatycznie"</string> <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index cbd43f878e08..8f499f7fbb04 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index a8f62585dc38..b0c65d866b78 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por agora, o Wi-Fi não irá estabelecer lig. automaticamente"</string> <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index cbd43f878e08..8f499f7fbb04 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string> <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 751ea3ac4270..c143d5349ad4 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblocați pentru a vedea rețelele"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string> <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 4452a0206c7e..752110ea59ec 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблокируйте, чтобы посмотреть сети Wi-Fi."</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Подключение по Wi-Fi не установится автоматически."</string> <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index e87aefb1db58..b3a7a8e3cf7f 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ජාල බැලීමට අගුලු හරින්න"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi දැනට ස්වයං-සබැඳි නොවනු ඇත"</string> <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index a1719f060ece..f45226b41ffc 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odomknutím si zobrazte siete"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi sa teraz automaticky nepripojí"</string> <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 7d86eb1f94df..ed753906d8f1 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odklenite za ogled omrežij"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Vmesnik Wi-Fi trenutno ne bo samodejno vzpostavil povezave."</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index c5eb836f3d95..dc7e82564df0 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Shkyçe për të parë rrjetet"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nuk do të lidhet automatikisht për momentin"</string> <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index b75535a132bf..b486589b3bd5 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -1181,6 +1181,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Откључајте да бисте видели мреже"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi тренутно не може да се аутоматски повеже"</string> <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index b0ef22502cd2..5db089bccc58 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås upp för att visa nätverk"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string> <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index fa1517a23802..60104f623848 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -82,7 +82,7 @@ <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string> - <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili utazame picha ya skrini uliyohifadhi"</string> + <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili uone picha ya skrini uliyohifadhi"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Imeshindwa kuhifadhi picha ya skrini"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Ni sharti ufungue kifaa kabla ya kuhifadhi picha ya skrini"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string> @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Fungua ili uangalie mitandao"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi haitaunganishwa kiotomatiki kwa sasa"</string> <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index d921d4958090..85f8f0957a08 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -104,6 +104,6 @@ <!-- When split shade is used, this panel should be aligned to the top --> <dimen name="qs_detail_margin_top">0dp</dimen> - <!-- Internet panel related dimensions --> - <dimen name="internet_dialog_list_max_width">624dp</dimen> + <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> + <dimen name="large_dialog_width">624dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 5e6e4c60dc89..8cc4c8dd5336 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"நெட்வொர்க்குகளைப் பார்க்க அன்லாக் செய்யுங்கள்"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"தற்போதைக்கு வைஃபை தானாக இணைக்கப்படாது"</string> <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index f4d93ac3fcee..dda2830bbc8c 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"నెట్వర్క్లను చూడటానికి అన్లాక్ చేయండి"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్వర్క్ల కోసం సెర్చ్ చేస్తోంది…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్వర్క్కు కనెక్ట్ చేయడం విఫలమైంది"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ప్రస్తుతానికి Wi-Fi ఆటోమేటిక్గా కనెక్ట్ అవ్వదు"</string> <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్వర్క్లను మార్చడానికి, ఈథర్నెట్ను డిస్కనెక్ట్ చేయండి"</string> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index d0603a9ea149..91a24403f4d0 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ปลดล็อกเพื่อดูเครือข่าย"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi จะไม่เชื่อมต่ออัตโนมัติในตอนนี้"</string> <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index bb1cbe8d8ee9..dcde0fc67584 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"I-unlock para tingnan ang mga network"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Hindi awtomatikong kokonekta ang Wi-Fi sa ngayon"</string> <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 15b702c384ee..367d0bf3e372 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ağları görmek için kilidi açın"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Şu anda kablosuz ağa otomatik olarak bağlanılamıyor"</string> <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index dc30e0beaafb..e692a36985b1 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -1187,6 +1187,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Розблокувати, щоб переглянути мережі"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Пристрій не підключатиметься до Wi-Fi автоматично"</string> <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 667a9577ef3e..da431826c278 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1175,6 +1175,8 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"نیٹ ورکس کو دیکھنے کے لیے غیر مقفل کریں"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string> + <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) --> + <skip /> <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index b02ef403bfeb..f0afb6cb704b 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Tarmoqlarni koʻrish uchun qulfdan chiqaring"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hozir avtomatik ulanmaydi"</string> <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index d046494e2134..b0f1dcf31133 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Mở khóa để xem mạng"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Tạm thời, Wi-Fi sẽ không tự động kết nối"</string> <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index c7747fac198e..7caae5df46e5 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解锁即可查看网络"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WLAN 暂时无法自动连接"</string> <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index e7ffeeae336e..4a8567d7d29b 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖即可查看網絡"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前系統不會自動連線至 Wi-Fi"</string> <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index b49cd2b1e7cc..b444d4223b7f 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖螢幕即可查看網路"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前不會自動連上 Wi-Fi"</string> <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index d4fb489807cb..79884764562a 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1175,6 +1175,7 @@ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Vula ukuze ubuke amanethiwekhi"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string> + <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"I-Wi-Fi ngeke ixhume ngokuzenzakalelayo okwamanje"</string> <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 08778bf14c66..03c6fddb145c 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -70,7 +70,7 @@ <color name="keyguard_shadow_color">#B2000000</color> <!-- Color for the images in keyguard number pad buttons --> - <color name="keyguard_keypad_image_color">@android:color/background_light</color> + <color name="keyguard_keypad_image_color">?android:attr/textColorPrimaryInverse</color> <!-- Color for rounded background for activated user in keyguard user switcher --> <color name="kg_user_switcher_activated_background_color">#26000000</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 70149267e3dd..8650654ea288 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1596,7 +1596,9 @@ <!-- Internet panel related dimensions --> <dimen name="internet_dialog_list_margin">12dp</dimen> <dimen name="internet_dialog_list_max_height">646dp</dimen> - <dimen name="internet_dialog_list_max_width">@dimen/match_parent</dimen> + + <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> + <dimen name="large_dialog_width">@dimen/match_parent</dimen> <!-- Signal icon in internet dialog --> <dimen name="signal_strength_icon_size">24dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 93d60cce2915..6594216e4290 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -331,9 +331,6 @@ <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast"> </style> - <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod"> - </style> - <!-- Standard animations for hiding and showing the status bar. --> <style name="Animation.StatusBar"> </style> @@ -436,10 +433,6 @@ <item name="android:windowCloseOnTouchOutside">true</item> </style> - <style name="Theme.SystemUI.Dialog.MediaOutput"> - <item name="android:windowBackground">@drawable/media_output_dialog_background</item> - </style> - <style name="QSBorderlessButton"> <item name="android:padding">12dp</item> <item name="android:background">@drawable/qs_btn_borderless_rect</item> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index 92f8454fc93e..0529cdbcbb13 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -208,8 +208,7 @@ public class KeyguardPasswordViewController mView.post(() -> { if (mView.isShown()) { mPasswordEntry.requestFocus(); - mInputMethodManager.showSoftInput( - mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + mPasswordEntry.getWindowInsetsController().show(WindowInsets.Type.ime()); } }); } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index c659bf786a45..f4ce643a085c 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -18,6 +18,7 @@ package com.android.keyguard; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.VectorDrawable; @@ -26,8 +27,6 @@ import android.view.MotionEvent; import androidx.annotation.Nullable; -import com.android.systemui.R; - /** * Similar to the {@link NumPadKey}, but displays an image. */ @@ -92,7 +91,10 @@ public class NumPadButton extends AlphaOptimizedImageButton { public void reloadColors() { if (mAnimator != null) mAnimator.reloadColors(getContext()); - int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color); + int[] customAttrs = {android.R.attr.textColorPrimaryInverse}; + TypedArray a = getContext().obtainStyledAttributes(customAttrs); + int imageColor = a.getColor(0, 0); + a.recycle(); ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index e7445f920ffe..424f80177d9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -364,9 +364,9 @@ public class MediaControlPanel { seamlessView.setVisibility(View.VISIBLE); setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */); setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */); - seamlessView.setOnClickListener(v -> { - mMediaOutputDialogFactory.create(data.getPackageName(), true); - }); + seamlessView.setOnClickListener( + v -> mMediaOutputDialogFactory.create(data.getPackageName(), true, + mPlayerViewHolder.getSeamlessButton())); ImageView iconView = mPlayerViewHolder.getSeamlessIcon(); TextView deviceName = mPlayerViewHolder.getSeamlessText(); diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 35603b6ef6cc..f32dad632721 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -43,6 +43,7 @@ class PlayerViewHolder private constructor(itemView: View) { val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless) val seamlessIcon = itemView.requireViewById<ImageView>(R.id.media_seamless_image) val seamlessText = itemView.requireViewById<TextView>(R.id.media_seamless_text) + val seamlessButton = itemView.requireViewById<View>(R.id.media_seamless_button) // Seek bar val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar) diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 391dff634dab..d1b6548132a6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -45,11 +45,14 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final String TAG = "MediaOutputAdapter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private final MediaOutputDialog mMediaOutputDialog; private ViewGroup mConnectedItem; private boolean mIncludeDynamicGroup; - public MediaOutputAdapter(MediaOutputController controller) { + public MediaOutputAdapter(MediaOutputController controller, + MediaOutputDialog mediaOutputDialog) { super(controller); + mMediaOutputDialog = mediaOutputDialog; } @Override @@ -136,7 +139,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mDivider.setTransitionAlpha(1); mAddIcon.setVisibility(View.VISIBLE); mAddIcon.setTransitionAlpha(1); - mAddIcon.setOnClickListener(v -> onEndItemClick()); + mAddIcon.setOnClickListener(this::onEndItemClick); } else { // Init non-active device layout mDivider.setVisibility(View.GONE); @@ -197,7 +200,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mDivider.setTransitionAlpha(1); mAddIcon.setVisibility(View.VISIBLE); mAddIcon.setTransitionAlpha(1); - mAddIcon.setOnClickListener(v -> onEndItemClick()); + mAddIcon.setOnClickListener(this::onEndItemClick); } else { mDivider.setVisibility(View.GONE); mAddIcon.setVisibility(View.GONE); @@ -232,8 +235,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } } - private void onEndItemClick() { - mController.launchMediaOutputGroupDialog(); + private void onEndItemClick(View view) { + mController.launchMediaOutputGroupDialog(mMediaOutputDialog.getDialogView()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index cdcdf9a1d4de..85d0802012ac 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -82,7 +82,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements }; public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) { - super(context, R.style.Theme_SystemUI_Dialog_MediaOutput); + super(context); mContext = context; mMediaOutputController = mediaOutputController; mLayoutManager = new LinearLayoutManager(mContext); @@ -97,15 +97,15 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null); final Window window = getWindow(); final WindowManager.LayoutParams lp = window.getAttributes(); - lp.gravity = Gravity.BOTTOM; + lp.gravity = Gravity.CENTER; // Config insets to make sure the layout is above the navigation bar lp.setFitInsetsTypes(statusBars() | navigationBars()); lp.setFitInsetsSides(WindowInsets.Side.all()); lp.setFitInsetsIgnoringVisibility(true); window.setAttributes(lp); window.setContentView(mDialogView); - window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - window.setWindowAnimations(R.style.Animation_MediaOutputDialog); + window.setLayout(mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width), + ViewGroup.LayoutParams.WRAP_CONTENT); mHeaderTitle = mDialogView.requireViewById(R.id.header_title); mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); @@ -229,4 +229,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements void onHeaderIconClick() { } + + View getDialogView() { + return mDialogView; + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index b2def7a8596a..437a0c85a6e0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -32,6 +32,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; import android.util.Log; +import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -48,6 +49,7 @@ import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputConstants; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.R; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -73,6 +75,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { private final MediaSessionManager mMediaSessionManager; private final ShadeController mShadeController; private final ActivityStarter mActivityStarter; + private final DialogLaunchAnimator mDialogLaunchAnimator; private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>(); private final boolean mAboveStatusbar; private final NotificationEntryManager mNotificationEntryManager; @@ -82,6 +85,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { private MediaController mMediaController; @VisibleForTesting Callback mCallback; + Callback mPreviousCallback; @VisibleForTesting LocalMediaManager mLocalMediaManager; @@ -92,7 +96,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { public MediaOutputController(@NonNull Context context, String packageName, boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager lbm, ShadeController shadeController, ActivityStarter starter, - NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger) { + NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger, + DialogLaunchAnimator dialogLaunchAnimator) { mContext = context; mPackageName = packageName; mMediaSessionManager = mediaSessionManager; @@ -104,6 +109,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName); mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName); mUiEventLogger = uiEventLogger; + mDialogLaunchAnimator = dialogLaunchAnimator; } void start(@NonNull Callback cb) { @@ -129,7 +135,19 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { } return; } + + if (mPreviousCallback != null) { + Log.w(TAG, + "Callback started when mPreviousCallback is not null, which is unexpected"); + mPreviousCallback.dismissDialog(); + } + + // If we start the output group dialog when the output dialog is shown, we need to keep a + // reference to the output dialog to set it back as the callback once we dismiss the output + // group dialog. + mPreviousCallback = mCallback; mCallback = cb; + mLocalMediaManager.unregisterCallback(this); mLocalMediaManager.stopScan(); mLocalMediaManager.registerCallback(this); @@ -145,6 +163,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { mLocalMediaManager.stopScan(); } mMediaDevices.clear(); + + // If there was a previous callback, i.e. we just dismissed the output group dialog and are + // now back on the output dialog, then we reset the callback to its previous value. + mCallback = null; + Callback previous = mPreviousCallback; + mPreviousCallback = null; + if (previous != null) { + start(previous); + } } @Override @@ -436,6 +463,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { } void launchBluetoothPairing() { + // Dismissing a dialog into its touch surface and starting an activity at the same time + // looks bad, so let's make sure the dialog just fades out quickly. + mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); + mCallback.dismissDialog(); final ActivityStarter.OnDismissAction postKeyguardAction = () -> { mContext.sendBroadcast(new Intent() @@ -447,14 +478,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true); } - void launchMediaOutputDialog() { - mCallback.dismissDialog(); - new MediaOutputDialog(mContext, mAboveStatusbar, this, mUiEventLogger); - } - - void launchMediaOutputGroupDialog() { - mCallback.dismissDialog(); - new MediaOutputGroupDialog(mContext, mAboveStatusbar, this); + void launchMediaOutputGroupDialog(View mediaOutputDialog) { + // We show the output group dialog from the output dialog. + MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar, this); + mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog); } boolean isActiveRemoteDevice(@NonNull MediaDevice device) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index 53029bd04ef6..eca8ac90427b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -40,11 +40,10 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { mediaOutputController, UiEventLogger uiEventLogger) { super(context, mediaOutputController); mUiEventLogger = uiEventLogger; - mAdapter = new MediaOutputAdapter(mMediaOutputController); + mAdapter = new MediaOutputAdapter(mMediaOutputController, this); if (!aboveStatusbar) { getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } - show(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index 0f340a5cedaa..b91901de5af3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -18,8 +18,10 @@ package com.android.systemui.media.dialog import android.content.Context import android.media.session.MediaSessionManager +import android.view.View import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.phone.ShadeController @@ -35,19 +37,29 @@ class MediaOutputDialogFactory @Inject constructor( private val shadeController: ShadeController, private val starter: ActivityStarter, private val notificationEntryManager: NotificationEntryManager, - private val uiEventLogger: UiEventLogger + private val uiEventLogger: UiEventLogger, + private val dialogLaunchAnimator: DialogLaunchAnimator ) { companion object { var mediaOutputDialog: MediaOutputDialog? = null } /** Creates a [MediaOutputDialog] for the given package. */ - fun create(packageName: String, aboveStatusBar: Boolean) { + fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) { + // Dismiss the previous dialog, if any. mediaOutputDialog?.dismiss() - mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar, - mediaSessionManager, lbm, shadeController, starter, notificationEntryManager, - uiEventLogger).run { - MediaOutputDialog(context, aboveStatusBar, this, uiEventLogger) + + val controller = MediaOutputController(context, packageName, aboveStatusBar, + mediaSessionManager, lbm, shadeController, starter, notificationEntryManager, + uiEventLogger, dialogLaunchAnimator) + val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger) + mediaOutputDialog = dialog + + // Show the dialog. + if (view != null) { + dialogLaunchAnimator.showFromView(dialog, view) + } else { + dialog.show() } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java index 407930492fbe..1300400f3b66 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java @@ -38,7 +38,6 @@ public class MediaOutputGroupDialog extends MediaOutputBaseDialog { if (!aboveStatusbar) { getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } - show(); } @Override @@ -83,6 +82,8 @@ public class MediaOutputGroupDialog extends MediaOutputBaseDialog { @Override void onHeaderIconClick() { - mMediaOutputController.launchMediaOutputDialog(); + // Given that we launched the media output group dialog from the media output dialog, + // dismissing this dialog will show the media output dialog again. + dismiss(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index dc54e1b52f2e..11430d93106a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -206,7 +206,7 @@ public class InternetDialog extends SystemUIDialog implements window.setContentView(mDialogView); //Only fix the width for large screen or tablet. window.setLayout(mContext.getResources().getDimensionPixelSize( - R.dimen.internet_dialog_list_max_width), ViewGroup.LayoutParams.WRAP_CONTENT); + R.dimen.large_dialog_width), ViewGroup.LayoutParams.WRAP_CONTENT); window.setWindowAnimations(R.style.Animation_InternetDialog); window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); window.addFlags(FLAG_LAYOUT_NO_LIMITS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 17bcfe73ccd9..8a397199dc84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -335,7 +335,7 @@ public class KeyguardIndicationController { info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); } } - if (info != null) { + if (!TextUtils.isEmpty(info)) { mRotateTextViewController.updateIndication( INDICATION_TYPE_OWNER_INFO, new KeyguardIndication.Builder() @@ -433,7 +433,7 @@ public class KeyguardIndicationController { } private void updateResting() { - if (mRestingIndication != null + if (!TextUtils.isEmpty(mRestingIndication) && !mRotateTextViewController.hasIndications()) { mRotateTextViewController.updateIndication( INDICATION_TYPE_RESTING, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index 94f186f00778..ea00d920c9b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -22,6 +22,9 @@ import android.content.Context; import android.os.Handler; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -62,6 +65,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; +import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.RemoteInputUriController; @@ -261,4 +265,29 @@ public interface StatusBarDependenciesModule { @Binds QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver( QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl); + + /** + */ + @Provides + @SysUISingleton + static LaunchAnimator provideLaunchAnimator(Context context) { + return new LaunchAnimator(context); + } + + /** + */ + @Provides + @SysUISingleton + static ActivityLaunchAnimator provideActivityLaunchAnimator(LaunchAnimator launchAnimator) { + return new ActivityLaunchAnimator(launchAnimator); + } + + /** + */ + @Provides + @SysUISingleton + static DialogLaunchAnimator provideDialogLaunchAnimator(Context context, + LaunchAnimator launchAnimator) { + return new DialogLaunchAnimator(context, launchAnimator, new SystemUIHostDialogProvider()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index d7f32251e3d9..a76389b1bbc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -24,7 +24,6 @@ import android.app.smartspace.SmartspaceTarget import android.content.ContentResolver import android.content.Context import android.content.Intent -import android.content.pm.UserInfo import android.database.ContentObserver import android.net.Uri import android.os.Handler @@ -193,7 +192,7 @@ class LockscreenSmartspaceController @Inject constructor( } private fun connectSession() { - if (plugin == null || session != null) { + if (plugin == null || session != null || smartspaceViews.isEmpty()) { return } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt index f19cf5d8d9c7..64a73054c434 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt @@ -2,8 +2,8 @@ package com.android.systemui.statusbar.notification import android.util.MathUtils import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.Interpolators +import com.android.systemui.animation.LaunchAnimator import kotlin.math.min /** Parameters for the notifications expand animations. */ @@ -15,7 +15,7 @@ class ExpandAnimationParameters( topCornerRadius: Float = 0f, bottomCornerRadius: Float = 0f -) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) { +) : LaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) { @VisibleForTesting constructor() : this( top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f @@ -55,6 +55,6 @@ class ExpandAnimationParameters( } fun getProgress(delay: Long, duration: Long): Float { - return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration) + return LaunchAnimator.getProgress(linearProgress, delay, duration) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index 1bbef2562d21..22c3eda03b1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -3,6 +3,7 @@ package com.android.systemui.statusbar.notification import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.LaunchAnimator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.HeadsUpManagerPhone @@ -54,7 +55,7 @@ class NotificationLaunchAnimatorController( // Do nothing. Notifications are always animated inside their rootView. } - override fun createAnimatorState(): ActivityLaunchAnimator.State { + override fun createAnimatorState(): LaunchAnimator.State { // If the notification panel is collapsed, the clip may be larger than the height. val height = max(0, notification.actualHeight - notification.clipBottomAmount) val location = notification.locationOnScreen @@ -72,12 +73,12 @@ class NotificationLaunchAnimatorController( notification.currentBackgroundRadiusTop } val params = ExpandAnimationParameters( - top = windowTop, - bottom = location[1] + height, - left = location[0], - right = location[0] + notification.width, - topCornerRadius = topCornerRadius, - bottomCornerRadius = notification.currentBackgroundRadiusBottom + top = windowTop, + bottom = location[1] + height, + left = location[0], + right = location[0] + notification.width, + topCornerRadius = topCornerRadius, + bottomCornerRadius = notification.currentBackgroundRadiusBottom ) params.startTranslationZ = notification.translationZ @@ -86,8 +87,8 @@ class NotificationLaunchAnimatorController( params.startClipTopAmount = notification.clipTopAmount if (notification.isChildInGroup) { params.startNotificationTop += notification.notificationParent.translationY - val parentRoundedClip = Math.max(clipStartLocation - - notification.notificationParent.locationOnScreen[1], 0) + val parentRoundedClip = Math.max( + clipStartLocation - notification.notificationParent.locationOnScreen[1], 0) params.parentStartRoundedTopClipping = parentRoundedClip val parentClip = notification.notificationParent.clipTopAmount @@ -157,7 +158,7 @@ class NotificationLaunchAnimatorController( } override fun onLaunchAnimationProgress( - state: ActivityLaunchAnimator.State, + state: LaunchAnimator.State, progress: Float, linearProgress: Float ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 9018cef72f7d..6d6cb7dcc693 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -103,8 +103,8 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; +import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; @@ -212,7 +212,7 @@ public class NotificationPanelViewController extends PanelViewController { */ private static final int FLING_HIDE = 2; private static final long ANIMATION_DELAY_ICON_FADE_IN = - ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION + LaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY - 48; private final DozeParameters mDozeParameters; @@ -3594,7 +3594,7 @@ public class NotificationPanelViewController extends PanelViewController { } public void applyLaunchAnimationProgress(float linearProgress) { - boolean hideIcons = ActivityLaunchAnimator.getProgress(linearProgress, + boolean hideIcons = LaunchAnimator.getProgress(linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; if (hideIcons != mHideIconsDuringLaunchAnimation) { mHideIconsDuringLaunchAnimation = hideIcons; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index ee7725f670b0..e104aef2e8b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -135,6 +135,7 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DelegateLaunchAnimatorController; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.biometrics.AuthRippleController; @@ -669,7 +670,8 @@ public class StatusBar extends SystemUI implements private final SysuiStatusBarStateController mStatusBarStateController; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private ActivityLaunchAnimator mActivityLaunchAnimator; + private final ActivityLaunchAnimator mActivityLaunchAnimator; + private final DialogLaunchAnimator mDialogLaunchAnimator; private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; @@ -792,7 +794,9 @@ public class StatusBar extends SystemUI implements UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, Optional<StartingSurface> startingSurfaceOptional, TunerService tunerService, - DumpManager dumpManager) { + DumpManager dumpManager, + ActivityLaunchAnimator activityLaunchAnimator, + DialogLaunchAnimator dialogLaunchAnimator) { super(context); mNotificationsController = notificationsController; mLightBarController = lightBarController; @@ -900,6 +904,8 @@ public class StatusBar extends SystemUI implements }); mActivityIntentHelper = new ActivityIntentHelper(mContext); + mActivityLaunchAnimator = activityLaunchAnimator; + mDialogLaunchAnimator = dialogLaunchAnimator; // TODO(b/190746471): Find a better home for this. DateTimeView.setReceiverHandler(timeTickHandler); @@ -1467,7 +1473,7 @@ public class StatusBar extends SystemUI implements private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext); + mActivityLaunchAnimator.setCallback(mKeyguardHandler); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), @@ -2549,7 +2555,8 @@ public class StatusBar extends SystemUI implements animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch( true /* isActivityIntent */); ActivityLaunchAnimator.Controller animController = - animate ? wrapAnimationController(animationController, dismissShade) : null; + animationController != null ? wrapAnimationController(animationController, + dismissShade) : null; // If we animate, we will dismiss the shade only once the animation is done. This is taken // care of by the StatusBarLaunchAnimationController. @@ -4426,6 +4433,8 @@ public class StatusBar extends SystemUI implements && !mBiometricUnlockController.isWakeAndUnlock()) { mLightRevealScrim.setRevealAmount(1f - linear); } + + mDialogLaunchAnimator.onDozeAmountChanged(linear); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 77254435b688..5bc97a5b316d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -53,6 +53,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.FaceAuthScreenBrightnessController; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; @@ -94,7 +95,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // with the appear animations of the PIN/pattern/password views. private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320; - private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200; + // The duration to fade the nav bar content in/out when the device starts to sleep + private static final long NAV_BAR_CONTENT_FADE_DURATION = 125; // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to // make everything a bit slower to bridge a gap until the user is unlocked and home screen has @@ -197,10 +199,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastGesturalNav; private boolean mLastIsDocked; private boolean mLastPulsing; - private boolean mLastAnimatedToSleep; private int mLastBiometricMode; private boolean mQsExpanded; - private boolean mAnimatedToSleep; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -325,20 +325,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); } - mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() { - @Override - public void onFinishedWakingUp() { - mAnimatedToSleep = false; - updateStates(); - } - - @Override - public void onFinishedGoingToSleep() { - mAnimatedToSleep = - mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying(); - updateStates(); - } - }); } @Override @@ -571,12 +557,26 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void onStartedWakingUp() { mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() .setAnimationsDisabled(false); + View currentView = getCurrentNavBarView(); + if (currentView != null) { + currentView.animate() + .alpha(1f) + .setDuration(NAV_BAR_CONTENT_FADE_DURATION) + .start(); + } } @Override public void onStartedGoingToSleep() { mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() .setAnimationsDisabled(true); + View currentView = getCurrentNavBarView(); + if (currentView != null) { + currentView.animate() + .alpha(0f) + .setDuration(NAV_BAR_CONTENT_FADE_DURATION) + .start(); + } } @Override @@ -1013,10 +1013,28 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLastBiometricMode = mBiometricUnlockController.getMode(); mLastGesturalNav = mGesturalNav; mLastIsDocked = mIsDocked; - mLastAnimatedToSleep = mAnimatedToSleep; mStatusBar.onKeyguardViewManagerStatesUpdated(); } + /** + * Updates the visibility of the nav bar content views. + */ + private void updateNavigationBarContentVisibility(boolean navBarContentVisible) { + final NavigationBarView navBarView = mStatusBar.getNavigationBarView(); + if (navBarView != null && navBarView.getCurrentView() != null) { + final View currentView = navBarView.getCurrentView(); + currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE); + } + } + + private View getCurrentNavBarView() { + final NavigationBarView navBarView = mStatusBar.getNavigationBarView(); + return navBarView != null ? navBarView.getCurrentView() : null; + } + + /** + * Updates the visibility of the nav bar window (which will cause insets changes). + */ protected void updateNavigationBarVisibility(boolean navBarVisible) { if (mStatusBar.getNavigationBarView() != null) { if (navBarVisible) { @@ -1044,7 +1062,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING; boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked) && mGesturalNav; - return (!mAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mBouncer.isShowing() + return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav || mGlobalActionsVisible); } @@ -1057,7 +1075,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING; boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing || mLastPulsing && !mLastIsDocked) && mLastGesturalNav; - return (!mLastAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mLastBouncerShowing + return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav || mLastGlobalActionsVisible); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt index 14e513a0556d..32aae6c05df6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt @@ -1,6 +1,7 @@ package com.android.systemui.statusbar.phone import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.LaunchAnimator /** * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right @@ -22,7 +23,7 @@ class StatusBarLaunchAnimatorController( delegate.onLaunchAnimationStart(isExpandingFullyAbove) statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true) if (!isExpandingFullyAbove) { - statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt()) + statusBar.collapsePanelWithDuration(LaunchAnimator.ANIMATION_DURATION.toInt()) } } @@ -33,7 +34,7 @@ class StatusBarLaunchAnimatorController( } override fun onLaunchAnimationProgress( - state: ActivityLaunchAnimator.State, + state: LaunchAnimator.State, progress: Float, linearProgress: Float ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index 1e98c75f2616..9415d5082d10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -30,19 +30,24 @@ import android.view.WindowManager.LayoutParams; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.animation.DialogListener; +import com.android.systemui.animation.ListenableDialog; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.policy.KeyguardStateController; +import java.util.LinkedHashSet; +import java.util.Set; + /** * Base class for dialogs that should appear over panels and keyguard. * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast, * and dismisses itself when it receives the broadcast. */ -public class SystemUIDialog extends AlertDialog { - +public class SystemUIDialog extends AlertDialog implements ListenableDialog { private final Context mContext; private final DismissReceiver mDismissReceiver; + private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>(); public SystemUIDialog(Context context) { this(context, R.style.Theme_SystemUI_Dialog); @@ -72,6 +77,43 @@ public class SystemUIDialog extends AlertDialog { mDismissReceiver.unregister(); } + @Override + public void addListener(DialogListener listener) { + mDialogListeners.add(listener); + } + + @Override + public void removeListener(DialogListener listener) { + mDialogListeners.remove(listener); + } + + @Override + public void dismiss() { + super.dismiss(); + + for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) { + listener.onDismiss(); + } + } + + @Override + public void hide() { + super.hide(); + + for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) { + listener.onHide(); + } + } + + @Override + public void show() { + super.show(); + + for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) { + listener.onShow(); + } + } + public void setShowForAllUsers(boolean show) { setShowForAllUsers(this, show); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt new file mode 100644 index 000000000000..6a49a6da0d62 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt @@ -0,0 +1,36 @@ +package com.android.systemui.statusbar.phone + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import com.android.systemui.animation.HostDialogProvider + +/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */ +class SystemUIHostDialogProvider : HostDialogProvider { + override fun createHostDialog( + context: Context, + theme: Int, + onCreateCallback: () -> Unit, + dismissOverride: (() -> Unit) -> Unit + ): Dialog { + return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride) + } + + private class SystemUIHostDialog( + context: Context, + theme: Int, + private val onCreateCallback: () -> Unit, + private val dismissOverride: (() -> Unit) -> Unit + ) : SystemUIDialog(context, theme) { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + onCreateCallback() + } + + override fun dismiss() { + dismissOverride { + super.dismiss() + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 13eb75a4d508..0d43b9323a39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -28,6 +28,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; +import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; @@ -235,7 +237,9 @@ public interface StatusBarPhoneModule { UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, Optional<StartingSurface> startingSurfaceOptional, TunerService tunerService, - DumpManager dumpManager) { + DumpManager dumpManager, + ActivityLaunchAnimator activityLaunchAnimator, + DialogLaunchAnimator dialogLaunchAnimator) { return new StatusBar( context, notificationsController, @@ -333,6 +337,8 @@ public interface StatusBarPhoneModule { unlockedScreenOffAnimationController, startingSurfaceOptional, tunerService, - dumpManager); + dumpManager, + activityLaunchAnimator, + dialogLaunchAnimator); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt index db2aca873d0c..a9810458fe90 100644 --- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt @@ -40,8 +40,8 @@ class WallpaperController @Inject constructor(private val wallpaperManager: Wall this.wallpaperInfo = wallpaperInfo } - private val shouldUseDefaultUnfoldTransition: Boolean - get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition() + private val shouldUseDefaultDeviceStateChangeTransition: Boolean + get() = wallpaperInfo?.shouldUseDefaultDeviceStateChangeTransition() ?: true fun setNotificationShadeZoom(zoomOut: Float) { @@ -50,7 +50,7 @@ class WallpaperController @Inject constructor(private val wallpaperManager: Wall } fun setUnfoldTransitionZoom(zoomOut: Float) { - if (shouldUseDefaultUnfoldTransition) { + if (shouldUseDefaultDeviceStateChangeTransition) { unfoldTransitionZoomOut = zoomOut updateZoom() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index cc35a8f9e1b5..d819fa2adc38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -46,6 +46,7 @@ import org.mockito.junit.MockitoJUnit @RunWithLooper class ActivityLaunchAnimatorTest : SysuiTestCase() { private val launchContainer = LinearLayout(mContext) + private val launchAnimator = LaunchAnimator(mContext, isForTesting = true) @Mock lateinit var callback: ActivityLaunchAnimator.Callback @Spy private val controller = TestLaunchAnimatorController(launchContainer) @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback @@ -56,7 +57,8 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Before fun setup() { - activityLaunchAnimator = ActivityLaunchAnimator(callback, mContext) + activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator) + activityLaunchAnimator.callback = callback } private fun startIntentWithAnimation( @@ -120,7 +122,8 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Test fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() { `when`(callback.isOnKeyguard()).thenReturn(true) - val animator = ActivityLaunchAnimator(callback, context) + val animator = ActivityLaunchAnimator(launchAnimator) + animator.callback = callback val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java) var animationAdapter: RemoteAnimationAdapter? = null @@ -208,7 +211,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { private class TestLaunchAnimatorController( override var launchContainer: ViewGroup ) : ActivityLaunchAnimator.Controller { - override fun createAnimatorState() = ActivityLaunchAnimator.State( + override fun createAnimatorState() = LaunchAnimator.State( top = 100, bottom = 200, left = 300, @@ -232,7 +235,7 @@ private class TestLaunchAnimatorController( } override fun onLaunchAnimationProgress( - state: ActivityLaunchAnimator.State, + state: LaunchAnimator.State, progress: Float, linearProgress: Float ) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt new file mode 100644 index 000000000000..5bcf828afe9f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt @@ -0,0 +1,186 @@ +package com.android.systemui.animation + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.testing.ViewUtils +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.LinearLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class DialogLaunchAnimatorTest : SysuiTestCase() { + private val launchAnimator = LaunchAnimator(context, isForTesting = true) + private val hostDialogprovider = TestHostDialogProvider() + private val dialogLaunchAnimator = + DialogLaunchAnimator(context, launchAnimator, hostDialogprovider) + + @Test + fun testShowDialogFromView() { + // Show the dialog. showFromView() must be called on the main thread with a dialog created + // on the main thread too. + val (dialog, hostDialog) = runOnMainThreadAndWaitForIdleSync { + val touchSurfaceRoot = LinearLayout(context) + val touchSurface = View(context) + touchSurfaceRoot.addView(touchSurface) + + // We need to attach the root to the window manager otherwise the exit animation will + // be skipped + ViewUtils.attachView(touchSurfaceRoot) + + val dialog = TestDialog(context) + val hostDialog = + dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog + dialog to hostDialog + } + + // Only the host dialog is actually showing. + assertTrue(hostDialog.isShowing) + assertFalse(dialog.isShowing) + + // The dialog onStart() method was called but not onStop(). + assertTrue(dialog.onStartCalled) + assertFalse(dialog.onStopCalled) + + // The dialog content has been stolen and is shown inside the host dialog. + val hostDialogContent = hostDialog.findViewById<ViewGroup>(android.R.id.content) + assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount) + assertEquals(1, hostDialogContent.childCount) + + val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup + assertEquals(1, hostDialogRoot.childCount) + assertEquals(dialog.contentView, hostDialogRoot.getChildAt(0)) + + // If we are dozing, the host dialog window also fades out. + runOnMainThreadAndWaitForIdleSync { dialogLaunchAnimator.onDozeAmountChanged(0.5f) } + assertTrue(hostDialog.window!!.decorView.alpha < 1f) + + // Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that + // it's a ListenableDialog. + runOnMainThreadAndWaitForIdleSync { dialog.hide() } + assertFalse(hostDialog.isShowing) + assertFalse(dialog.isShowing) + + runOnMainThreadAndWaitForIdleSync { dialog.show() } + assertTrue(hostDialog.isShowing) + assertFalse(dialog.isShowing) + + assertFalse(dialog.onStopCalled) + runOnMainThreadAndWaitForIdleSync { dialog.dismiss() } + assertFalse(hostDialog.isShowing) + assertFalse(dialog.isShowing) + assertTrue(hostDialog.wasDismissed) + assertTrue(dialog.onStopCalled) + } + + private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T { + lateinit var result: T + context.mainExecutor.execute { + result = f() + } + waitForIdleSync() + return result + } + + private class TestHostDialogProvider : HostDialogProvider { + override fun createHostDialog( + context: Context, + theme: Int, + onCreateCallback: () -> Unit, + dismissOverride: (() -> Unit) -> Unit + ): Dialog = TestHostDialog(context, onCreateCallback, dismissOverride) + } + + private class TestHostDialog( + context: Context, + private val onCreateCallback: () -> Unit, + private val dismissOverride: (() -> Unit) -> Unit + ) : Dialog(context) { + var wasDismissed = false + + init { + // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw. + window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + onCreateCallback() + } + + override fun dismiss() { + dismissOverride { + super.dismiss() + wasDismissed = true + } + } + } + + private class TestDialog(context: Context) : Dialog(context), ListenableDialog { + private val listeners = hashSetOf<DialogListener>() + val contentView = View(context) + var onStartCalled = false + var onStopCalled = false + + init { + // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw. + window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(contentView) + } + + override fun onStart() { + super.onStart() + onStartCalled = true + } + + override fun onStop() { + super.onStart() + onStopCalled = true + } + + override fun addListener(listener: DialogListener) { + listeners.add(listener) + } + + override fun removeListener(listener: DialogListener) { + listeners.remove(listener) + } + + override fun dismiss() { + super.dismiss() + notifyListeners { onDismiss() } + } + + override fun hide() { + super.hide() + notifyListeners { onHide() } + } + + override fun show() { + super.show() + notifyListeners { onShow() } + } + + private fun notifyListeners(notify: DialogListener.() -> Unit) { + for (listener in HashSet(listeners)) { + listener.notify() + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt index 8cba25dc1b92..58e0cb259bb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt @@ -32,7 +32,7 @@ class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() { fun animatingOrphanViewDoesNotCrash() { val ghostedView = LinearLayout(mContext) val controller = GhostedViewLaunchAnimatorController(ghostedView) - val state = ActivityLaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0) + val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0) controller.onIntentStarted(willAnimate = true) controller.onLaunchAnimationStart(isExpandingFullyAbove = true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 2c686618a361..25ca8c95500b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -54,6 +54,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { // Mock private MediaOutputController mMediaOutputController = mock(MediaOutputController.class); + private MediaOutputDialog mMediaOutputDialog = mock(MediaOutputDialog.class); private MediaDevice mMediaDevice1 = mock(MediaDevice.class); private MediaDevice mMediaDevice2 = mock(MediaDevice.class); private Icon mIcon = mock(Icon.class); @@ -65,7 +66,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Before public void setUp() { - mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); + mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController, mMediaOutputDialog); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 9bd07b88417d..053851ec385d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -39,6 +39,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -63,6 +64,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { private NotificationEntryManager mNotificationEntryManager = mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); + private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl; private MediaOutputController mMediaOutputController; @@ -75,7 +77,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mMediaOutputController); mMediaOutputBaseDialogImpl.onCreate(new Bundle()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index d1a617bcc0cb..f7e60caa2624 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -49,6 +49,7 @@ import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -91,6 +92,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { private NotificationEntryManager mNotificationEntryManager = mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); + private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); private Context mSpyContext; private MediaOutputController mMediaOutputController; @@ -113,7 +115,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; MediaDescription.Builder builder = new MediaDescription.Builder(); @@ -157,7 +159,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void start_withoutPackageName_verifyMediaControllerInit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mMediaOutputController.start(mCb); @@ -178,7 +180,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void stop_withoutPackageName_verifyMediaControllerDeinit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mMediaOutputController.start(mCb); @@ -449,7 +451,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { public void getNotificationLargeIcon_withoutPackageName_returnsNull() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); assertThat(mMediaOutputController.getNotificationIcon()).isNull(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index 86f6bdec43f0..8a3ea562269d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -36,6 +36,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -65,6 +66,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { private final NotificationEntryManager mNotificationEntryManager = mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); + private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); private MediaOutputDialog mMediaOutputDialog; private MediaOutputController mMediaOutputController; @@ -74,10 +76,11 @@ public class MediaOutputDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController, mUiEventLogger); + mMediaOutputDialog.show(); when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice); when(mMediaDevice.getFeatures()).thenReturn(mFeatures); @@ -123,6 +126,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { public void onCreate_ShouldLogVisibility() { MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mMediaOutputController, mUiEventLogger); + testDialog.show(); testDialog.dismissDialog(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java index c296ff5cf19a..e8cd6c88956d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java @@ -34,6 +34,7 @@ import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -64,6 +65,7 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { private NotificationEntryManager mNotificationEntryManager = mock(NotificationEntryManager.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); + private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); private MediaOutputGroupDialog mMediaOutputGroupDialog; private MediaOutputController mMediaOutputController; @@ -73,10 +75,11 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, - mNotificationEntryManager, mUiEventLogger); + mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, mMediaOutputController); + mMediaOutputGroupDialog.show(); when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 09a3d35d2d32..f5cab1df9fa2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -23,6 +23,7 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; @@ -725,6 +726,20 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg)); } + @Test + public void testEmptyOwnerInfoHidesIndicationArea() { + createController(); + + // GIVEN the owner info is set to an empty string + when(mLockPatternUtils.getDeviceOwnerInfo()).thenReturn(""); + + // WHEN asked to update the indication area + mController.setVisible(true); + + // THEN the owner info should be hidden + verifyHideIndication(INDICATION_TYPE_OWNER_INFO); + } + private void sendUpdateDisclosureBroadcast() { mBroadcastReceiver.onReceive(mContext, new Intent()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index c8fec0212137..fd932803ff37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -445,6 +445,20 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(smartspaceView2).registerDataProvider(plugin) } + @Test + fun testConnectAttemptBeforeInitializationShouldNotCreateSession() { + // GIVEN an uninitalized smartspaceView + // WHEN the device is provisioned + `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true) + `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true) + deviceProvisionedListener.onDeviceProvisionedChanged() + + // THEN no calls to createSmartspaceSession should occur + verify(smartspaceManager, never()).createSmartspaceSession(any()) + // THEN no listeners should be registered + verify(configurationController, never()).addCallback(any()) + } + private fun connectSession() { val view = controller.buildAndConnectView(fakeParent) smartspaceView = view as SmartspaceView diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 5115614f8bfb..6e3d5ce7953e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -308,18 +308,4 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mBouncer).updateKeyguardPosition(1.0f); } - - @Test - public void testNavBarHiddenWhenSleepAnimationStarts() { - mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); - assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible()); - - // Verify that the nav bar is hidden when the screen off animation starts - doReturn(true).when(mUnlockedScreenOffAnimationController).isScreenOffAnimationPlaying(); - mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); - assertFalse(mStatusBarKeyguardViewManager.isNavBarVisible()); - - mWakefulnessLifecycle.dispatchFinishedWakingUp(); - assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible()); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 88a3827d0582..fada64e02ac8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -77,6 +77,8 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; @@ -274,6 +276,8 @@ public class StatusBarTest extends SysuiTestCase { @Mock private StartingSurface mStartingSurface; @Mock private OperatorNameViewController mOperatorNameViewController; @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory; + @Mock private ActivityLaunchAnimator mActivityLaunchAnimator; + @Mock private DialogLaunchAnimator mDialogLaunchAnimator; private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -452,7 +456,9 @@ public class StatusBarTest extends SysuiTestCase { mUnlockedScreenOffAnimationController, Optional.of(mStartingSurface), mTunerService, - mock(DumpManager.class)); + mock(DumpManager.class), + mActivityLaunchAnimator, + mDialogLaunchAnimator); when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class), any(NotificationPanelViewController.class), any(BiometricUnlockController.class), any(ViewGroup.class), any(KeyguardBypassController.class))) diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt index 3cb19e3460dd..de86821c0535 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt @@ -91,7 +91,7 @@ class WallpaperControllerTest : SysuiTestCase() { @Test fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() { wallaperController.onWallpaperInfoUpdated(createWallpaperInfo( - useDefaultUnfoldTransition = false + useDefaultTransition = false )) wallaperController.setUnfoldTransitionZoom(0.5f) @@ -136,9 +136,10 @@ class WallpaperControllerTest : SysuiTestCase() { verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat()) } - private fun createWallpaperInfo(useDefaultUnfoldTransition: Boolean = true): WallpaperInfo { + private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo { val info = mock(WallpaperInfo::class.java) - whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultUnfoldTransition) + whenever(info.shouldUseDefaultDeviceStateChangeTransition()) + .thenReturn(useDefaultTransition) return info } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2b1c69546248..18ed9586d263 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1550,6 +1550,13 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int INDEX_TOTAL_MEMTRACK_GL = 14; private static final int INDEX_LAST = 15; + /** + * Used to notify activity lifecycle events. + */ + @Nullable + volatile ActivityManagerInternal.VoiceInteractionManagerProvider + mVoiceInteractionManagerProvider; + final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); @@ -1886,6 +1893,14 @@ public class ActivityManagerService extends IActivityManager.Stub return mAppOpsService; } + /** + * Sets the internal voice interaction manager service. + */ + private void setVoiceInteractionManagerProvider( + @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) { + mVoiceInteractionManagerProvider = provider; + } + static class MemBinder extends Binder { ActivityManagerService mActivityManagerService; private final PriorityDump.PriorityDumper mPriorityDumper = @@ -2737,6 +2752,11 @@ public class ActivityManagerService extends IActivityManager.Stub || event == Event.ACTIVITY_DESTROYED)) { contentCaptureService.notifyActivityEvent(userId, activity, event); } + // TODO(b/201234353): Move the logic to client side. + if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED + || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) { + mVoiceInteractionManagerProvider.notifyActivityEventChanged(); + } } /** @@ -16366,6 +16386,12 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code, intent, resolvedType, finishedReceiver, requiredPermission, options); } + + @Override + public void setVoiceInteractionManagerProvider( + @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) { + ActivityManagerService.this.setVoiceInteractionManagerProvider(provider); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 8c192844c891..e09ba346f8d8 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -2635,18 +2635,19 @@ public class AudioService extends IAudioService.Stub case KeyEvent.KEYCODE_VOLUME_UP: adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE, AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller, - Binder.getCallingUid(), true, keyEventMode); + Binder.getCallingUid(), Binder.getCallingPid(), true, keyEventMode); break; case KeyEvent.KEYCODE_VOLUME_DOWN: adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER, AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller, - Binder.getCallingUid(), true, keyEventMode); + Binder.getCallingUid(), Binder.getCallingPid(), true, keyEventMode); break; case KeyEvent.KEYCODE_VOLUME_MUTE: if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE, AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller, - Binder.getCallingUid(), true, VOL_ADJUST_NORMAL); + Binder.getCallingUid(), Binder.getCallingPid(), + true, VOL_ADJUST_NORMAL); } break; default: @@ -2659,8 +2660,8 @@ public class AudioService extends IAudioService.Stub public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller) { adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage, - caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(), - VOL_ADJUST_NORMAL); + caller, Binder.getCallingUid(), Binder.getCallingPid(), + callingHasAudioSettingsPermission(), VOL_ADJUST_NORMAL); } public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) { @@ -2686,7 +2687,7 @@ public class AudioService extends IAudioService.Stub } private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, - String callingPackage, String caller, int uid, boolean hasModifyAudioSettings, + String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings, int keyEventMode) { if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType + ", flags=" + flags + ", caller=" + caller @@ -2759,7 +2760,7 @@ public class AudioService extends IAudioService.Stub if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment"); } - adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid, + adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid, pid, hasModifyAudioSettings, keyEventMode); } @@ -2791,12 +2792,12 @@ public class AudioService extends IAudioService.Stub sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType, direction/*val1*/, flags/*val2*/, callingPackage)); adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage, - Binder.getCallingUid(), callingHasAudioSettingsPermission(), - VOL_ADJUST_NORMAL); + Binder.getCallingUid(), Binder.getCallingPid(), + callingHasAudioSettingsPermission(), VOL_ADJUST_NORMAL); } protected void adjustStreamVolume(int streamType, int direction, int flags, - String callingPackage, String caller, int uid, boolean hasModifyAudioSettings, + String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings, int keyEventMode) { if (mUseFixedVolume) { return; @@ -2818,8 +2819,7 @@ public class AudioService extends IAudioService.Stub if (isMuteAdjust && (streamType == AudioSystem.STREAM_VOICE_CALL || streamType == AudioSystem.STREAM_BLUETOOTH_SCO) && - mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MODIFY_PHONE_STATE) + mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); @@ -2829,8 +2829,8 @@ public class AudioService extends IAudioService.Stub // If the stream is STREAM_ASSISTANT, // make sure that the calling app have the MODIFY_AUDIO_ROUTING permission. if (streamType == AudioSystem.STREAM_ASSISTANT && - mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MODIFY_AUDIO_ROUTING) + mContext.checkPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); @@ -4014,7 +4014,7 @@ public class AudioService extends IAudioService.Stub } private void setMasterMuteInternal(boolean mute, int flags, String callingPackage, int uid, - int userId) { + int userId, int pid) { // If we are being called by the system check for user we are going to change // so we handle user restrictions correctly. if (uid == android.os.Process.SYSTEM_UID) { @@ -4025,8 +4025,8 @@ public class AudioService extends IAudioService.Stub return; } if (userId != UserHandle.getCallingUserId() && - mContext.checkCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + pid, uid) != PackageManager.PERMISSION_GRANTED) { return; } @@ -4066,7 +4066,7 @@ public class AudioService extends IAudioService.Stub public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) { enforceModifyAudioRoutingPermission(); setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(), - userId); + userId, Binder.getCallingPid()); } /** @see AudioManager#getStreamVolume(int) */ @@ -4967,8 +4967,8 @@ public class AudioService extends IAudioService.Stub // direction and stream type swap here because the public // adjustSuggested has a different order than the other methods. - adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid, - hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL); + adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, + uid, pid, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL); } /** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */ @@ -4987,7 +4987,7 @@ public class AudioService extends IAudioService.Stub .toString())); } - adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid, + adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid, pid, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL); } diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java index a81213df6fe3..11dc1dbd25cc 100644 --- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java +++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java @@ -175,29 +175,25 @@ final class AppCompatOverridesParser { /** * Parses the given {@code configStr}, that is expected to be a comma separated list of changes - * overrides, and returns a {@link PackageOverrides}. + * overrides, and returns a map from change ID to {@link PackageOverride} instances to add. * * <p>Each change override is in the following format: - * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty, - * this indicates that any override for the specified change ID should be removed. + * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled>'. * * <p>If there are multiple overrides that should be added with the same change ID, the one * that best fits the given {@code versionCode} is added. * * <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored. * - * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the - * same change ID is both added and removed, i.e., has a change override entry with an empty - * enabled and another with a non-empty enabled, the change ID will only be removed. + * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. */ - static PackageOverrides parsePackageOverrides( - String configStr, long versionCode, Set<Long> changeIdsToSkip) { + static Map<Long, PackageOverride> parsePackageOverrides(String configStr, long versionCode, + Set<Long> changeIdsToSkip) { if (configStr.isEmpty()) { - return new PackageOverrides(); + return emptyMap(); } PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode); Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>(); - Set<Long> overridesToRemove = new ArraySet<>(); for (String overrideEntryString : configStr.split(",")) { List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4)); if (changeIdAndVersions.size() != 4) { @@ -220,16 +216,6 @@ final class AppCompatOverridesParser { String maxVersionCodeStr = changeIdAndVersions.get(2); String enabledStr = changeIdAndVersions.get(3); - if (enabledStr.isEmpty()) { - if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) { - Slog.w( - TAG, - "min/max version code should be empty if enabled is empty: " - + overrideEntryString); - } - overridesToRemove.add(changeId); - continue; - } if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) { Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString); continue; @@ -262,39 +248,7 @@ final class AppCompatOverridesParser { } } - for (Long changeId : overridesToRemove) { - if (overridesToAdd.containsKey(changeId)) { - Slog.w( - TAG, - "Change ID [" - + changeId - + "] is both added and removed in package override flag: " - + configStr); - overridesToAdd.remove(changeId); - } - } - - return new PackageOverrides(overridesToAdd, overridesToRemove); - } - - /** - * A container for a map from change ID to {@link PackageOverride} to add and a set of change - * IDs to remove overrides for. - * - * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive. - */ - static final class PackageOverrides { - public final Map<Long, PackageOverride> overridesToAdd; - public final Set<Long> overridesToRemove; - - PackageOverrides() { - this(emptyMap(), emptySet()); - } - - PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) { - this.overridesToAdd = overridesToAdd; - this.overridesToRemove = overridesToRemove; - } + return overridesToAdd; } /** diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java index 63ae1af42f08..6aed4b023297 100644 --- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java +++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java @@ -49,7 +49,6 @@ import com.android.internal.compat.CompatibilityOverrideConfig; import com.android.internal.compat.CompatibilityOverridesToRemoveConfig; import com.android.internal.compat.IPlatformCompat; import com.android.server.SystemService; -import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides; import java.util.ArrayList; import java.util.Arrays; @@ -128,20 +127,25 @@ public final class AppCompatOverridesService { } /** - * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code - * namespace} are fetched via {@link DeviceConfig#getProperties}. + * Same as {@link #applyOverrides(Properties, Set, Map)} except all properties of the given + * {@code namespace} are fetched via {@link DeviceConfig#getProperties}. */ - private void applyAllOverrides(String namespace, + private void applyAllOverrides(String namespace, Set<Long> ownedChangeIds, Map<String, Set<Long>> packageToChangeIdsToSkip) { - applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip); + applyOverrides(DeviceConfig.getProperties(namespace), ownedChangeIds, + packageToChangeIdsToSkip); } /** * Iterates all package override flags in the given {@code properties}, and for each flag whose - * package is installed on the device, parses its value and applies the overrides in it with + * package is installed on the device, parses its value and adds the overrides in it with * respect to the package's current installed version. + * + * <p>In addition, for each package, removes any override that wasn't just added, whose change + * ID is in {@code ownedChangeIds} but not in the respective set in {@code + * packageToChangeIdsToSkip}. */ - private void applyOverrides(Properties properties, + private void applyOverrides(Properties properties, Set<Long> ownedChangeIds, Map<String, Set<Long>> packageToChangeIdsToSkip) { Set<String> packageNames = new ArraySet<>(properties.getKeyset()); packageNames.remove(FLAG_OWNED_CHANGE_IDS); @@ -154,15 +158,16 @@ public final class AppCompatOverridesService { } applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""), - packageName, versionCode, - packageToChangeIdsToSkip.getOrDefault(packageName, emptySet())); + packageName, versionCode, ownedChangeIds, + packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()), + /* removeOtherOwnedOverrides= */ true); } } /** - * Applies all overrides in all supported namespaces for the given {@code packageName}. + * Adds all overrides in all supported namespaces for the given {@code packageName}. */ - private void applyAllPackageOverrides(String packageName) { + private void addAllPackageOverrides(String packageName) { Long versionCode = getVersionCodeOrNull(packageName); if (versionCode == null) { return; @@ -171,26 +176,40 @@ public final class AppCompatOverridesService { for (String namespace : mSupportedNamespaces) { // We apply overrides for each namespace separately so that if there is a failure for // one namespace, the other namespaces won't be affected. + Set<Long> ownedChangeIds = getOwnedChangeIds(namespace); applyPackageOverrides( DeviceConfig.getString(namespace, packageName, /* defaultValue= */ ""), - packageName, versionCode, - getOverridesToRemove(namespace).getOrDefault(packageName, emptySet())); + packageName, versionCode, ownedChangeIds, + getOverridesToRemove(namespace, ownedChangeIds).getOrDefault(packageName, + emptySet()), /* removeOtherOwnedOverrides */ false); } } /** - * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the - * resulting {@link PackageOverrides#overridesToAdd} via {@link - * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link - * PackageOverrides#overridesToRemove} via {@link - * IPlatformCompat#removeOverridesOnReleaseBuilds}. + * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments and adds + * the resulting overrides via {@link IPlatformCompat#putOverridesOnReleaseBuilds}. + * + * <p>In addition, if {@code removeOtherOwnedOverrides} is true, removes any override that + * wasn't just added, whose change ID is in {@code ownedChangeIds} but not in {@code + * changeIdsToSkip}, via {@link IPlatformCompat#removeOverridesOnReleaseBuilds}. */ - private void applyPackageOverrides(String configStr, String packageName, - long versionCode, Set<Long> changeIdsToSkip) { - PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides( + private void applyPackageOverrides(String configStr, String packageName, long versionCode, + Set<Long> ownedChangeIds, Set<Long> changeIdsToSkip, + boolean removeOtherOwnedOverrides) { + Map<Long, PackageOverride> overridesToAdd = AppCompatOverridesParser.parsePackageOverrides( configStr, versionCode, changeIdsToSkip); - putPackageOverrides(packageName, packageOverrides.overridesToAdd); - removePackageOverrides(packageName, packageOverrides.overridesToRemove); + putPackageOverrides(packageName, overridesToAdd); + + if (!removeOtherOwnedOverrides) { + return; + } + Set<Long> overridesToRemove = new ArraySet<>(); + for (Long changeId : ownedChangeIds) { + if (!overridesToAdd.containsKey(changeId) && !changeIdsToSkip.contains(changeId)) { + overridesToRemove.add(changeId); + } + } + removePackageOverrides(packageName, overridesToRemove); } /** @@ -227,10 +246,11 @@ public final class AppCompatOverridesService { * {@code namespace} and parses it into a map from package name to a set of change IDs to * remove for that package. */ - private Map<String, Set<Long>> getOverridesToRemove(String namespace) { + private Map<String, Set<Long>> getOverridesToRemove(String namespace, + Set<Long> ownedChangeIds) { return mOverridesParser.parseRemoveOverrides( DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""), - getOwnedChangeIds(namespace)); + ownedChangeIds); } /** @@ -333,7 +353,9 @@ public final class AppCompatOverridesService { boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains( FLAG_OWNED_CHANGE_IDS); - Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace); + Set<Long> ownedChangeIds = getOwnedChangeIds(mNamespace); + Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace, + ownedChangeIds); if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) { // In both cases it's possible that overrides that weren't removed before should // now be removed. @@ -343,9 +365,9 @@ public final class AppCompatOverridesService { if (removeOverridesFlagChanged) { // We need to re-apply all overrides in the namespace since the remove overrides // flag might have blocked some of them from being applied before. - applyAllOverrides(mNamespace, overridesToRemove); + applyAllOverrides(mNamespace, ownedChangeIds, overridesToRemove); } else { - applyOverrides(properties, overridesToRemove); + applyOverrides(properties, ownedChangeIds, overridesToRemove); } } } @@ -392,7 +414,7 @@ public final class AppCompatOverridesService { switch (action) { case ACTION_PACKAGE_ADDED: case ACTION_PACKAGE_CHANGED: - applyAllPackageOverrides(packageName); + addAllPackageOverrides(packageName); break; case ACTION_PACKAGE_REMOVED: if (!isInstalledForAnyUser(packageName)) { diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 5797b061f2d0..5fc301e60b9d 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -53,6 +53,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayInfo; @@ -77,7 +78,6 @@ import java.util.List; import java.util.Locale; import java.util.Objects; - /** * The DisplayModeDirector is responsible for determining what modes are allowed to be automatically * picked by the system based on system-wide and display-specific configuration. @@ -92,6 +92,8 @@ public class DisplayModeDirector { private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4; private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5; private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6; + private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7; + private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8; // Special ID used to indicate that given vote is to be applied globally, rather than to a // specific display. @@ -161,9 +163,10 @@ public class DisplayModeDirector { } }; mSensorObserver = new SensorObserver(context, ballotBox, injector); - mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler()); mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); + mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(), + mDeviceConfigDisplaySettings); mDeviceConfig = injector.getDeviceConfig(); mAlwaysRespectAppRequest = false; } @@ -724,6 +727,11 @@ public class DisplayModeDirector { } @VisibleForTesting + HbmObserver getHbmObserver() { + return mHbmObserver; + } + + @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { @@ -792,6 +800,19 @@ public class DisplayModeDirector { (DesiredDisplayModeSpecsListener) msg.obj; desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged(); break; + + case MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED: { + int refreshRateInHbmSunlight = msg.arg1; + mHbmObserver.onDeviceConfigRefreshRateInHbmSunlightChanged( + refreshRateInHbmSunlight); + break; + } + + case MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED: { + int refreshRateInHbmHdr = msg.arg1; + mHbmObserver.onDeviceConfigRefreshRateInHbmHdrChanged(refreshRateInHbmHdr); + break; + } } } } @@ -918,16 +939,19 @@ public class DisplayModeDirector { // result is a range. public static final int PRIORITY_FLICKER_REFRESH_RATE = 1; + // High-brightness-mode may need a specific range of refresh-rates to function properly. + public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2; + // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] - public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; + public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 3; // APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh // rate in certain cases, mostly to preserve power. // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate]. - public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3; + public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 4; // We split the app request into different priorities in case we can satisfy one desire // without the other. @@ -942,27 +966,24 @@ public class DisplayModeDirector { // The preferred refresh rate is set on the main surface of the app outside of // DisplayModeDirector. // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded - public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4; - public static final int PRIORITY_APP_REQUEST_SIZE = 5; + public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5; + public static final int PRIORITY_APP_REQUEST_SIZE = 6; // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest // of low priority voters. It votes [0, max(PEAK, MIN)] - public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6; + public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7; // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. - public static final int PRIORITY_LOW_POWER_MODE = 7; + public static final int PRIORITY_LOW_POWER_MODE = 8; // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the // higher priority voters' result is a range, it will fix the rate to a single choice. // It's used to avoid refresh rate switches in certain conditions which may result in the // user seeing the display flickering when the switches occur. - public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8; + public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9; // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL. - public static final int PRIORITY_SKIN_TEMPERATURE = 9; - - // High-brightness-mode may need a specific range of refresh-rates to function properly. - public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10; + public static final int PRIORITY_SKIN_TEMPERATURE = 10; // The proximity sensor needs the refresh rate to be locked in order to function, so this is // set to a high priority. @@ -2258,33 +2279,78 @@ public class DisplayModeDirector { * HBM that are associated with that display. Restrictions are retrieved from * DisplayManagerInternal but originate in the display-device-config file. */ - private static class HbmObserver implements DisplayManager.DisplayListener { + public static class HbmObserver implements DisplayManager.DisplayListener { private final BallotBox mBallotBox; private final Handler mHandler; - private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray(); + private final SparseIntArray mHbmMode = new SparseIntArray(); private final Injector mInjector; + private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; + private int mRefreshRateInHbmSunlight; + private int mRefreshRateInHbmHdr; private DisplayManagerInternal mDisplayManagerInternal; - HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) { + HbmObserver(Injector injector, BallotBox ballotBox, Handler handler, + DeviceConfigDisplaySettings displaySettings) { mInjector = injector; mBallotBox = ballotBox; mHandler = handler; + mDeviceConfigDisplaySettings = displaySettings; } public void observe() { + mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight(); + mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr(); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mInjector.registerDisplayListener(this, mHandler, DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); } + /** + * @return the refresh to lock to when the device is in high brightness mode for Sunlight. + */ + @VisibleForTesting + int getRefreshRateInHbmSunlight() { + return mRefreshRateInHbmSunlight; + } + + /** + * @return the refresh to lock to when the device is in high brightness mode for HDR. + */ + @VisibleForTesting + int getRefreshRateInHbmHdr() { + return mRefreshRateInHbmHdr; + } + + /** + * Recalculates the HBM vote when the device config has been changed. + */ + public void onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate) { + if (refreshRate != mRefreshRateInHbmSunlight) { + mRefreshRateInHbmSunlight = refreshRate; + onDeviceConfigRefreshRateInHbmChanged(); + } + } + + /** + * Recalculates the HBM vote when the device config has been changed. + */ + public void onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate) { + if (refreshRate != mRefreshRateInHbmHdr) { + mRefreshRateInHbmHdr = refreshRate; + onDeviceConfigRefreshRateInHbmChanged(); + } + } + @Override public void onDisplayAdded(int displayId) {} @Override public void onDisplayRemoved(int displayId) { mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null); + mHbmMode.delete(displayId); } @Override @@ -2294,31 +2360,56 @@ public class DisplayModeDirector { // Display no longer there. Assume we'll get an onDisplayRemoved very soon. return; } - final boolean isHbmEnabled = - info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; - if (isHbmEnabled == mHbmEnabled.get(displayId)) { + final int hbmMode = info.highBrightnessMode; + if (hbmMode == mHbmMode.get(displayId)) { // no change, ignore. return; } + mHbmMode.put(displayId, hbmMode); + recalculateVotesForDisplay(displayId); + } + + private void onDeviceConfigRefreshRateInHbmChanged() { + final int[] displayIds = mHbmMode.copyKeys(); + if (displayIds != null) { + for (int id : displayIds) { + recalculateVotesForDisplay(id); + } + } + } + + private void recalculateVotesForDisplay(int displayId) { + final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); Vote vote = null; - mHbmEnabled.put(displayId, isHbmEnabled); - if (isHbmEnabled) { - final List<RefreshRateLimitation> limits = + if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { + // Device resource properties take priority over DisplayDeviceConfig + if (mRefreshRateInHbmSunlight > 0) { + vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight, + mRefreshRateInHbmSunlight); + } else { + final List<RefreshRateLimitation> limits = mDisplayManagerInternal.getRefreshRateLimitations(displayId); - for (int i = 0; limits != null && i < limits.size(); i++) { - final RefreshRateLimitation limitation = limits.get(i); - if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { - vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max); - break; + for (int i = 0; limits != null && i < limits.size(); i++) { + final RefreshRateLimitation limitation = limits.get(i); + if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { + vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max); + break; + } } } } + if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + && mRefreshRateInHbmHdr > 0) { + vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr); + } mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote); } void dumpLocked(PrintWriter pw) { pw.println(" HbmObserver"); - pw.println(" mHbmEnabled: " + mHbmEnabled); + pw.println(" mHbmMode: " + mHbmMode); + pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight); + pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr); } } @@ -2437,6 +2528,29 @@ public class DisplayModeDirector { return refreshRate; } + public int getRefreshRateInHbmSunlight() { + final int defaultRefreshRateInHbmSunlight = + mContext.getResources().getInteger( + R.integer.config_defaultRefreshRateInHbmSunlight); + + final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, + defaultRefreshRateInHbmSunlight); + + return refreshRate; + } + + public int getRefreshRateInHbmHdr() { + final int defaultRefreshRateInHbmHdr = + mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr); + + final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR, + defaultRefreshRateInHbmHdr); + + return refreshRate; + } + /* * Return null if no such property */ @@ -2476,6 +2590,15 @@ public class DisplayModeDirector { .sendToTarget(); mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0) .sendToTarget(); + + final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED, + refreshRateInHbmSunlight, 0) + .sendToTarget(); + + final int refreshRateInHbmHdr = getRefreshRateInHbmHdr(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0) + .sendToTarget(); } private int[] getIntArrayProperty(String prop) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 45f5daac8dd9..dd2583a0ce1a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -508,7 +508,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mKeyguardOccludedChanged; private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer; - volatile boolean mKeyguardOccluded; Intent mHomeIntent; Intent mCarDockIntent; Intent mDeskDockIntent; @@ -2399,7 +2398,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // the keyguard is being hidden. This is okay because starting windows never show // secret information. // TODO(b/113840485): Occluded may not only happen on default display - if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) { + if (displayId == DEFAULT_DISPLAY && isKeyguardOccluded()) { windowFlags |= FLAG_SHOW_WHEN_LOCKED; } } @@ -3215,7 +3214,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } - if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) { + if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) { // when in keyguard restricted mode, must first verify unlock // before launching home mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() { @@ -3266,42 +3265,38 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void setKeyguardCandidateLw(WindowState win) { mKeyguardCandidate = win; - setKeyguardOccludedLw(mKeyguardOccluded, true /* force */); + setKeyguardOccludedLw(isKeyguardOccluded(), true /* force */); } /** * Updates the occluded state of the Keyguard. * + * @param isOccluded Whether the Keyguard is occluded by another window. + * @param force notify the occluded status to KeyguardService and update flags even though + * occlude status doesn't change. * @return Whether the flags have changed and we have to redo the layout. */ private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force) { if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded); - final boolean wasOccluded = mKeyguardOccluded; + if (isKeyguardOccluded() == isOccluded && !force) { + return false; + } + final boolean showing = mKeyguardDelegate.isShowing(); - final boolean changed = wasOccluded != isOccluded || force; - if (!isOccluded && changed && showing) { - mKeyguardOccluded = false; - mKeyguardDelegate.setOccluded(false, true /* animate */); - if (mKeyguardCandidate != null) { - if (!mKeyguardDelegate.hasLockscreenWallpaper()) { - mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER; - } - } - return true; - } else if (isOccluded && changed && showing) { - mKeyguardOccluded = true; - mKeyguardDelegate.setOccluded(true, false /* animate */); - if (mKeyguardCandidate != null) { + final boolean animate = showing && !isOccluded; + mKeyguardDelegate.setOccluded(isOccluded, animate); + + if (!showing) { + return false; + } + if (mKeyguardCandidate != null) { + if (isOccluded) { mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; + } else if (!mKeyguardDelegate.hasLockscreenWallpaper()) { + mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER; } - return true; - } else if (changed) { - mKeyguardOccluded = isOccluded; - mKeyguardDelegate.setOccluded(isOccluded, false /* animate */); - return false; - } else { - return false; } + return true; } /** {@inheritDoc} */ @@ -4596,7 +4591,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean isKeyguardShowingAndNotOccluded() { if (mKeyguardDelegate == null) return false; - return mKeyguardDelegate.isShowing() && !mKeyguardOccluded; + return mKeyguardDelegate.isShowing() && !isKeyguardOccluded(); } @Override @@ -4622,7 +4617,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean isKeyguardOccluded() { if (mKeyguardDelegate == null) return false; - return mKeyguardOccluded; + return mKeyguardDelegate.isOccluded(); } /** {@inheritDoc} */ @@ -5308,7 +5303,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete()); proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mDefaultDisplayPolicy.isWindowManagerDrawComplete()); - proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded); + proto.write(KEYGUARD_OCCLUDED, isKeyguardOccluded()); proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged); proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded); if (mKeyguardDelegate != null) { @@ -5393,7 +5388,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int key = mDisplayHomeButtonHandlers.keyAt(i); pw.println(mDisplayHomeButtonHandlers.get(key)); } - pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded); + pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(isKeyguardOccluded()); pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged); pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded); pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays="); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 0535af57d8ab..d1906785994a 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -67,7 +67,7 @@ public class KeyguardServiceDelegate { boolean showing; boolean showingAndNotOccluded; boolean inputRestricted; - boolean occluded; + volatile boolean occluded; boolean secure; boolean dreaming; boolean systemIsReady; @@ -272,6 +272,10 @@ public class KeyguardServiceDelegate { mKeyguardState.occluded = isOccluded; } + public boolean isOccluded() { + return mKeyguardState.occluded; + } + public void dismiss(IKeyguardDismissCallback callback, CharSequence message) { if (mKeyguardService != null) { mKeyguardService.dismiss(callback, message); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 19a968628248..168686c04d36 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1411,7 +1411,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A this.task = newTask; if (shouldStartChangeTransition(newParent, oldParent)) { - initializeChangeTransition(getBounds()); + // The new parent and old parent may be in different position. Need to offset the + // animation surface to keep it in its original position. + initializeChangeTransition(getBounds(), newParent.getBounds()); } super.onParentChanged(newParent, oldParent); @@ -4944,11 +4946,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // dispatchTaskInfoChangedIfNeeded() right after ActivityRecord#setVisibility() can report // the stale visible state, because the state will be updated after the app transition. // So tries to report the actual visible state again where the state is changed. - if (!mTaskSupervisor.inActivityVisibilityUpdate()) { - final Task task = getOrganizedTask(); - if (task != null) { - task.dispatchTaskInfoChangedIfNeeded(false /* force */); - } + Task task = getOrganizedTask(); + while (task != null) { + task.dispatchTaskInfoChangedIfNeeded(false /* force */); + task = task.getParent().asTask(); } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "commitVisibility: %s: visible=%b mVisibleRequested=%b", this, @@ -9276,7 +9277,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A record.getMode(), record.mAdapter.mCapturedLeash, !fillsParent(), new Rect(), insets, getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds, - record.mAdapter.mRootTaskBounds, task.getWindowConfiguration(), + record.mAdapter.mEndBounds, task.getWindowConfiguration(), false /*isNotInRecents*/, record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null, record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState()); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 0ba77d8552d3..4149980c7d45 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -596,11 +596,18 @@ public abstract class ActivityTaskManagerInternal { public abstract boolean isBaseOfLockedTask(String packageName); /** - * Create an interface to update configuration for an application. + * Creates an interface to update configuration for the calling application. */ public abstract PackageConfigurationUpdater createPackageConfigurationUpdater(); /** + * Creates an interface to update configuration for an arbitrary application specified by it's + * packageName and userId. + */ + public abstract PackageConfigurationUpdater createPackageConfigurationUpdater( + String packageName, int userId); + + /** * An interface to update configuration for an application, and will persist override * configuration for this package. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 681ff9022e4b..f278e3e22982 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1732,10 +1732,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions); final long origId = Binder.clearCallingIdentity(); try { - synchronized (mGlobalLock) { - return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId, - safeOptions); - } + return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId, + safeOptions); } finally { Binder.restoreCallingIdentity(origId); } @@ -6559,6 +6557,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public PackageConfigurationUpdater createPackageConfigurationUpdater( + String packageName , int userId) { + return new PackageConfigurationUpdaterImpl(packageName, userId, + ActivityTaskManagerService.this); + } + + @Override public boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) { return ActivityTaskManagerService.this.hasSystemAlertWindowPermission(callingUid, diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 0497fb5001d4..145bdfda3ce3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2474,102 +2474,126 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } + /** + * Start the given task from the recent tasks. Do not hold WM global lock when calling this + * method to avoid potential deadlock or permission deny by UriGrantsManager when resolving + * activity (see {@link ActivityStarter.Request#resolveActivity} and + * {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}). + * + * @return The result code of starter. + */ int startActivityFromRecents(int callingPid, int callingUid, int taskId, SafeActivityOptions options) { - Task task = null; + final Task task; + final int taskCallingUid; final String callingPackage; final String callingFeatureId; final Intent intent; final int userId; - int activityType = ACTIVITY_TYPE_UNDEFINED; - int windowingMode = WINDOWING_MODE_UNDEFINED; final ActivityOptions activityOptions = options != null ? options.getOptions(this) : null; boolean moveHomeTaskForward = true; - if (activityOptions != null) { - activityType = activityOptions.getLaunchActivityType(); - windowingMode = activityOptions.getLaunchWindowingMode(); - if (activityOptions.freezeRecentTasksReordering() - && mRecentTasks.isCallerRecents(callingUid)) { - mRecentTasks.setFreezeTaskListReordering(); - } - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || activityOptions.getLaunchRootTask() != null) { - // Don't move home activity forward if we are launching into primary split or there - // is a launch root set. - moveHomeTaskForward = false; + synchronized (mService.mGlobalLock) { + int activityType = ACTIVITY_TYPE_UNDEFINED; + if (activityOptions != null) { + activityType = activityOptions.getLaunchActivityType(); + final int windowingMode = activityOptions.getLaunchWindowingMode(); + if (activityOptions.freezeRecentTasksReordering() + && mRecentTasks.isCallerRecents(callingUid)) { + mRecentTasks.setFreezeTaskListReordering(); + } + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || activityOptions.getLaunchRootTask() != null) { + // Don't move home activity forward if we are launching into primary split or + // there is a launch root set. + moveHomeTaskForward = false; + } } - } - if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { - throw new IllegalArgumentException("startActivityFromRecents: Task " - + taskId + " can't be launch in the home/recents root task."); - } - - mService.deferWindowLayout(); - try { - task = mRootWindowContainer.anyTaskForId(taskId, - MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP); - if (task == null) { - mWindowManager.executeAppTransition(); - throw new IllegalArgumentException( - "startActivityFromRecents: Task " + taskId + " not found."); + if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { + throw new IllegalArgumentException("startActivityFromRecents: Task " + + taskId + " can't be launch in the home/recents root task."); } - if (moveHomeTaskForward) { - // We always want to return to the home activity instead of the recents activity - // from whatever is started from the recents activity, so move the home root task - // forward. - // TODO (b/115289124): Multi-display supports for recents. - mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront( - "startActivityFromRecents"); - } + boolean shouldStartActivity = false; + mService.deferWindowLayout(); + try { + task = mRootWindowContainer.anyTaskForId(taskId, + MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP); + if (task == null) { + mWindowManager.executeAppTransition(); + throw new IllegalArgumentException( + "startActivityFromRecents: Task " + taskId + " not found."); + } - // If the user must confirm credentials (e.g. when first launching a work app and the - // Work Challenge is present) let startActivityInPackage handle the intercepting. - if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId) - && task.getRootActivity() != null) { - final ActivityRecord targetActivity = task.getTopNonFinishingActivity(); - - mRootWindowContainer.startPowerModeLaunchIfNeeded( - true /* forceSend */, targetActivity); - final LaunchingState launchingState = - mActivityMetricsLogger.notifyActivityLaunching(task.intent); - try { - mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */, - task.mTaskId, 0, options); - // Apply options to prevent pendingOptions be taken when scheduling activity - // lifecycle transaction to make sure the override pending app transition will - // be applied immediately. - targetActivity.applyOptionsAnimation(); - } finally { - mActivityMetricsLogger.notifyActivityLaunched(launchingState, - START_TASK_TO_FRONT, false /* newActivityCreated */, targetActivity, - activityOptions); + if (moveHomeTaskForward) { + // We always want to return to the home activity instead of the recents + // activity from whatever is started from the recents activity, so move + // the home root task forward. + // TODO (b/115289124): Multi-display supports for recents. + mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront( + "startActivityFromRecents"); } - mService.getActivityStartController().postStartActivityProcessingForLastStarter( - task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT, - task.getRootTask()); + // If the user must confirm credentials (e.g. when first launching a work + // app and the Work Challenge is present) let startActivityInPackage handle + // the intercepting. + if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId) + && task.getRootActivity() != null) { + final ActivityRecord targetActivity = task.getTopNonFinishingActivity(); + + mRootWindowContainer.startPowerModeLaunchIfNeeded( + true /* forceSend */, targetActivity); + final LaunchingState launchingState = + mActivityMetricsLogger.notifyActivityLaunching(task.intent); + try { + mService.moveTaskToFrontLocked(null /* appThread */, + null /* callingPackage */, task.mTaskId, 0, options); + // Apply options to prevent pendingOptions be taken when scheduling + // activity lifecycle transaction to make sure the override pending app + // transition will be applied immediately. + targetActivity.applyOptionsAnimation(); + } finally { + mActivityMetricsLogger.notifyActivityLaunched(launchingState, + START_TASK_TO_FRONT, false /* newActivityCreated */, + targetActivity, activityOptions); + } - // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume - // app switching here also. - mService.resumeAppSwitches(); + mService.getActivityStartController().postStartActivityProcessingForLastStarter( + task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT, + task.getRootTask()); - return ActivityManager.START_TASK_TO_FRONT; + // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume + // app switching here also. + mService.resumeAppSwitches(); + return ActivityManager.START_TASK_TO_FRONT; + } + // The task is empty or needs to show the confirmation for credential. + shouldStartActivity = true; + } finally { + if (!shouldStartActivity) { + mService.continueWindowLayout(); + } } + taskCallingUid = task.mCallingUid; callingPackage = task.mCallingPackage; callingFeatureId = task.mCallingFeatureId; intent = task.intent; intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); userId = task.mUserId; - return mService.getActivityStartController().startActivityInPackage(task.mCallingUid, + } + // ActivityStarter will acquire the lock where the places need, so execute the request + // outside of the lock. + try { + return mService.getActivityStartController().startActivityInPackage(taskCallingUid, callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null, null, 0, 0, options, userId, task, "startActivityFromRecents", false /* validateIncomingUser */, null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */); } finally { - mService.continueWindowLayout(); + synchronized (mService.mGlobalLock) { + mService.continueWindowLayout(); + } } } diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java index 486328a758da..529c4f608743 100644 --- a/services/core/java/com/android/server/wm/AnimationAdapter.java +++ b/services/core/java/com/android/server/wm/AnimationAdapter.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import android.annotation.ColorInt; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; @@ -107,14 +106,4 @@ interface AnimationAdapter { default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { return false; } - - /** - * Gets the background color to show behind an animation. - * - * @return The background color to show behind an animation (0 for no background color). - */ - @ColorInt - default int getBackgroundColor() { - return 0; - } } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 7f0adcacc951..558939611905 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -35,10 +35,10 @@ import android.os.UserHandle; */ class AppTaskImpl extends IAppTask.Stub { private static final String TAG = "AppTaskImpl"; - private ActivityTaskManagerService mService; + private final ActivityTaskManagerService mService; - private int mTaskId; - private int mCallingUid; + private final int mTaskId; + private final int mCallingUid; public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) { mService = service; @@ -113,9 +113,9 @@ class AppTaskImpl extends IAppTask.Stub { return; } } - mService.mTaskSupervisor.startActivityFromRecents(callingPid, - callingUid, mTaskId, null); } + mService.mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId, + null /* options */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java index 8f161bf6ecc4..520bd8b2108e 100644 --- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static com.android.server.wm.AnimationAdapterProto.LOCAL; import static com.android.server.wm.LocalAnimationAdapterProto.ANIMATION_SPEC; -import android.annotation.ColorInt; import android.os.SystemClock; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -73,12 +72,6 @@ class LocalAnimationAdapter implements AnimationAdapter { } @Override - @ColorInt - public int getBackgroundColor() { - return mSpec.getBackgroundColor(); - } - - @Override public void dump(PrintWriter pw, String prefix) { mSpec.dump(pw, prefix); } @@ -156,9 +149,5 @@ class LocalAnimationAdapter implements AnimationAdapter { } void dumpDebugInner(ProtoOutputStream proto); - - default int getBackgroundColor() { - return 0; - } } } diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java index 960250544299..8bbcf1f9c029 100644 --- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java +++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java @@ -16,24 +16,38 @@ package com.android.server.wm; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.LocaleList; +import android.util.ArraySet; import android.util.Slog; +import java.util.Optional; + /** * An implementation of {@link ActivityTaskManagerInternal.PackageConfigurationUpdater}. */ final class PackageConfigurationUpdaterImpl implements ActivityTaskManagerInternal.PackageConfigurationUpdater { private static final String TAG = "PackageConfigurationUpdaterImpl"; - private final int mPid; + private final Optional<Integer> mPid; private Integer mNightMode; private LocaleList mLocales; + private String mPackageName; + private int mUserId; private ActivityTaskManagerService mAtm; PackageConfigurationUpdaterImpl(int pid, ActivityTaskManagerService atm) { - mPid = pid; + mPid = Optional.of(pid); + mAtm = atm; + } + + PackageConfigurationUpdaterImpl(String packageName, int userId, + ActivityTaskManagerService atm) { + mPackageName = packageName; + mUserId = userId; mAtm = atm; + mPid = Optional.empty(); } @Override @@ -59,16 +73,29 @@ final class PackageConfigurationUpdaterImpl implements synchronized (mAtm.mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid); - if (wpc == null) { - Slog.w(TAG, "Override application configuration: cannot find pid " + mPid); - return; + final int uid; + if (mPid.isPresent()) { + WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid.get()); + if (wpc == null) { + Slog.w(TAG, "commit: Override application configuration failed: " + + "cannot find pid " + mPid); + return; + } + uid = wpc.mUid; + mUserId = wpc.mUserId; + mPackageName = wpc.mInfo.packageName; + } else { + uid = mAtm.getPackageManagerInternalLocked().getPackageUid(mPackageName, + /* flags = */ PackageManager.MATCH_ALL, mUserId); + if (uid < 0) { + Slog.w(TAG, "commit: update of application configuration failed: " + + "userId or packageName not valid " + mUserId); + return; + } } - LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists( - mLocales, mAtm.getGlobalConfiguration().getLocales()); - wpc.applyAppSpecificConfig(mNightMode, localesOverride); - wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride); - mAtm.mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this); + updateConfig(uid, mPackageName); + mAtm.mPackageConfigPersister.updateFromImpl(mPackageName, mUserId, this); + } finally { Binder.restoreCallingIdentity(ident); } @@ -76,6 +103,19 @@ final class PackageConfigurationUpdaterImpl implements } } + private void updateConfig(int uid, String packageName) { + final ArraySet<WindowProcessController> processes = mAtm.mProcessMap.getProcesses(uid); + if (processes == null) return; + for (int i = processes.size() - 1; i >= 0; i--) { + final WindowProcessController wpc = processes.valueAt(i); + if (!wpc.mInfo.packageName.equals(packageName)) continue; + LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists( + mLocales, mAtm.getGlobalConfiguration().getLocales()); + wpc.applyAppSpecificConfig(mNightMode, localesOverride); + wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride); + } + } + Integer getNightMode() { return mNightMode; } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index b90f937ca56e..16a45fe7cec7 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -65,7 +65,6 @@ class RemoteAnimationController implements DeathRecipient { new ArrayList<>(); @VisibleForTesting final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>(); - private final Rect mTmpRect = new Rect(); private final Handler mHandler; private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable"); @@ -85,18 +84,18 @@ class RemoteAnimationController implements DeathRecipient { * Creates an animation record for each individual {@link WindowContainer}. * * @param windowContainer The windows to animate. - * @param position The position app bounds, in screen coordinates. + * @param position The position app bounds relative to its parent. * @param localBounds The bounds of the app relative to its parent. - * @param stackBounds The stack bounds of the app relative to position. - * @param startBounds The stack bounds before the transition, in screen coordinates + * @param endBounds The end bounds after the transition, in screen coordinates. + * @param startBounds The start bounds before the transition, in screen coordinates. * @return The record representing animation(s) to run on the app. */ RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer, - Point position, Rect localBounds, Rect stackBounds, Rect startBounds) { + Point position, Rect localBounds, Rect endBounds, Rect startBounds) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s", windowContainer); final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position, - localBounds, stackBounds, startBounds); + localBounds, endBounds, startBounds); mPendingAnimations.add(adapters); return adapters; } @@ -405,16 +404,17 @@ class RemoteAnimationController implements DeathRecipient { mStartBounds = new Rect(startBounds); mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, mStartBounds); - mTmpRect.set(startBounds); - mTmpRect.offsetTo(0, 0); if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { - mThumbnailAdapter = - new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds, - mTmpRect, new Rect()); + final Rect thumbnailLocalBounds = new Rect(startBounds); + thumbnailLocalBounds.offsetTo(0, 0); + // Snapshot is located at (0,0) of the animation leash. It doesn't have size + // change, so the startBounds is its end bounds, and no start bounds for it. + mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0), + thumbnailLocalBounds, startBounds, new Rect()); } } else { mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, - new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom)); + new Rect()); mStartBounds = null; } } @@ -458,15 +458,15 @@ class RemoteAnimationController implements DeathRecipient { private @AnimationType int mAnimationType; final Point mPosition = new Point(); final Rect mLocalBounds; - final Rect mRootTaskBounds = new Rect(); + final Rect mEndBounds = new Rect(); final Rect mStartBounds = new Rect(); RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, - Rect localBounds, Rect rootTaskBounds, Rect startBounds) { + Rect localBounds, Rect endBounds, Rect startBounds) { mRecord = record; mPosition.set(position.x, position.y); mLocalBounds = localBounds; - mRootTaskBounds.set(rootTaskBounds); + mEndBounds.set(endBounds); mStartBounds.set(startBounds); } @@ -480,12 +480,17 @@ class RemoteAnimationController implements DeathRecipient { @AnimationType int type, OnAnimationFinishedCallback finishCallback) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); - // Restore position and stack crop until client has a chance to modify it. if (mStartBounds.isEmpty()) { - t.setPosition(animationLeash, 0, 0); - t.setWindowCrop(animationLeash, -1, -1); + // Restore position and stack crop until client has a chance to modify it. + t.setPosition(animationLeash, mPosition.x, mPosition.y); + t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height()); } else { - t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top); + // Offset the change animation leash to the relative start position in parent. + // (mPosition) is the relative end position in parent container. + // (mStartBounds - mEndBounds) is the position difference between start and end. + // (mPosition + mStartBounds - mEndBounds) will be the relative start position. + t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left, + mPosition.y + mStartBounds.top - mEndBounds.top); t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height()); } mCapturedLeash = animationLeash; diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index fce2f8da10c7..9c4f6f574487 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.view.Surface; @@ -70,13 +71,14 @@ class SurfaceFreezer { * above the target surface) and then taking a snapshot and placing it over the target surface. * * @param startBounds The original bounds (on screen) of the surface we are snapshotting. + * @param relativePosition The related position of the snapshot surface to its parent. */ - void freeze(SurfaceControl.Transaction t, Rect startBounds) { + void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition) { mFreezeBounds.set(startBounds); mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(), t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(), - startBounds.left, startBounds.top, false /* hidden */, + relativePosition.x, relativePosition.y, false /* hidden */, mWmService.mTransactionFactory); mAnimatable.onAnimationLeashCreated(t, mLeash); @@ -123,6 +125,18 @@ class SurfaceFreezer { } } + void setLayer(SurfaceControl.Transaction t, int layer) { + if (mLeash != null) { + t.setLayer(mLeash, layer); + } + } + + void setRelativeLayer(SurfaceControl.Transaction t, SurfaceControl relativeTo, int layer) { + if (mLeash != null) { + t.setRelativeLayer(mLeash, relativeTo, layer); + } + } + boolean hasLeash() { return mLeash != null; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3f6708d1843c..c5362d38c613 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4809,10 +4809,6 @@ class Task extends TaskFragment { notifyClients); }, true /* traverseTopToBottom */); - // Notify WM shell that task visibilities may have changed - forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false), - true /* traverseTopToBottom */); - if (mTranslucentActivityWaiting != null && mUndrawnActivitiesBelowTopTranslucent.isEmpty()) { // Nothing is getting drawn or everything was already visible, don't wait for diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index a257e902dc46..66f2dbc8ec09 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1336,12 +1336,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { final Task launchRootTask = mLaunchRootTasks.get(i).task; - // Return the focusable root task for improving the UX with staged split screen. final TaskFragment adjacentTaskFragment = launchRootTask != null ? launchRootTask.getAdjacentTaskFragment() : null; final Task adjacentRootTask = adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null; - if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) { + if (sourceTask != null && sourceTask.getRootTask() == adjacentRootTask) { return adjacentRootTask; } else { return launchRootTask; diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index f43cd7a80ede..9cc24e2b2120 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -261,7 +261,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { if (launchMode == WINDOWING_MODE_PINNED) { if (DEBUG) appendLog("picture-in-picture"); } else if (!root.isResizeable()) { - if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) { + if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, + options.getLaunchWindowingMode())) { launchMode = WINDOWING_MODE_FREEFORM; if (outParams.mBounds.isEmpty()) { getTaskBounds(root, suggestedDisplayArea, layout, launchMode, @@ -617,7 +618,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity, - TaskDisplayArea displayArea) { + TaskDisplayArea displayArea, int launchWindowingMode) { + if (launchWindowingMode == WINDOWING_MODE_FULLSCREEN) { + // Do not launch the activity in freeform if it explicitly requested fullscreen mode. + return false; + } if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 28beaf36d435..ccda126b3e95 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -688,6 +688,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (state != null) { state.mOrganizer.onTaskVanished(task); } + mLastSentTaskInfos.remove(task); break; case PendingTaskEvent.EVENT_INFO_CHANGED: dispatchTaskInfoChanged(event.mTask, event.mForce); diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 08758afcb0b2..073a508083f0 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -21,7 +21,6 @@ import static com.android.server.wm.AnimationSpecProto.WINDOW; import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE; -import android.annotation.ColorInt; import android.graphics.Point; import android.graphics.Rect; import android.os.SystemClock; @@ -86,12 +85,6 @@ public class WindowAnimationSpec implements AnimationSpec { } @Override - @ColorInt - public int getBackgroundColor() { - return mAnimation.getBackgroundColor(); - } - - @Override public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) { final TmpValues tmp = mThreadLocalTmps.get(); tmp.transformation.clear(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 54644151c630..2a8fa1086799 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2334,10 +2334,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } protected void setLayer(Transaction t, int layer) { - - // Route through surface animator to accommodate that our surface control might be - // attached to the leash, and leash is attached to parent container. - mSurfaceAnimator.setLayer(t, layer); + if (mSurfaceFreezer.hasLeash()) { + // When the freezer has created animation leash parent for the window, set the layer + // there instead. + mSurfaceFreezer.setLayer(t, layer); + } else { + // Route through surface animator to accommodate that our surface control might be + // attached to the leash, and leash is attached to parent container. + mSurfaceAnimator.setLayer(t, layer); + } } int getLastLayer() { @@ -2345,10 +2350,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { - - // Route through surface animator to accommodate that our surface control might be - // attached to the leash, and leash is attached to parent container. - mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer); + if (mSurfaceFreezer.hasLeash()) { + // When the freezer has created animation leash parent for the window, set the layer + // there instead. + mSurfaceFreezer.setRelativeLayer(t, relativeTo, layer); + } else { + // Route through surface animator to accommodate that our surface control might be + // attached to the leash, and leash is attached to parent container. + mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer); + } } protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) { @@ -2613,11 +2623,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. * * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case. + * + * @param startBounds The original bounds (on screen) of the surface we are snapshotting. + * @param parentBounds The parent bounds (on screen) to calculate the animation surface + * position. */ - void initializeChangeTransition(Rect startBounds) { + void initializeChangeTransition(Rect startBounds, Rect parentBounds) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); - mSurfaceFreezer.freeze(getSyncTransaction(), startBounds); + mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top); + mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint); + } + + void initializeChangeTransition(Rect startBounds) { + initializeChangeTransition(startBounds, getParent().getBounds()); } ArraySet<WindowContainer> getAnimationSources() { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 4cc764ac33ca..69313810fa93 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -318,7 +318,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub * @param caller Info about the calling process. */ private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId, - @Nullable Transition transition, @Nullable CallerInfo caller) { + @Nullable Transition transition, @NonNull CallerInfo caller) { int effects = 0; ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); mService.deferWindowLayout(); @@ -540,7 +540,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, - @Nullable CallerInfo caller, @Nullable IBinder errorCallbackToken, + @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) { final int type = hop.getType(); switch (type) { @@ -628,11 +628,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final int taskId = launchOpts.getInt( WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); - final SafeActivityOptions safeOptions = caller != null - ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid) - : SafeActivityOptions.fromBundle(launchOpts); - mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid, - taskId, safeOptions); + final SafeActivityOptions safeOptions = + SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); + final Integer[] starterResult = { null }; + // startActivityFromRecents should not be called in lock. + mService.mH.post(() -> { + try { + starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents( + caller.mPid, caller.mUid, taskId, safeOptions); + } catch (Throwable t) { + starterResult[0] = ActivityManager.START_CANCELED; + Slog.w(TAG, t); + } + synchronized (mGlobalLock) { + mGlobalLock.notifyAll(); + } + }); + while (starterResult[0] == null) { + try { + mGlobalLock.wait(); + } catch (InterruptedException ignored) { + } + } break; case HIERARCHY_OP_TYPE_PENDING_INTENT: String resolvedType = hop.getActivityIntent() != null diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 6eb2e8a2fd54..719d52c89209 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -264,7 +264,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } onConfigurationChanged(atm.getGlobalConfiguration()); - mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mName); + mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName); } public void setPid(int pid) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6b4b0c94f657..c283ef07dce6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8414,17 +8414,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mDeviceAdminServiceController.startServiceForOwner( admin.getPackageName(), userId, "set-device-owner"); - Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); + Slogf.i(LOG_TAG, "Device owner set: %s on user %d", admin.flattenToShortString(), + userId); if (mInjector.userManagerIsHeadlessSystemUserMode()) { int currentForegroundUser = getCurrentForegroundUserId(); - Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin - + " as profile owner on user " + currentForegroundUser); + Slogf.i(LOG_TAG, "setDeviceOwner(): setting %s as profile owner on user %d", + admin.flattenToShortString(), currentForegroundUser); // Sets profile owner on current foreground user since // the human user will complete the DO setup workflow from there. - manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, + mInjector.binderWithCleanCallingIdentity(() -> manageUserUnchecked( + /* deviceOwner= */ admin, /* profileOwner= */ admin, /* managedUser= */ currentForegroundUser, /* adminExtras= */ null, - /* showDisclaimer= */ false); + /* showDisclaimer= */ false)); } return true; } diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java index bf97042d7b88..df19be4a9cfe 100644 --- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java @@ -31,8 +31,6 @@ import android.util.ArraySet; import androidx.test.filters.SmallTest; -import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -183,115 +181,84 @@ public class AppCompatOverridesParserTest { } @Test - public void parsePackageOverrides_emptyConfig_returnsEmpty() { - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet()); + public void parsePackageOverrides_emptyConfigNoOwnedChangeIds_returnsEmpty() { + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet()); - assertThat(result.overridesToAdd).isEmpty(); - assertThat(result.overridesToRemove).isEmpty(); + assertThat(result).isEmpty(); } @Test public void parsePackageOverrides_configWithSingleOverride_returnsOverride() { - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet()); + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ + emptySet()); - assertThat(result.overridesToAdd).hasSize(1); - assertThat(result.overridesToAdd.get(123L)).isEqualTo( + assertThat(result).hasSize(1); + assertThat(result.get(123L)).isEqualTo( new PackageOverride.Builder().setEnabled(true).build()); } @Test - public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() { - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true," - + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true," - + "56:6::true,1112:6:7:false", /* versionCode= */ + public void parsePackageOverrides_configWithMultipleOverrides_returnsOverrides() { + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true," + + "56::2:true,56:3:4:false,34:4:8:true,78:6:7:true,910:5::true," + + "1112::5:true,56:6::true,1112:6:7:false", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet()); - assertThat(result.overridesToAdd).hasSize(6); - assertThat(result.overridesToAdd.get(12L)).isEqualTo( + assertThat(result).hasSize(6); + assertThat(result.get(12L)).isEqualTo( new PackageOverride.Builder().setEnabled(false).build()); - assertThat(result.overridesToAdd.get(34L)).isEqualTo( + assertThat(result.get(34L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled( true).build()); - assertThat(result.overridesToAdd.get(56L)).isEqualTo( + assertThat(result.get(56L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled( false).build()); - assertThat(result.overridesToAdd.get(78L)).isEqualTo( + assertThat(result.get(78L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled( true).build()); - assertThat(result.overridesToAdd.get(910L)).isEqualTo( + assertThat(result.get(910L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build()); - assertThat(result.overridesToAdd.get(1112L)).isEqualTo( + assertThat(result.get(1112L)).isEqualTo( new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build()); - assertThat(result.overridesToRemove).isEmpty(); - } - - @Test - public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() { - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet()); - - assertThat(result.overridesToRemove).containsExactly(12L, 34L); - assertThat(result.overridesToAdd).isEmpty(); - } - - @Test - public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() { - // Note that change 56 is both added and removed, therefore it will only be removed. - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */ - emptySet()); - - assertThat(result.overridesToAdd).hasSize(1); - assertThat(result.overridesToAdd.get(12L)).isEqualTo( - new PackageOverride.Builder().setEnabled(true).build()); - assertThat(result.overridesToRemove).containsExactly(34L, 56L); } @Test public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() { - ArraySet<Long> changeIdsToSkip = new ArraySet<>(); - changeIdsToSkip.add(34L); - changeIdsToSkip.add(56L); - changeIdsToSkip.add(910L); - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip); - - assertThat(result.overridesToAdd).hasSize(1); - assertThat(result.overridesToAdd.get(12L)).isEqualTo( + ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(34L, 56L)); + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "12:::true,56:3:7:true", /* versionCode= */ 5, changeIdsToSkip); + + assertThat(result).hasSize(1); + assertThat(result.get(12L)).isEqualTo( new PackageOverride.Builder().setEnabled(true).build()); - assertThat(result.overridesToRemove).containsExactly(78L); } @Test public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() { - ArraySet<Long> changeIdsToSkip = new ArraySet<>(); - changeIdsToSkip.add(12L); - changeIdsToSkip.add(34L); - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip); - - assertThat(result.overridesToAdd).isEmpty(); - assertThat(result.overridesToRemove).isEmpty(); + ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(12L, 34L)); + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "12:::true", /* versionCode= */ 5, changeIdsToSkip); + + assertThat(result).isEmpty(); } @Test public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() { // We add a valid entry before and after the invalid ones to make sure they are applied. - PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */ - "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no," - + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:", + Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides( + /* configStr= */ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no," + + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:::", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet()); - assertThat(result.overridesToAdd).hasSize(2); - assertThat(result.overridesToAdd.get(12L)).isEqualTo( + assertThat(result).hasSize(2); + assertThat(result.get(12L)).isEqualTo( new PackageOverride.Builder().setEnabled(true).build()); - assertThat(result.overridesToAdd.get(56L)).isEqualTo( + assertThat(result.get(56L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled( false).build()); - assertThat(result.overridesToRemove).containsExactly(34L); } private static ApplicationInfo createAppInfo(String packageName) { diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java index 312927206a80..007191f02631 100644 --- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java @@ -95,7 +95,6 @@ public class AppCompatOverridesServiceTest { private static final String PACKAGE_2 = "com.android.test2"; private static final String PACKAGE_3 = "com.android.test3"; private static final String PACKAGE_4 = "com.android.test4"; - private static final String PACKAGE_5 = "com.android.test5"; private MockContext mMockContext; private BroadcastReceiver mPackageReceiver; @@ -157,16 +156,14 @@ public class AppCompatOverridesServiceTest { mockGetApplicationInfoNotInstalled(PACKAGE_2); mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10); mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1); - mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1); mService.registerDeviceConfigListeners(); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) - .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true") + .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789") + .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true,789:::false") .setString(PACKAGE_2, "123:::true") - .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::") - .setString(PACKAGE_4, "") - .setString(PACKAGE_5, "123:::,789:::") - .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build()); + .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true") + .setString(PACKAGE_4, "").build()); Map<Long, PackageOverride> addedOverrides; // Package 1 @@ -175,11 +172,13 @@ public class AppCompatOverridesServiceTest { verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1)); addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides; - assertThat(addedOverrides).hasSize(2); + assertThat(addedOverrides).hasSize(3); assertThat(addedOverrides.get(123L)).isEqualTo( new PackageOverride.Builder().setEnabled(true).build()); assertThat(addedOverrides.get(456L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build()); + assertThat(addedOverrides.get(789L)).isEqualTo( + new PackageOverride.Builder().setEnabled(false).build()); // Package 2 verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( any(CompatibilityOverrideConfig.class), eq(PACKAGE_2)); @@ -195,24 +194,37 @@ public class AppCompatOverridesServiceTest { assertThat(addedOverrides.get(123L)).isEqualTo( new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode( 11).setEnabled(false).build()); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L); // Package 4 verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( any(CompatibilityOverrideConfig.class), eq(PACKAGE_4)); - verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( - any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4)); - // Package 5 - verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( - any(CompatibilityOverrideConfig.class), eq(PACKAGE_5)); verify(mPlatformCompat).removeOverridesOnReleaseBuilds( - mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5)); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L); + mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_4)); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L, + 789L); + } + + @Test + public void onPropertiesChanged_ownedChangeIdsFlagNotSet_onlyAddsOverrides() + throws Exception { + mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); + + mService.registerDeviceConfigListeners(); + DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) + .setString(PACKAGE_1, "123:::true").build()); + + verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(), + eq(PACKAGE_1)); + verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( + any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1)); + assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L); } @Test public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove() throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) + .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789") .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123") .setString(PACKAGE_1, "123:::true") .setString(PACKAGE_4, "123:::true").build()); @@ -222,7 +234,7 @@ public class AppCompatOverridesServiceTest { mService.registerDeviceConfigListeners(); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) - .setString(PACKAGE_1, "123:::true,456:::,789:::false") + .setString(PACKAGE_1, "123:::true,789:::false") .setString(PACKAGE_2, "123:::true") .setString(PACKAGE_3, "456:::true").build()); @@ -235,14 +247,16 @@ public class AppCompatOverridesServiceTest { // Package 2 verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( any(CompatibilityOverrideConfig.class), eq(PACKAGE_2)); - verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( - any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2)); + verify(mPlatformCompat).removeOverridesOnReleaseBuilds( + mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2)); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L); // Package 3 verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_3)); - verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( - any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3)); + verify(mPlatformCompat).removeOverridesOnReleaseBuilds( + mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3)); assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L); // Package 4 (not applied because it hasn't changed after the listener was added) verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( any(CompatibilityOverrideConfig.class), eq(PACKAGE_4)); @@ -253,27 +267,44 @@ public class AppCompatOverridesServiceTest { @Test public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly() throws Exception { + DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) + .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789") + .setString(PACKAGE_1, "") + .setString(PACKAGE_2, "").build()); + mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); + mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0); + mService.registerDeviceConfigListeners(); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) .setString(FLAG_REMOVE_OVERRIDES, - PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build()); + PACKAGE_1 + "=123:456," + PACKAGE_2 + "=*").build()); // Package 1 - verify(mPlatformCompat).removeOverridesOnReleaseBuilds( + verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( + any(CompatibilityOverrideConfig.class), eq(PACKAGE_1)); + verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds( mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1)); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L); + List<CompatibilityOverridesToRemoveConfig> configs = + mOverridesToRemoveConfigCaptor.getAllValues(); + assertThat(configs.size()).isAtLeast(2); + assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L, 456L); + assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(789L); // Package 2 + verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( + any(CompatibilityOverrideConfig.class), eq(PACKAGE_2)); verify(mPlatformCompat).removeOverridesOnReleaseBuilds( mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2)); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L, + 789L); } @Test public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok() throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) + .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789") .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456") - .setString(PACKAGE_1, "123:::true,456:::,789:::false") + .setString(PACKAGE_1, "123:::true,789:::false") .setString(PACKAGE_3, "456:::false,789:::true").build()); mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0); @@ -282,7 +313,7 @@ public class AppCompatOverridesServiceTest { mService.registerDeviceConfigListeners(); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789") - .setString(PACKAGE_2, "123:::true,456:::").build()); + .setString(PACKAGE_2, "123:::true").build()); // Package 1 verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(), @@ -301,14 +332,17 @@ public class AppCompatOverridesServiceTest { mOverridesToRemoveConfigCaptor.getAllValues(); assertThat(configs.size()).isAtLeast(2); assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L); - assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L); + assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L, 789L); // Package 3 verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_3)); - verify(mPlatformCompat).removeOverridesOnReleaseBuilds( + verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds( mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3)); assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L); + configs = mOverridesToRemoveConfigCaptor.getAllValues(); + assertThat(configs.size()).isAtLeast(2); + assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(789L); + assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(123L); } @Test @@ -338,9 +372,10 @@ public class AppCompatOverridesServiceTest { // Package 2 verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_2)); - verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( - any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2)); + verify(mPlatformCompat).removeOverridesOnReleaseBuilds( + mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2)); assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L); + assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L, 789L); // Package 3 verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds( any(CompatibilityOverrideConfig.class), eq(PACKAGE_3)); @@ -362,10 +397,11 @@ public class AppCompatOverridesServiceTest { mService.registerDeviceConfigListeners(); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) - .setString(PACKAGE_1, "123:::true,456:::") - .setString(PACKAGE_2, "123:::true,456:::") - .setString(PACKAGE_3, "123:::true,456:::") - .setString(PACKAGE_4, "123:::true,456:::").build()); + .setString(FLAG_OWNED_CHANGE_IDS, "123,456") + .setString(PACKAGE_1, "123:::true") + .setString(PACKAGE_2, "123:::true") + .setString(PACKAGE_3, "123:::true") + .setString(PACKAGE_4, "123:::true").build()); // Package 1 verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class), @@ -478,12 +514,16 @@ public class AppCompatOverridesServiceTest { @Test public void packageReceiver_packageAddedIntent_appliesOverridesFromAllNamespaces() throws Exception { + // We're adding the owned_change_ids flag to make sure it's ignored. DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) - .setString(PACKAGE_1, "101:::true,103:::") + .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103") + .setString(PACKAGE_1, "101:::true") .setString(PACKAGE_2, "102:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2) + .setString(FLAG_OWNED_CHANGE_IDS, "201,202,203") .setString(PACKAGE_3, "201:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3) + .setString(FLAG_OWNED_CHANGE_IDS, "301,302") .setString(PACKAGE_1, "301:::true,302:::false") .setString(PACKAGE_2, "302:::false").build()); mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); @@ -493,19 +533,18 @@ public class AppCompatOverridesServiceTest { verify(mPlatformCompat, times(2)).putOverridesOnReleaseBuilds( mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1)); - verify(mPlatformCompat).removeOverridesOnReleaseBuilds( - mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1)); + verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( + any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1)); List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues(); assertThat(configs.get(0).overrides.keySet()).containsExactly(101L); assertThat(configs.get(1).overrides.keySet()).containsExactly(301L, 302L); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L); } @Test public void packageReceiver_packageChangedIntent_appliesOverrides() throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) - .setString(PACKAGE_1, "101:::true,103:::").build()); + .setString(PACKAGE_1, "101:::true,103:::false").build()); mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); mPackageReceiver.onReceive(mMockContext, @@ -513,10 +552,10 @@ public class AppCompatOverridesServiceTest { verify(mPlatformCompat).putOverridesOnReleaseBuilds( mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1)); - verify(mPlatformCompat).removeOverridesOnReleaseBuilds( - mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1)); - assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L); + verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( + any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1)); + assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L, + 103L); } @Test @@ -524,13 +563,13 @@ public class AppCompatOverridesServiceTest { throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=103," + PACKAGE_2 + "=101") - .setString(PACKAGE_1, "101:::true,103:::") + .setString(PACKAGE_1, "101:::true,103:::false") .setString(PACKAGE_2, "102:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2) .setString(PACKAGE_1, "201:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3) .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=301," + PACKAGE_3 + "=302") - .setString(PACKAGE_1, "301:::true,302:::false,303:::") + .setString(PACKAGE_1, "301:::true,302:::false,303:::true") .setString(PACKAGE_3, "302:::false").build()); mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0); @@ -539,13 +578,12 @@ public class AppCompatOverridesServiceTest { verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds( mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1)); - verify(mPlatformCompat).removeOverridesOnReleaseBuilds( - mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1)); + verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds( + any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1)); List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues(); assertThat(configs.get(0).overrides.keySet()).containsExactly(101L); assertThat(configs.get(1).overrides.keySet()).containsExactly(201L); - assertThat(configs.get(2).overrides.keySet()).containsExactly(302L); - assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(303L); + assertThat(configs.get(2).overrides.keySet()).containsExactly(302L, 303L); } @Test @@ -573,7 +611,7 @@ public class AppCompatOverridesServiceTest { throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103") - .setString(PACKAGE_1, "101:::true,103:::").build()); + .setString(PACKAGE_1, "101:::true,103:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2) .setString(FLAG_OWNED_CHANGE_IDS, "201,202") .setString(PACKAGE_1, "202:::false").build()); @@ -593,14 +631,14 @@ public class AppCompatOverridesServiceTest { throws Exception { DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1) .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103") - .setString(PACKAGE_1, "101:::true,103:::") + .setString(PACKAGE_1, "101:::true,103:::false") .setString(PACKAGE_2, "102:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2) .setString(FLAG_OWNED_CHANGE_IDS, "201") .setString(PACKAGE_3, "201:::false").build()); DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3) .setString(FLAG_OWNED_CHANGE_IDS, "301,302") - .setString(PACKAGE_1, "302:::") + .setString(PACKAGE_1, "302:::false") .setString(PACKAGE_2, "301:::true").build()); mockGetApplicationInfoNotInstalled(PACKAGE_1); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 4564296810ff..0dd5c61121db 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -20,6 +20,8 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REF import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; @@ -1408,6 +1410,12 @@ public class DisplayModeDirectorTest { public void testHbmVoting_forHdr() { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + final int hbmRefreshRate = 72; + + // Specify limitation before starting DisplayModeDirector to avoid waiting on property + // propagation + mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hbmRefreshRate); + director.start(createMockSensorManager()); ArgumentCaptor<DisplayListener> captor = @@ -1432,7 +1440,7 @@ public class DisplayModeDirectorTest { new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForRefreshRate(vote, hbmRefreshRate); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1443,6 +1451,44 @@ public class DisplayModeDirectorTest { } @Test + public void testHbmObserverGetsUpdatedRefreshRateInHbmSunlight() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + + final int initialRefreshRate = 60; + mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(initialRefreshRate); + director.start(createMockSensorManager()); + assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()) + .isEqualTo(initialRefreshRate); + + final int updatedRefreshRate = 90; + mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()) + .isEqualTo(updatedRefreshRate); + } + + @Test + public void testHbmObserverGetsUpdatedRefreshRateInHbmHdr() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + + final int initialRefreshRate = 60; + mInjector.getDeviceConfig().setRefreshRateInHbmHdr(initialRefreshRate); + director.start(createMockSensorManager()); + assertThat(director.getHbmObserver().getRefreshRateInHbmHdr()) + .isEqualTo(initialRefreshRate); + + final int updatedRefreshRate = 90; + mInjector.getDeviceConfig().setRefreshRateInHbmHdr(updatedRefreshRate); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getHbmObserver().getRefreshRateInHbmHdr()) + .isEqualTo(updatedRefreshRate); + } + + @Test public void testHbmVoting_forSunlight() { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); @@ -1455,11 +1501,12 @@ public class DisplayModeDirectorTest { | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); DisplayListener listener = captor.getValue(); + final int initialRefreshRate = 60; // Specify Limitation when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn( List.of(new RefreshRateLimitation( DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, - 60.f, 60.f))); + initialRefreshRate, initialRefreshRate))); // Verify that there is no HBM vote initially Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); @@ -1470,7 +1517,39 @@ public class DisplayModeDirectorTest { new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); - assertVoteForRefreshRate(vote, 60.f); + assertVoteForRefreshRate(vote, initialRefreshRate); + + // Change refresh rate vote value through DeviceConfig, ensure it takes precedence + final int updatedRefreshRate = 90; + mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()) + .isEqualTo(updatedRefreshRate); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, updatedRefreshRate); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn HBM on again and ensure the updated vote value stuck + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, updatedRefreshRate); + + // Reset DeviceConfig refresh rate, ensure vote falls back to the initial value + mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, initialRefreshRate); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( @@ -1518,6 +1597,63 @@ public class DisplayModeDirectorTest { assertNull(vote); } + private void setHbmAndAssertRefreshRate( + DisplayModeDirector director, DisplayListener listener, int mode, float rr) { + when(mInjector.getBrightnessInfo(DISPLAY_ID)) + .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode)); + listener.onDisplayChanged(DISPLAY_ID); + + final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + if (Float.isNaN(rr)) { + assertNull(vote); + } else { + assertVoteForRefreshRate(vote, rr); + } + } + + @Test + public void testHbmVoting_forSunlightAndHdr() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + + // Specify HDR limitation before starting DisplayModeDirector to avoid waiting on property + // propagation + final int hdrRr = 60; + mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hdrRr); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Sunlight limitations + final float sunlightRr = 90.0f; + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)) + .thenReturn(List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, sunlightRr, + sunlightRr))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Verify all state transitions + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr); + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr); + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN); + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr); + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr); + setHbmAndAssertRefreshRate( + director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN); + } + @Test public void testHbmVoting_RemovedDisplay() { DisplayModeDirector director = @@ -1622,6 +1758,16 @@ public class DisplayModeDirectorTest { String.valueOf(fps)); } + void setRefreshRateInHbmSunlight(int fps) { + putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps)); + } + + void setRefreshRateInHbmHdr(int fps) { + putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps)); + } + void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) { String thresholds = toPropertyValue(brightnessThresholds); diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 89ae5ed9b425..08312ef0b865 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -341,7 +341,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { verify(mMockTransaction).setWindowCrop( mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); - verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1); + verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), + app.startBounds.height()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), @@ -394,7 +395,63 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { verify(mMockTransaction).setWindowCrop( mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); - verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1); + verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), + app.startBounds.height()); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), + eq(record.mAdapter)); + verify(mThumbnailFinishedCallback).onAnimationFinished( + eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); + } finally { + mDisplayContent.mChangingContainers.clear(); + } + } + + @Test + public void testChangeToDifferentPosition() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mChangingContainers.add(win.mActivityRecord); + try { + final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( + win.mActivityRecord, new Point(100, 100), null, new Rect(150, 150, 400, 400), + new Rect(50, 100, 150, 150)); + assertNotNull(record.mThumbnailAdapter); + ((AnimationAdapter) record.mAdapter) + .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, + mFinishedCallback); + ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, + mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); + mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), + finishedCaptor.capture()); + assertEquals(1, appsCaptor.getValue().length); + final RemoteAnimationTarget app = appsCaptor.getValue()[0]; + assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode); + assertEquals(new Point(100, 100), app.position); + assertEquals(new Rect(150, 150, 400, 400), app.sourceContainerBounds); + assertEquals(new Rect(50, 100, 150, 150), app.startBounds); + assertEquals(mMockLeash, app.leash); + assertEquals(mMockThumbnailLeash, app.startLeash); + assertEquals(false, app.isTranslucent); + verify(mMockTransaction).setPosition( + mMockLeash, app.position.x + app.startBounds.left - app.screenSpaceBounds.left, + app.position.y + app.startBounds.top - app.screenSpaceBounds.top); + verify(mMockTransaction).setWindowCrop( + mMockLeash, app.startBounds.width(), app.startBounds.height()); + verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); + verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), + app.startBounds.height()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 00f3d8b874f7..68053eb324b2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -1071,6 +1071,41 @@ public class WindowContainerTests extends WindowTestsBase { verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 1 /* layer */); } + @Test + public void testAssignAnimationLayer() { + final WindowContainer container = new WindowContainer(mWm); + container.mSurfaceControl = mock(SurfaceControl.class); + final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator; + final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer; + final SurfaceControl relativeParent = mock(SurfaceControl.class); + final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + spyOn(container); + spyOn(surfaceAnimator); + spyOn(surfaceFreezer); + + container.setLayer(t, 1); + container.setRelativeLayer(t, relativeParent, 2); + + // Set through surfaceAnimator if surfaceFreezer doesn't have leash. + verify(surfaceAnimator).setLayer(t, 1); + verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 2); + verify(surfaceFreezer, never()).setLayer(any(), anyInt()); + verify(surfaceFreezer, never()).setRelativeLayer(any(), any(), anyInt()); + + clearInvocations(surfaceAnimator); + clearInvocations(surfaceFreezer); + doReturn(true).when(surfaceFreezer).hasLeash(); + + container.setLayer(t, 1); + container.setRelativeLayer(t, relativeParent, 2); + + // Set through surfaceFreezer if surfaceFreezer has leash. + verify(surfaceFreezer).setLayer(t, 1); + verify(surfaceFreezer).setRelativeLayer(t, relativeParent, 2); + verify(surfaceAnimator, never()).setLayer(any(), anyInt()); + verify(surfaceAnimator, never()).setRelativeLayer(any(), any(), anyInt()); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 9ea2b7b12ad0..8445ed4884e2 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -167,6 +167,16 @@ public class VoiceInteractionManagerService extends SystemService { public void onStart() { publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub); publishLocalService(VoiceInteractionManagerInternal.class, new LocalService()); + mAmInternal.setVoiceInteractionManagerProvider( + new ActivityManagerInternal.VoiceInteractionManagerProvider() { + @Override + public void notifyActivityEventChanged() { + if (DEBUG) { + Slog.d(TAG, "call notifyActivityEventChanged"); + } + mServiceStub.notifyActivityEventChanged(); + } + }); } @Override @@ -386,6 +396,14 @@ public class VoiceInteractionManagerService extends SystemService { return mImpl.supportsLocalVoiceInteraction(); } + void notifyActivityEventChanged() { + synchronized (this) { + if (mImpl == null) return; + + Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked()); + } + } + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -1109,6 +1127,40 @@ public class VoiceInteractionManagerService extends SystemService { } } + @Override + public void startListeningVisibleActivityChanged(@NonNull IBinder token) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "startListeningVisibleActivityChanged without running" + + " voice interaction service"); + return; + } + final long caller = Binder.clearCallingIdentity(); + try { + mImpl.startListeningVisibleActivityChangedLocked(token); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + + @Override + public void stopListeningVisibleActivityChanged(@NonNull IBinder token) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "stopListeningVisibleActivityChanged without running" + + " voice interaction service"); + return; + } + final long caller = Binder.clearCallingIdentity(); + try { + mImpl.stopListeningVisibleActivityChangedLocked(token); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + //----------------- Hotword Detection/Validation APIs --------------------------------// @Override diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 558a9ac9298e..52c5b6bbc239 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -414,6 +414,44 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne return mInfo.getSupportsLocalInteraction(); } + public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) { + if (DEBUG) { + Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token); + } + if (mActiveSession == null || token != mActiveSession.mToken) { + Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match" + + " active session"); + return; + } + mActiveSession.startListeningVisibleActivityChangedLocked(); + } + + public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) { + if (DEBUG) { + Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token); + } + if (mActiveSession == null || token != mActiveSession.mToken) { + Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match" + + " active session"); + return; + } + mActiveSession.stopListeningVisibleActivityChangedLocked(); + } + + public void notifyActivityEventChangedLocked() { + if (DEBUG) { + Slog.d(TAG, "notifyActivityEventChangedLocked"); + } + if (mActiveSession == null || !mActiveSession.mShown) { + if (DEBUG) { + Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or" + + " hidden session"); + } + return; + } + mActiveSession.notifyActivityEventChangedLocked(); + } + public void updateStateLocked( @NonNull Identity voiceInteractorIdentity, @Nullable PersistableBundle options, diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index 08e9703124ab..90ccec852e1e 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -56,6 +56,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSessionService; +import android.service.voice.VisibleActivityInfo; import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionSession; import android.util.Slog; @@ -71,16 +72,20 @@ import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.ActivityAssistInfo; +import com.android.server.wm.ActivityTaskManagerInternal; import java.io.PrintWriter; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; final class VoiceInteractionSessionConnection implements ServiceConnection, AssistDataRequesterCallbacks { static final String TAG = "VoiceInteractionServiceManager"; + static final boolean DEBUG = false; static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt( System.getProperty("vendor.powerhal.interaction.max", "200")); static final int BOOST_TIMEOUT_MS = 300; @@ -114,6 +119,10 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>(); private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>(); AssistDataRequester mAssistDataRequester; + private boolean mListeningVisibleActivity; + private final ScheduledExecutorService mScheduledExecutorService = + Executors.newSingleThreadScheduledExecutor(); + private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>(); private final PowerManagerInternal mPowerManagerInternal; private PowerBoostSetter mSetPowerBoostRunnable; private final Handler mFgHandler; @@ -496,6 +505,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, } public void cancelLocked(boolean finishTask) { + mListeningVisibleActivity = false; + mVisibleActivityInfos.clear(); hideLocked(); mCanceled = true; if (mBound) { @@ -569,6 +580,156 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, mPendingShowCallbacks.clear(); } + void startListeningVisibleActivityChangedLocked() { + if (DEBUG) { + Slog.d(TAG, "startListeningVisibleActivityChangedLocked"); + } + mListeningVisibleActivity = true; + mVisibleActivityInfos.clear(); + + mScheduledExecutorService.execute(() -> { + if (DEBUG) { + Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening"); + } + synchronized (mLock) { + updateVisibleActivitiesLocked(); + } + }); + } + + void stopListeningVisibleActivityChangedLocked() { + if (DEBUG) { + Slog.d(TAG, "stopListeningVisibleActivityChangedLocked"); + } + mListeningVisibleActivity = false; + mVisibleActivityInfos.clear(); + } + + void notifyActivityEventChangedLocked() { + if (DEBUG) { + Slog.d(TAG, "notifyActivityEventChangedLocked"); + } + if (!mListeningVisibleActivity) { + if (DEBUG) { + Slog.d(TAG, "not enable listening visible activity"); + } + return; + } + mScheduledExecutorService.execute(() -> { + if (DEBUG) { + Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event"); + } + synchronized (mLock) { + updateVisibleActivitiesLocked(); + } + }); + } + + private List<VisibleActivityInfo> getVisibleActivityInfosLocked() { + if (DEBUG) { + Slog.d(TAG, "getVisibleActivityInfosLocked"); + } + List<ActivityAssistInfo> allVisibleActivities = + LocalServices.getService(ActivityTaskManagerInternal.class) + .getTopVisibleActivities(); + if (DEBUG) { + Slog.d(TAG, + "getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities); + } + if (allVisibleActivities == null || allVisibleActivities.isEmpty()) { + Slog.w(TAG, "no visible activity"); + return null; + } + final int count = allVisibleActivities.size(); + final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + ActivityAssistInfo info = allVisibleActivities.get(i); + if (DEBUG) { + Slog.d(TAG, " : activityToken=" + info.getActivityToken() + + ", assistToken=" + info.getAssistToken() + + ", taskId=" + info.getTaskId()); + } + visibleActivityInfos.add( + new VisibleActivityInfo(info.getTaskId(), info.getAssistToken())); + } + return visibleActivityInfos; + } + + private void updateVisibleActivitiesLocked() { + if (DEBUG) { + Slog.d(TAG, "updateVisibleActivitiesLocked"); + } + if (mSession == null) { + return; + } + if (!mShown || !mListeningVisibleActivity || mCanceled) { + return; + } + final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked(); + + if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) { + updateVisibleActivitiesChangedLocked(mVisibleActivityInfos, + VisibleActivityInfo.TYPE_ACTIVITY_REMOVED); + mVisibleActivityInfos.clear(); + return; + } + if (mVisibleActivityInfos.isEmpty()) { + updateVisibleActivitiesChangedLocked(newVisibleActivityInfos, + VisibleActivityInfo.TYPE_ACTIVITY_ADDED); + mVisibleActivityInfos.addAll(newVisibleActivityInfos); + return; + } + + final List<VisibleActivityInfo> addedActivities = new ArrayList<>(); + final List<VisibleActivityInfo> removedActivities = new ArrayList<>(); + + removedActivities.addAll(mVisibleActivityInfos); + for (int i = 0; i < newVisibleActivityInfos.size(); i++) { + final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i); + if (!removedActivities.isEmpty() && removedActivities.contains( + candidateVisibleActivityInfo)) { + removedActivities.remove(candidateVisibleActivityInfo); + } else { + addedActivities.add(candidateVisibleActivityInfo); + } + } + + if (!addedActivities.isEmpty()) { + updateVisibleActivitiesChangedLocked(addedActivities, + VisibleActivityInfo.TYPE_ACTIVITY_ADDED); + } + if (!removedActivities.isEmpty()) { + updateVisibleActivitiesChangedLocked(removedActivities, + VisibleActivityInfo.TYPE_ACTIVITY_REMOVED); + } + + mVisibleActivityInfos.clear(); + mVisibleActivityInfos.addAll(newVisibleActivityInfos); + } + + private void updateVisibleActivitiesChangedLocked( + List<VisibleActivityInfo> visibleActivityInfos, int type) { + if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) { + return; + } + if (mSession == null) { + return; + } + try { + for (int i = 0; i < visibleActivityInfos.size(); i++) { + mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type); + } + } catch (RemoteException e) { + if (DEBUG) { + Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e); + } + } + if (DEBUG) { + Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count=" + + visibleActivityInfos.size()); + } + } + @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { |