diff options
69 files changed, 1819 insertions, 1129 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 52e64e80e6d4..44dc28d2b0fa 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2618,15 +2618,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2698,15 +2689,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 8cc4cdb955ca..3e1c5bb3d7ec 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -316,15 +316,6 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Parsed package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -377,27 +368,19 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } - // If it's a pre-release SDK and the codename matches this platform, it - // definitely targets this SDK. - if (matchTargetCode(platformSdkCodenames, targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); - } - - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Parsed package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - try { if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } } catch (IllegalArgumentException e) { - return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); + // isAtMost() throws it when encountering an older SDK codename + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage()); + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (matchTargetCode(platformSdkCodenames, targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } // Otherwise, we're looking at an incompatible pre-release SDK. diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java new file mode 100644 index 000000000000..9e66f54321cc --- /dev/null +++ b/core/java/android/view/InsetsFrameProvider.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Insets; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Insets provided by a window. + * + * The insets frame will by default as the window frame size. If the providers are set, the + * calculation result based on the source size will be used as the insets frame. + * @hide + */ +public class InsetsFrameProvider implements Parcelable { + + /** + * If specified in source field, the insets calculation will be based on the display frame. + */ + public static final int SOURCE_DISPLAY = 0; + + /** + * If specified in source field, the insets calculation will be based on the window bounds. The + * container bounds can sometimes be different from the window frame. For example, when a task + * bar needs the entire screen to be prepared to showing the apps, the window container can take + * the entire display, or display area, but the window frame, as a result of the layout, will + * stay small until it actually taking the entire display to draw their view. + */ + public static final int SOURCE_CONTAINER_BOUNDS = 1; + + /** + * If specified in source field, the insets calculation will be based on the window frame. This + * is also the default value of the source. + */ + public static final int SOURCE_FRAME = 2; + + private static final int HAS_INSETS_SIZE = 1; + private static final int HAS_IME_INSETS_SIZE = 2; + + /** + * The type of insets to provide. + */ + public @InsetsState.InternalInsetsType int type; + + /** + * The source of frame. By default, all adjustment will be based on the window frame, it + * can be set to window bounds or display bounds instead. + */ + public int source = SOURCE_FRAME; + + /** + * The provided insets size based on the source frame. The result will be used as the insets + * size to windows other than IME. Only one side should be set. + * + * For example, when the given source frame is (0, 0) - (100, 200), and the insetsSize is null, + * the source frame will be directly used as the final insets frame. If the insetsSize is set to + * (0, 0, 0, 50) instead, the insets frame will be a frame starting from the bottom side of the + * source frame with height of 50, i.e., (0, 150) - (100, 200). + */ + public Insets insetsSize = null; + + /** + * The provided frame based on the source frame. The result will be used as the insets + * size to IME window. Only one side should be set. + */ + public Insets imeInsetsSize = null; + + public InsetsFrameProvider(int type) { + this(type, SOURCE_FRAME, null, null); + } + + public InsetsFrameProvider(int type, Insets insetsSize) { + this(type, SOURCE_FRAME, insetsSize, insetsSize); + } + + public InsetsFrameProvider(int type, Insets insetsSize, Insets imeInsetsSize) { + this(type, SOURCE_FRAME, insetsSize, imeInsetsSize); + } + + public InsetsFrameProvider(int type, int source, Insets insetsSize, + Insets imeInsetsSize) { + this.type = type; + this.source = source; + this.insetsSize = insetsSize; + this.imeInsetsSize = imeInsetsSize; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(32); + sb.append("InsetsFrameProvider: {"); + sb.append("type=").append(InsetsState.typeToString(type)); + sb.append(", source="); + switch (source) { + case SOURCE_DISPLAY: + sb.append("SOURCE_DISPLAY"); + break; + case SOURCE_CONTAINER_BOUNDS: + sb.append("SOURCE_CONTAINER_BOUNDS Bounds"); + break; + case SOURCE_FRAME: + sb.append("SOURCE_FRAME"); + break; + } + if (insetsSize != null) { + sb.append(", insetsSize=").append(insetsSize); + } + if (imeInsetsSize != null) { + sb.append(", imeInsetsSize=").append(imeInsetsSize); + } + sb.append("}"); + return sb.toString(); + } + + public InsetsFrameProvider(Parcel in) { + int insetsSizeModified = in.readInt(); + type = in.readInt(); + source = in.readInt(); + if ((insetsSizeModified & HAS_INSETS_SIZE) != 0) { + insetsSize = Insets.CREATOR.createFromParcel(in); + } + if ((insetsSizeModified & HAS_IME_INSETS_SIZE) != 0) { + imeInsetsSize = Insets.CREATOR.createFromParcel(in); + } + } + + @Override + public void writeToParcel(Parcel out, int flags) { + int insetsSizeModified = 0; + if (insetsSize != null) { + insetsSizeModified |= HAS_INSETS_SIZE; + } + if (imeInsetsSize != null) { + insetsSizeModified |= HAS_IME_INSETS_SIZE; + } + out.writeInt(insetsSizeModified); + out.writeInt(type); + out.writeInt(source); + if (insetsSize != null) { + insetsSize.writeToParcel(out, flags); + } + if (imeInsetsSize != null) { + imeInsetsSize.writeToParcel(out, flags); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InsetsFrameProvider other = (InsetsFrameProvider) o; + return type == other.type && source == other.source + && Objects.equals(insetsSize, other.insetsSize) + && Objects.equals(imeInsetsSize, other.imeInsetsSize); + } + + @Override + public int hashCode() { + int result = type; + result = 31 * result + source; + result = 31 * result + (insetsSize != null ? insetsSize.hashCode() : 0); + result = 31 * result + (imeInsetsSize != null ? imeInsetsSize.hashCode() : 0); + return result; + } + + public static final @android.annotation.NonNull Parcelable.Creator<InsetsFrameProvider> + CREATOR = new Parcelable.Creator<InsetsFrameProvider>() { + @Override + public InsetsFrameProvider createFromParcel(Parcel in) { + return new InsetsFrameProvider(in); + } + + @Override + public InsetsFrameProvider[] newArray(int size) { + return new InsetsFrameProvider[size]; + } + }; +} + diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index f6e4f6e7ef2b..11e02e03c4e4 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -404,27 +404,20 @@ public class InsetsState implements Parcelable { if (source == null) { continue; } - if (!canControlSide(frame, getInsetSide( - source.calculateInsets(frame, true /* ignoreVisibility */)))) { + if (!canControlSource(frame, source)) { blocked |= toPublicType(type); } } return blocked; } - private boolean canControlSide(Rect frame, int side) { - switch (side) { - case ISIDE_LEFT: - case ISIDE_RIGHT: - return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right; - case ISIDE_TOP: - case ISIDE_BOTTOM: - return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom; - case ISIDE_FLOATING: - return true; - default: - return false; - } + private static boolean canControlSource(Rect frame, InsetsSource source) { + final Insets insets = source.calculateInsets(frame, true /* ignoreVisibility */); + final Rect sourceFrame = source.getFrame(); + final int sourceWidth = sourceFrame.width(); + final int sourceHeight = sourceFrame.height(); + return insets.left == sourceWidth || insets.right == sourceWidth + || insets.top == sourceHeight || insets.bottom == sourceHeight; } private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 690981eaa14a..326b8514ce00 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -98,7 +98,6 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -3603,34 +3602,17 @@ public interface WindowManager extends ViewManager { private boolean mFitInsetsIgnoringVisibility = false; /** - * {@link InsetsState.InternalInsetsType}s to be applied to the window - * If {@link #type} has the predefined insets (like {@link #TYPE_STATUS_BAR} or - * {@link #TYPE_NAVIGATION_BAR}), this field will be ignored. + * If set, the specified insets types will be provided by the window and the insets frame + * will be calculated based on the provider's parameters. The insets types and the array + * should not be modified after the window is added. If multiple layout parameters are + * provided for different rotations in {@link LayoutParams#paramsForRotation}, the types in + * the providedInsets array should be the same in all rotations, including the base one. + * All other params can be adjusted at runtime. + * See {@link InsetsFrameProvider}. * - * <p>Note: provide only one inset corresponding to the window type (like - * {@link InsetsState.InternalInsetsType#ITYPE_STATUS_BAR} or - * {@link InsetsState.InternalInsetsType#ITYPE_NAVIGATION_BAR})</p> * @hide */ - public @InsetsState.InternalInsetsType int[] providesInsetsTypes; - - /** - * If specified, the insets provided by this window will be our window frame minus the - * insets specified by providedInternalInsets for each type. This should not be used - * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will - * be applied. - * - * @hide - */ - public Insets[] providedInternalInsets; - - /** - * If specified, the insets provided by this window for the IME will be our window frame - * minus the insets specified by providedInternalImeInsets. - * - * @hide - */ - public Insets[] providedInternalImeInsets; + public InsetsFrameProvider[] providedInsets; /** * If specified, the frame that used to calculate relative {@link RoundedCorner} will be @@ -4010,32 +3992,10 @@ public interface WindowManager extends ViewManager { out.writeBoolean(mFitInsetsIgnoringVisibility); out.writeBoolean(preferMinimalPostProcessing); out.writeInt(mBlurBehindRadius); - if (providesInsetsTypes != null) { - out.writeInt(providesInsetsTypes.length); - out.writeIntArray(providesInsetsTypes); - } else { - out.writeInt(0); - } - if (providedInternalInsets != null) { - out.writeInt(providedInternalInsets.length); - out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */); - } else { - out.writeInt(0); - } - if (providedInternalImeInsets != null) { - out.writeInt(providedInternalImeInsets.length); - out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */); - } else { - out.writeInt(0); - } out.writeBoolean(insetsRoundedCornerFrame); - if (paramsForRotation != null) { - checkNonRecursiveParams(); - out.writeInt(paramsForRotation.length); - out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */); - } else { - out.writeInt(0); - } + out.writeTypedArray(providedInsets, 0 /* parcelableFlags */); + checkNonRecursiveParams(); + out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */); } public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR @@ -4102,27 +4062,9 @@ public interface WindowManager extends ViewManager { mFitInsetsIgnoringVisibility = in.readBoolean(); preferMinimalPostProcessing = in.readBoolean(); mBlurBehindRadius = in.readInt(); - int insetsTypesLength = in.readInt(); - if (insetsTypesLength > 0) { - providesInsetsTypes = new int[insetsTypesLength]; - in.readIntArray(providesInsetsTypes); - } - int providedInternalInsetsLength = in.readInt(); - if (providedInternalInsetsLength > 0) { - providedInternalInsets = new Insets[providedInternalInsetsLength]; - in.readTypedArray(providedInternalInsets, Insets.CREATOR); - } - int providedInternalImeInsetsLength = in.readInt(); - if (providedInternalImeInsetsLength > 0) { - providedInternalImeInsets = new Insets[providedInternalImeInsetsLength]; - in.readTypedArray(providedInternalImeInsets, Insets.CREATOR); - } insetsRoundedCornerFrame = in.readBoolean(); - int paramsForRotationLength = in.readInt(); - if (paramsForRotationLength > 0) { - paramsForRotation = new LayoutParams[paramsForRotationLength]; - in.readTypedArray(paramsForRotation, LayoutParams.CREATOR); - } + providedInsets = in.createTypedArray(InsetsFrameProvider.CREATOR); + paramsForRotation = in.createTypedArray(LayoutParams.CREATOR); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -4414,18 +4356,8 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } - if (!Arrays.equals(providesInsetsTypes, o.providesInsetsTypes)) { - providesInsetsTypes = o.providesInsetsTypes; - changes |= LAYOUT_CHANGED; - } - - if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) { - providedInternalInsets = o.providedInternalInsets; - changes |= LAYOUT_CHANGED; - } - - if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) { - providedInternalImeInsets = o.providedInternalImeInsets; + if (!Arrays.equals(providedInsets, o.providedInsets)) { + providedInsets = o.providedInsets; changes |= LAYOUT_CHANGED; } @@ -4627,28 +4559,12 @@ public interface WindowManager extends ViewManager { sb.append(System.lineSeparator()); sb.append(prefix).append(" fitIgnoreVis"); } - if (providesInsetsTypes != null) { - sb.append(System.lineSeparator()); - sb.append(prefix).append(" insetsTypes="); - for (int i = 0; i < providesInsetsTypes.length; ++i) { - if (i > 0) sb.append(' '); - sb.append(InsetsState.typeToString(providesInsetsTypes[i])); - } - } - if (providedInternalInsets != null) { - sb.append(System.lineSeparator()); - sb.append(" providedInternalInsets="); - for (int i = 0; i < providedInternalInsets.length; ++i) { - if (i > 0) sb.append(' '); - sb.append((providedInternalInsets[i])); - } - } - if (providedInternalImeInsets != null) { + if (providedInsets != null) { sb.append(System.lineSeparator()); - sb.append(" providedInternalImeInsets="); - for (int i = 0; i < providedInternalImeInsets.length; ++i) { + sb.append(" providedInsets="); + for (int i = 0; i < providedInsets.length; ++i) { if (i > 0) sb.append(' '); - sb.append((providedInternalImeInsets[i])); + sb.append((providedInsets[i])); } } if (insetsRoundedCornerFrame) { diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 36a3c5c84ed2..40429c609150 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1501,7 +1501,10 @@ public class ResolverActivity extends Activity implements : R.string.miniresolver_use_personal_browser); findViewById(R.id.use_same_profile_browser).setOnClickListener( - v -> safelyStartActivity(sameProfileResolveInfo)); + v -> { + safelyStartActivity(sameProfileResolveInfo); + finish(); + }); findViewById(R.id.button_open).setOnClickListener(v -> { Intent intent = otherProfileResolveInfo.getResolvedIntent(); @@ -1510,6 +1513,7 @@ public class ResolverActivity extends Activity implements } safelyStartActivityAsUser(otherProfileResolveInfo, inactiveAdapter.mResolverListController.getUserHandle()); + finish(); }); } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 3436b9e75c65..9b583be547c3 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -113,6 +113,24 @@ public class SystemConfig { final ArrayList<SplitPermissionInfo> mSplitPermissions = new ArrayList<>(); + private static boolean isAtLeastSdkLevel(String version) { + try { + return UnboundedSdkLevel.isAtLeast(version); + } catch (IllegalArgumentException e) { + // UnboundedSdkLevel throws when it sees a known old codename + return false; + } + } + + private static boolean isAtMostSdkLevel(String version) { + try { + return UnboundedSdkLevel.isAtMost(version); + } catch (IllegalArgumentException e) { + // UnboundedSdkLevel throws when it sees a known old codename + return true; + } + } + public static final class SharedLibraryEntry { public final String name; public final String filename; @@ -180,9 +198,9 @@ public class SystemConfig { // - onBootclasspathBefore is set and we are before that SDK canBeSafelyIgnored = (this.onBootclasspathSince != null - && UnboundedSdkLevel.isAtLeast(this.onBootclasspathSince)) + && isAtLeastSdkLevel(this.onBootclasspathSince)) || (this.onBootclasspathBefore != null - && !UnboundedSdkLevel.isAtLeast(this.onBootclasspathBefore)); + && !isAtLeastSdkLevel(this.onBootclasspathBefore)); } } @@ -885,11 +903,9 @@ public class SystemConfig { + parser.getPositionDescription()); } else { boolean allowedMinSdk = - minDeviceSdk == null || UnboundedSdkLevel.isAtLeast( - minDeviceSdk); + minDeviceSdk == null || isAtLeastSdkLevel(minDeviceSdk); boolean allowedMaxSdk = - maxDeviceSdk == null || UnboundedSdkLevel.isAtMost( - maxDeviceSdk); + maxDeviceSdk == null || isAtMostSdkLevel(maxDeviceSdk); final boolean exists = new File(lfile).exists(); if (allowedMinSdk && allowedMaxSdk && exists) { String bcpSince = parser.getAttributeValue(null, diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 0e6aa3f5344b..08929b21dab4 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -640,7 +640,7 @@ <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"يُرجى التواصل مع مقدِّم خدمات إصلاح."</string> <string name="face_acquired_insufficient" msgid="6889245852748492218">"يتعذّر إنشاء نموذج الوجه. يُرجى إعادة المحاولة."</string> <string name="face_acquired_too_bright" msgid="8070756048978079164">"ساطع للغاية. تجربة مستوى سطوع أقلّ."</string> - <string name="face_acquired_too_dark" msgid="7919016380747701228">"جرِّب زيادة سطوع الشاشة."</string> + <string name="face_acquired_too_dark" msgid="7919016380747701228">"جرِّب زيادة الإضاءة."</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"يُرجى إبعاد الهاتف عنك."</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"يُرجى تقريب الهاتف منك."</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"يُرجى رفع الهاتف للأعلى."</string> @@ -648,7 +648,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"يُرجى تحريك الهاتف جهة اليسار."</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"يُرجى تحريك الهاتف جهة اليمين."</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"يُرجى النظر إلى جهازك مباشرة أكثر."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"تتعذّر رؤية وجهك. اِرفع هاتفك ليكون في مستوى العينَين."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"تتعذّر رؤية وجهك. ارفع هاتفك إلى مستوى العينَين."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"حركة أكثر من اللازم يُرجى حمل بدون حركة."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"يُرجى إعادة تسجيل وجهك."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"يتعذّر التعرّف على الوجه. يُرجى إعادة المحاولة."</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 0bc58320be0a..971d23d3e7df 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1361,8 +1361,8 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"Povezani uređaj se puni. Dodirnite za još opcija."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Otkrivena je analogna dodatna oprema za audio sadržaj"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Priključeni uređaj nije kompatibilan sa ovim telefonom. Dodirnite da biste saznali više."</string> - <string name="adb_active_notification_title" msgid="408390247354560331">"Otklanjanje grešaka sa USB-a je omogućeno"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Dodirnite da biste isključili otklanjanje grešaka sa USB-a"</string> + <string name="adb_active_notification_title" msgid="408390247354560331">"Povezano je otklanjanje grešaka sa USB-a"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Dodirnite da biste ga isključili"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Izaberite da biste onemogućili otklanjanja grešaka sa USB-a."</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Bežično otklanjanje grešaka je povezano"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Dodirnite da biste isključili bežično otklanjanje grešaka"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index bde7a8fd6166..fad957ae74d5 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1616,7 +1616,7 @@ <string name="default_audio_route_category_name" msgid="5241740395748134483">"سیستم"</string> <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"بلوتوثهای صوتی"</string> <string name="wireless_display_route_description" msgid="8297563323032966831">"صفحه نمایش بیسیم"</string> - <string name="media_route_button_content_description" msgid="2299223698196869956">"ارسال محتوا"</string> + <string name="media_route_button_content_description" msgid="2299223698196869956">"پخش محتوا"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"برقراری ارتباط با دستگاه"</string> <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"فرستادن محتوای صفحه به دستگاه"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"درحال جستجوی دستگاهها…"</string> @@ -2138,7 +2138,7 @@ <string name="resolver_work_tab" msgid="2690019516263167035">"کاری"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"نمای شخصی"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"نمای کاری"</string> - <string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"سرپرست سیستم آن را مسدود کرده است"</string> + <string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"سرپرست فناوری اطلاعات آن را مسدود کرده است"</string> <string name="resolver_cant_share_with_work_apps_explanation" msgid="9071442683080586643">"نمیتوان این محتوا را با برنامههای کاری همرسانی کرد"</string> <string name="resolver_cant_access_work_apps_explanation" msgid="1129960195389373279">"نمیتوان این محتوا را با برنامههای کاری باز کرد"</string> <string name="resolver_cant_share_with_personal_apps_explanation" msgid="6349766201904601544">"نمیتوان این محتوا را با برنامههای شخصی همرسانی کرد"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index a3abae1cd805..409d69561356 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -650,7 +650,7 @@ <string name="face_acquired_too_different" msgid="2520389515612972889">"Visage non reconnu. Réessayez."</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"Déplacez légèrement votre tête"</string> <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Regardez plus directement votre téléphone"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Regardez plus directement votre téléphone"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Centrez bien votre visage devant votre téléphone"</string> <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Regardez plus directement votre téléphone"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Retirez tout ce qui cache votre visage."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Nettoyez la partie supérieure de l\'écran, y compris la barre noire"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 06fe4040d6b9..8066104d051d 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -328,7 +328,7 @@ <string name="permgrouplab_notifications" msgid="5472972361980668884">"सूचनाएं"</string> <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचनाएं दिखाएं"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडो का कॉन्टेंट वापस पाएं"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"उस विंडो की सामग्री की जाँच करें, जिसका आप इस्तेमाल कर रहे हैं."</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"विंडो पर नज़र आ रहे कॉन्टेंट की जांच करें."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"छूकर, किसी चीज़ से जुड़ी जानकारी सुनने की सुविधा चालू करें"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"जिन चीज़ों पर आप टैप करेंगे उन्हें ज़ोर से बोला जाएगा और स्क्रीन को जेस्चर के ज़रिए एक्सप्लोर किया जा सकेगा."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"अपने लिखे हुए लेख पर गौर करें"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 9f5ca75010cb..0431b50daaea 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -649,9 +649,9 @@ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Ripeti l\'acquisizione del volto."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"Impossibile riconoscere il volto. Riprova."</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"Cambia leggermente la posizione della testa"</string> - <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Guarda più direttamente verso il telefono"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Guarda più direttamente verso il telefono"</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Guarda più direttamente verso il telefono"</string> + <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Guarda dritto nel telefono"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Guarda dritto nel telefono"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Guarda dritto nel telefono"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Rimuovi tutto ciò che ti nasconde il viso."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Pulisci la parte superiore dello schermo, inclusa la barra nera"</string> <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Il tuo volto deve essere visibile per intero"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 4619ff502079..8fb5b31988ee 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1361,10 +1361,10 @@ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"アナログのオーディオ アクセサリを検出"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"接続したデバイスはこのスマートフォンと互換性がありません。タップすると、詳細を確認できます。"</string> <string name="adb_active_notification_title" msgid="408390247354560331">"USB デバッグが接続されました"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"USB デバッグを無効にするにはここをタップしてください"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"無効にするにはここをタップしてください"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB デバッグを無効にする場合に選択します。"</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ワイヤレス デバッグが接続されました"</string> - <string name="adbwifi_active_notification_message" msgid="930987922852867972">"ワイヤレス デバッグをUSB デバッグを無効にするにはここをタップしてください"</string> + <string name="adbwifi_active_notification_message" msgid="930987922852867972">"無効にするにはここをタップしてください"</string> <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ワイヤレス デバッグを無効にするには選択します。"</string> <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"テストハーネス モード有効"</string> <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"出荷時設定にリセットしてテストハーネス モードを無効にしてください。"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 685ab543a8ec..efca6624d32e 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -636,9 +636,9 @@ <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Тейлөө кызматына кайрылыңыз."</string> <string name="face_acquired_insufficient" msgid="6889245852748492218">"Жүзүңүздүн үлгүсү түзүлгөн жок. Кайталаңыз."</string> <string name="face_acquired_too_bright" msgid="8070756048978079164">"Өтө жарык. Жарыктыкты азайтып көрүңүз."</string> - <string name="face_acquired_too_dark" msgid="7919016380747701228">"Жарыгыраак жерден тартып көрүңүз"</string> + <string name="face_acquired_too_dark" msgid="7919016380747701228">"Жарыгыраак жерге туруңуз"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"Телефонду алысыраак жылдырыңыз"</string> - <string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонду жакыныраак жылдырыңыз"</string> + <string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонду жакындатыңыз"</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"Телефонду жогору жылдырыңыз"</string> <string name="face_acquired_too_low" msgid="4075391872960840081">"Телефонду ылдый жылдырыңыз"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"Телефонду солго жылдырыңыз"</string> @@ -654,11 +654,11 @@ <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Телефонуңузду караңыз"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Жүзүңүздү жашырып турган нерселерди алып салыңыз."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Экраныңыздын жогору жагын, анын ичинде тилкени да тазалаңыз"</string> - <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Жүзүңүз толугу менен көрүнүшү керек"</string> - <string name="face_acquired_mouth_covering_detected" msgid="615991670821926847">"Жүзүңүз толугу менен көрүнүшү керек"</string> + <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Жүзүңүз толук көрүнүшү керек"</string> + <string name="face_acquired_mouth_covering_detected" msgid="615991670821926847">"Жүзүңүз толук көрүнүшү керек"</string> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Жүзүңүздүн үлгүсү түзүлгөн жок. Кайталаңыз."</string> - <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Кара көз айнек аныкталды. Жүзүңүз толугу менен көрүнүшү керек."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Жүзүңүз жабылып турат. Жүзүңүз толугу менен көрүнүшү керек."</string> + <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Кара көз айнек кийгенге болбойт. Жүзүңүз толук көрүнүшү керек."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Жүзүңүз жабылып калды. Ал толук көрүнүшү керек."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Жүз ырасталбай жатат. Аппараттык камсыздоо жеткиликсиз."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index d70fc9b88cfe..d8c7f9e3d462 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2137,7 +2137,7 @@ <string name="resolver_personal_tab" msgid="2051260504014442073">"ବ୍ୟକ୍ତିଗତ"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"ୱାର୍କ"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"ବ୍ୟକ୍ତିଗତ ଭ୍ୟୁ"</string> - <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"କାର୍ଯ୍ୟସ୍ଥଳୀ ସମ୍ବନ୍ଧିତ ଭ୍ୟୁ"</string> + <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"ୱାର୍କ ଭ୍ୟୁ"</string> <string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ଦ୍ୱାରା ବ୍ଲକ୍ କରାଯାଇଛି"</string> <string name="resolver_cant_share_with_work_apps_explanation" msgid="9071442683080586643">"ଏହି ବିଷୟବସ୍ତୁ ୱାର୍କ ଆପଗୁଡ଼ିକରେ ସେୟାର୍ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="resolver_cant_access_work_apps_explanation" msgid="1129960195389373279">"ଏହି ବିଷୟବସ୍ତୁ ୱାର୍କ ଆପଗୁଡ଼ିକରେ ଖୋଲାଯାଇପାରିବ ନାହିଁ"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index f606eb222297..e8353dd363ab 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1361,8 +1361,8 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"Se încarcă dispozitivul conectat. Atingeți pentru mai multe opțiuni."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"S-a detectat un accesoriu audio analogic"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Dispozitivul atașat nu este compatibil cu acest telefon. Atingeți pentru a afla mai multe."</string> - <string name="adb_active_notification_title" msgid="408390247354560331">"Remedierea erorilor prin USB este conectată"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Atingeți pentru a dezactiva remedierea erorilor prin USB."</string> + <string name="adb_active_notification_title" msgid="408390247354560331">"Remedierea erorilor prin USB conectată"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Atingeți pentru a dezactiva."</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Selectați pentru a dezactiva remedierea erorilor prin USB."</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Remedierea erorilor wireless este activă"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Atingeți pentru a dezactiva remedierea erorilor wireless"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 072b1cfc691c..8be5450d62bb 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -638,28 +638,28 @@ <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Обратитесь в сервисный центр."</string> <string name="face_acquired_insufficient" msgid="6889245852748492218">"Невозможно создать модель лица. Повторите попытку."</string> <string name="face_acquired_too_bright" msgid="8070756048978079164">"Слишком светло. Сделайте освещение менее ярким."</string> - <string name="face_acquired_too_dark" msgid="7919016380747701228">"Сделайте освещение ярче."</string> + <string name="face_acquired_too_dark" msgid="7919016380747701228">"Сделайте освещение ярче"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"Переместите телефон дальше от лица."</string> - <string name="face_acquired_too_far" msgid="2922278214231064859">"Переместите телефон ближе к лицу."</string> + <string name="face_acquired_too_far" msgid="2922278214231064859">"Переместите телефон ближе к лицу"</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"Переместите телефон выше."</string> <string name="face_acquired_too_low" msgid="4075391872960840081">"Переместите телефон ниже."</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"Переместите телефон левее."</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Переместите телефон правее."</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Смотрите прямо на устройство."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Вашего лица не видно. Держите телефон на уровне глаз."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Вашего лица не видно. Держите телефон на уровне глаз"</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Не перемещайте устройство. Держите его неподвижно."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Повторите попытку."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"Не удалось распознать лицо. Повторите попытку."</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"Немного измените положение головы."</string> - <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Смотрите прямо на телефон."</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Смотрите прямо на телефон."</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Смотрите прямо на телефон."</string> + <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Смотрите прямо на телефон"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Смотрите прямо на телефон"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Смотрите прямо на телефон"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Ваше лицо плохо видно."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Протрите верхнюю часть экрана (в том числе черную панель)."</string> - <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Лицо должно быть полностью видно."</string> - <string name="face_acquired_mouth_covering_detected" msgid="615991670821926847">"Лицо должно быть полностью видно."</string> + <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Лицо должно быть полностью видно"</string> + <string name="face_acquired_mouth_covering_detected" msgid="615991670821926847">"Лицо должно быть полностью видно"</string> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Невозможно создать модель лица. Повторите попытку."</string> - <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Распознаны темные очки. Лицо должно быть полностью видно."</string> + <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Обнаружены темные очки. Лицо должно быть полностью видно"</string> <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Часть лица закрыта. Оно должно быть полностью видно."</string> <string-array name="face_acquired_vendor"> </string-array> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 1847f2d87058..4e533527dc60 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1307,7 +1307,7 @@ <string-array name="network_switch_type_name"> <item msgid="2255670471736226365">"të dhënat celulare"</item> <item msgid="5520925862115353992">"Wi-Fi"</item> - <item msgid="1055487873974272842">"Bluetooth"</item> + <item msgid="1055487873974272842">"Bluetooth-i"</item> <item msgid="1616528372438698248">"Eternet"</item> <item msgid="9177085807664964627">"VPN"</item> </string-array> @@ -1461,7 +1461,7 @@ <string name="ime_action_go" msgid="5536744546326495436">"Shko"</string> <string name="ime_action_search" msgid="4501435960587287668">"Kërko"</string> <string name="ime_action_send" msgid="8456843745664334138">"Dërgo"</string> - <string name="ime_action_next" msgid="4169702997635728543">"Përpara"</string> + <string name="ime_action_next" msgid="4169702997635728543">"Para"</string> <string name="ime_action_done" msgid="6299921014822891569">"U krye"</string> <string name="ime_action_previous" msgid="6548799326860401611">"I mëparshëm"</string> <string name="ime_action_default" msgid="8265027027659800121">"Ekzekuto"</string> @@ -1502,7 +1502,7 @@ <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacioni i drejtimit të makinës është në ekzekutim"</string> <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Trokit për të dalë nga aplikacioni i drejtimit të makinës."</string> <string name="back_button_label" msgid="4078224038025043387">"Prapa"</string> - <string name="next_button_label" msgid="6040209156399907780">"Përpara"</string> + <string name="next_button_label" msgid="6040209156399907780">"Para"</string> <string name="skip_button_label" msgid="3566599811326688389">"Kapërce"</string> <string name="no_matches" msgid="6472699895759164599">"Asnjë përputhje"</string> <string name="find_on_page" msgid="5400537367077438198">"Gjej brenda faqes"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 2fb6ac120d12..8c6689ac9c7e 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1361,8 +1361,8 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"Повезани уређај се пуни. Додирните за још опција."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Откривена је аналогна додатна опрема за аудио садржај"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Прикључени уређај није компатибилан са овим телефоном. Додирните да бисте сазнали више."</string> - <string name="adb_active_notification_title" msgid="408390247354560331">"Отклањање грешака са USB-а је омогућено"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Додирните да бисте искључили отклањање грешака са USB-а"</string> + <string name="adb_active_notification_title" msgid="408390247354560331">"Повезано је отклањање грешака са USB-а"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Додирните да бисте га искључили"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Бежично отклањање грешака је повезано"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Додирните да бисте искључили бежично отклањање грешака"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 6d3aedd43655..27436e14325e 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -649,9 +649,9 @@ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"โปรดลงทะเบียนใบหน้าอีกครั้ง"</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"ไม่รู้จักใบหน้า โปรดลองอีกครั้ง"</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"เปลี่ยนตำแหน่งของศีรษะเล็กน้อย"</string> - <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"โปรดมองตรงไปที่โทรศัพท์"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"โปรดมองตรงไปที่โทรศัพท์"</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"โปรดมองตรงไปที่โทรศัพท์"</string> + <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"โปรดมองตรงมาที่โทรศัพท์"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"โปรดมองตรงมาที่โทรศัพท์"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"โปรดมองตรงมาที่โทรศัพท์"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"เอาสิ่งที่ปิดบังใบหน้าออก"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ทำความสะอาดด้านบนของหน้าจอ รวมถึงแถบสีดำ"</string> <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"ต้องมองเห็นใบหน้าของคุณทั้งหมด"</string> @@ -1714,7 +1714,7 @@ <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"หากต้องการสลับระหว่างฟีเจอร์ต่างๆ ให้ใช้ 3 นิ้วเลื่อนขึ้นแล้วค้างไว้"</string> <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"การขยาย"</string> <string name="user_switched" msgid="7249833311585228097">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string> - <string name="user_switching_message" msgid="1912993630661332336">"กำลังเปลี่ยนเป็น <xliff:g id="NAME">%1$s</xliff:g>…"</string> + <string name="user_switching_message" msgid="1912993630661332336">"กำลังเปลี่ยนเป็น<xliff:g id="NAME">%1$s</xliff:g>…"</string> <string name="user_logging_out_message" msgid="7216437629179710359">"กำลังออกจากระบบ <xliff:g id="NAME">%1$s</xliff:g>…"</string> <string name="owner_name" msgid="8713560351570795743">"เจ้าของ"</string> <string name="guest_name" msgid="8502103277839834324">"ผู้ใช้ชั่วคราว"</string> diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index fe296c704095..5fd9dc0ca798 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -30,4 +30,5 @@ lockscreen, setting this to true should come with customized drawables. --> <bool name="use_lock_pattern_drawable">false</bool> <bool name="resolver_landscape_phone">true</bool> + <bool name="system_server_plays_face_haptics">true</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b5056941c05c..0817102f88f9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4809,4 +4809,6 @@ <java-symbol type="id" name="language_picker_header" /> <java-symbol type="dimen" name="status_bar_height_default" /> + + <java-symbol type="bool" name="system_server_plays_face_haptics" /> </resources> diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index c504f0cf2d94..dcb1835204dd 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -214,19 +214,23 @@ public class InsetsControllerTest { } @Test - public void testFrameDoesntMatchDisplay() { - mController.onFrameChanged(new Rect(0, 0, 100, 100)); - mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); - InsetsSourceControl control = - new InsetsSourceControl( - ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0)); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + public void testFrameDoesntOverlapWithInsets() { WindowInsetsAnimationControlListener controlListener = mock(WindowInsetsAnimationControlListener.class); - mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(), - new CancellationSignal(), controlListener); - mController.addOnControllableInsetsChangedListener( - (controller, typeMask) -> assertEquals(0, typeMask)); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + // The frame doesn't overlap with status bar. + mController.onFrameChanged(new Rect(0, 10, 100, 100)); + + InsetsSourceControl control = + new InsetsSourceControl( + ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0)); + mController.onControlsChanged(new InsetsSourceControl[]{control}); + mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, + new LinearInterpolator(), + new CancellationSignal(), controlListener); + mController.addOnControllableInsetsChangedListener( + (controller, typeMask) -> assertEquals(0, typeMask)); + }); verify(controlListener).onCancelled(null); verify(controlListener, never()).onReady(any(), anyInt()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index c29d18872ad0..c829bc316246 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -33,6 +33,7 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; import android.media.MediaRoute2Info; @@ -268,6 +269,20 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { } /** + * Checks if route's volume is fixed, if true, we should disable volume control for the device. + * + * @return route for this device is fixed. + */ + @SuppressLint("NewApi") + public boolean isVolumeFixed() { + if (mRouteInfo == null) { + Log.w(TAG, "RouteInfo is empty, regarded as volume fixed."); + return true; + } + return mRouteInfo.getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED; + } + + /** * Transfer MediaDevice for media * * @return result of transfer media 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 f934b1f3ab99..5b47ae525919 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -59,31 +59,31 @@ class ActivityLaunchAnimator( companion object { /** The timings when animating a View into an app. */ @JvmField - val TIMINGS = LaunchAnimator.Timings( - totalDuration = 500L, - contentBeforeFadeOutDelay = 0L, - contentBeforeFadeOutDuration = 150L, - contentAfterFadeInDelay = 150L, - contentAfterFadeInDuration = 183L - ) + val TIMINGS = + LaunchAnimator.Timings( + totalDuration = 500L, + contentBeforeFadeOutDelay = 0L, + contentBeforeFadeOutDuration = 150L, + contentAfterFadeInDelay = 150L, + contentAfterFadeInDuration = 183L + ) /** * The timings when animating a Dialog into an app. We need to wait at least 200ms before * showing the app (which is under the dialog window) so that the dialog window dim is fully * faded out, to avoid flicker. */ - val DIALOG_TIMINGS = TIMINGS.copy( - contentBeforeFadeOutDuration = 200L, - contentAfterFadeInDelay = 200L - ) + val DIALOG_TIMINGS = + TIMINGS.copy(contentBeforeFadeOutDuration = 200L, contentAfterFadeInDelay = 200L) /** The interpolators when animating a View or a dialog into an app. */ - val INTERPOLATORS = LaunchAnimator.Interpolators( - positionInterpolator = Interpolators.EMPHASIZED, - positionXInterpolator = createPositionXInterpolator(), - contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN, - contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f) - ) + val INTERPOLATORS = + LaunchAnimator.Interpolators( + positionInterpolator = Interpolators.EMPHASIZED, + positionXInterpolator = createPositionXInterpolator(), + contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN, + contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f) + ) /** Durations & interpolators for the navigation bar fading in & out. */ private const val ANIMATION_DURATION_NAV_FADE_IN = 266L @@ -98,11 +98,12 @@ class ActivityLaunchAnimator( private const val LAUNCH_TIMEOUT = 1000L private fun createPositionXInterpolator(): Interpolator { - val path = Path().apply { - moveTo(0f, 0f) - cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f) - cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f) - } + val path = + Path().apply { + moveTo(0f, 0f) + cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f) + cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f) + } return PathInterpolator(path) } } @@ -150,29 +151,37 @@ class ActivityLaunchAnimator( return } - val callback = this.callback ?: throw IllegalStateException( - "ActivityLaunchAnimator.callback must be set before using this animator") + val callback = + this.callback + ?: throw IllegalStateException( + "ActivityLaunchAnimator.callback must be set before using this animator" + ) val runner = Runner(controller) val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the // keyguard with the animation - val animationAdapter = if (!hideKeyguardWithAnimation) { - RemoteAnimationAdapter( - runner, - TIMINGS.totalDuration, - TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */ - ) - } else { - null - } + val animationAdapter = + if (!hideKeyguardWithAnimation) { + RemoteAnimationAdapter( + runner, + TIMINGS.totalDuration, + TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */ + ) + } else { + null + } // Register the remote animation for the given package to also animate trampoline // activity launches. if (packageName != null && animationAdapter != null) { try { - ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart( - packageName, animationAdapter, null /* launchCookie */) + ActivityTaskManager.getService() + .registerRemoteAnimationForNextActivityStart( + packageName, + animationAdapter, + null /* launchCookie */ + ) } catch (e: RemoteException) { Log.w(TAG, "Unable to register the remote animation", e) } @@ -184,12 +193,15 @@ class ActivityLaunchAnimator( // keyguard. val willAnimate = 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") + launchResult == ActivityManager.START_SUCCESS || + (launchResult == ActivityManager.START_DELIVERED_TO_TOP && + hideKeyguardWithAnimation) + + Log.i( + TAG, + "launchResult=$launchResult willAnimate=$willAnimate " + + "hideKeyguardWithAnimation=$hideKeyguardWithAnimation" + ) controller.callOnIntentStartedOnMainThread(willAnimate) // If we expect an animation, post a timeout to cancel it in case the remote animation is @@ -206,9 +218,7 @@ class ActivityLaunchAnimator( private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) { if (Looper.myLooper() != Looper.getMainLooper()) { - this.launchContainer.context.mainExecutor.execute { - this.onIntentStarted(willAnimate) - } + this.launchContainer.context.mainExecutor.execute { this.onIntentStarted(willAnimate) } } else { this.onIntentStarted(willAnimate) } @@ -246,8 +256,7 @@ class ActivityLaunchAnimator( } /** Create a new animation [Runner] controlled by [controller]. */ - @VisibleForTesting - fun createRunner(controller: Controller): Runner = Runner(controller) + @VisibleForTesting fun createRunner(controller: Controller): Runner = Runner(controller) interface PendingIntentStarter { /** @@ -271,19 +280,16 @@ class ActivityLaunchAnimator( interface Listener { /** Called when an activity launch animation started. */ - @JvmDefault - fun onLaunchAnimationStart() {} + @JvmDefault fun onLaunchAnimationStart() {} /** * Called when an activity launch animation is finished. This will be called if and only if * [onLaunchAnimationStart] was called earlier. */ - @JvmDefault - fun onLaunchAnimationEnd() {} + @JvmDefault fun onLaunchAnimationEnd() {} /** Called when an activity launch animation made progress. */ - @JvmDefault - fun onLaunchAnimationProgress(linearProgress: Float) {} + @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {} } /** @@ -327,6 +333,17 @@ class ActivityLaunchAnimator( get() = false /** + * Whether the expandable controller by this [Controller] is below the launching window that + * is going to be animated. + * + * This should be `false` when launching an app from the shade or status bar, given that + * they are drawn above all apps. This is usually `true` when using this launcher in a + * normal app or a launcher, that are drawn below the animating activity/window. + */ + val isBelowAnimatingWindow: Boolean + get() = false + + /** * The intent was started. If [willAnimate] is false, nothing else will happen and the * animation will not be started. */ @@ -394,9 +411,7 @@ class ActivityLaunchAnimator( return } - context.mainExecutor.execute { - startAnimation(apps, nonApps, iCallback) - } + context.mainExecutor.execute { startAnimation(apps, nonApps, iCallback) } } private fun startAnimation( @@ -408,9 +423,7 @@ class ActivityLaunchAnimator( Log.d(TAG, "Remote animation started") } - val window = apps?.firstOrNull { - it.mode == RemoteAnimationTarget.MODE_OPENING - } + val window = apps?.firstOrNull { it.mode == RemoteAnimationTarget.MODE_OPENING } if (window == null) { Log.i(TAG, "Aborting the animation as no window is opening") @@ -420,80 +433,96 @@ class ActivityLaunchAnimator( return } - val navigationBar = nonApps?.firstOrNull { - it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR - } + val navigationBar = + nonApps?.firstOrNull { + it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR + } val windowBounds = window.screenSpaceBounds - val endState = LaunchAnimator.State( - top = windowBounds.top, - bottom = windowBounds.bottom, - left = windowBounds.left, - right = windowBounds.right - ) + val endState = + LaunchAnimator.State( + top = windowBounds.top, + bottom = windowBounds.bottom, + left = windowBounds.left, + right = windowBounds.right + ) val callback = this@ActivityLaunchAnimator.callback!! - val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) } - ?: window.backgroundColor + val windowBackgroundColor = + window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor // Make sure we use the modified timings when animating a dialog into an app. - val launchAnimator = if (controller.isDialogLaunch) { - dialogToAppAnimator - } else { - launchAnimator - } + val launchAnimator = + if (controller.isDialogLaunch) { + dialogToAppAnimator + } else { + launchAnimator + } // 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. - ScreenDecorationsUtils.getWindowCornerRadius(context) - } else { - // This usually means we are in split screen mode, so 2 out of 4 corners will have - // a radius of 0. - 0f - } + val endRadius = + if (isExpandingFullyAbove) { + // Most of the time, expanding fully above the root view means expanding in full + // screen. + ScreenDecorationsUtils.getWindowCornerRadius(context) + } else { + // This usually means we are in split screen mode, so 2 out of 4 corners will + // have + // a radius of 0. + 0f + } endState.topCornerRadius = endRadius endState.bottomCornerRadius = endRadius // 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) { - listeners.forEach { it.onLaunchAnimationStart() } - delegate.onLaunchAnimationStart(isExpandingFullyAbove) - } - - override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { - listeners.forEach { it.onLaunchAnimationEnd() } - iCallback?.invoke() - delegate.onLaunchAnimationEnd(isExpandingFullyAbove) - } + val controller = + object : Controller by delegate { + override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { + listeners.forEach { it.onLaunchAnimationStart() } + delegate.onLaunchAnimationStart(isExpandingFullyAbove) + } - override fun onLaunchAnimationProgress( - state: LaunchAnimator.State, - progress: Float, - linearProgress: Float - ) { - // Apply the state to the window only if it is visible, i.e. when the expanding - // view is *not* visible. - if (!state.visible) { - applyStateToWindow(window, state) + override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { + listeners.forEach { it.onLaunchAnimationEnd() } + iCallback?.invoke() + delegate.onLaunchAnimationEnd(isExpandingFullyAbove) } - navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } - listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } - delegate.onLaunchAnimationProgress(state, progress, linearProgress) + override fun onLaunchAnimationProgress( + state: LaunchAnimator.State, + progress: Float, + linearProgress: Float + ) { + // Apply the state to the window only if it is visible, i.e. when the + // expanding view is *not* visible. + if (!state.visible) { + applyStateToWindow(window, state, linearProgress) + } + navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } + + listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } + delegate.onLaunchAnimationProgress(state, progress, linearProgress) + } } - } - animation = launchAnimator.startAnimation( - controller, endState, windowBackgroundColor, drawHole = true) + animation = + launchAnimator.startAnimation( + controller, + endState, + windowBackgroundColor, + fadeOutWindowBackgroundLayer = !controller.isBelowAnimatingWindow, + drawHole = !controller.isBelowAnimatingWindow, + ) } - private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) { + private fun applyStateToWindow( + window: RemoteAnimationTarget, + state: LaunchAnimator.State, + linearProgress: Float, + ) { if (transactionApplierView.viewRootImpl == null) { // If the view root we synchronize with was detached, don't apply any transaction // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw). @@ -535,19 +564,38 @@ class ActivityLaunchAnimator( windowCropF.bottom.roundToInt() ) + // The alpha of the opening window. If it opens above the expandable, then it should + // fade in progressively. Otherwise, it should be fully opaque and will be progressively + // revealed as the window background color layer above the window fades out. + val alpha = + if (controller.isBelowAnimatingWindow) { + val windowProgress = + LaunchAnimator.getProgress( + TIMINGS, + linearProgress, + TIMINGS.contentAfterFadeInDelay, + TIMINGS.contentAfterFadeInDuration + ) + + INTERPOLATORS.contentAfterFadeInInterpolator.getInterpolation(windowProgress) + } else { + 1f + } + // The scale will also be applied to the corner radius, so we divide by the scale to // keep the original radius. We use the max of (topCornerRadius, bottomCornerRadius) to // make sure that the window does not draw itself behind the expanding view. This is // especially important for lock screen animations, where the window is not clipped by // the shade. val cornerRadius = maxOf(state.topCornerRadius, state.bottomCornerRadius) / scale - val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash) - .withAlpha(1f) - .withMatrix(matrix) - .withWindowCrop(windowCrop) - .withCornerRadius(cornerRadius) - .withVisibility(true) - .build() + val params = + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash) + .withAlpha(alpha) + .withMatrix(matrix) + .withWindowCrop(windowCrop) + .withCornerRadius(cornerRadius) + .withVisibility(true) + .build() transactionApplier.scheduleApply(params) } @@ -563,14 +611,21 @@ class ActivityLaunchAnimator( return } - val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress, - ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT) + val fadeInProgress = + LaunchAnimator.getProgress( + TIMINGS, + linearProgress, + ANIMATION_DELAY_NAV_FADE_IN, + ANIMATION_DURATION_NAV_FADE_OUT + ) val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash) if (fadeInProgress > 0) { matrix.reset() matrix.setTranslate( - 0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat()) + 0f, + (state.top - navigationBar.sourceContainerBounds.top).toFloat() + ) windowCrop.set(state.left, 0, state.right, state.height) params .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress)) @@ -578,8 +633,13 @@ class ActivityLaunchAnimator( .withWindowCrop(windowCrop) .withVisibility(true) } else { - val fadeOutProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress, 0, - ANIMATION_DURATION_NAV_FADE_OUT) + val fadeOutProgress = + LaunchAnimator.getProgress( + TIMINGS, + linearProgress, + 0, + ANIMATION_DURATION_NAV_FADE_OUT + ) params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress)) } 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 258ca6bdf79b..b879ba0b1ece 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt @@ -23,4 +23,4 @@ package com.android.systemui.animation */ open class DelegateLaunchAnimatorController( protected val delegate: ActivityLaunchAnimator.Controller -) : ActivityLaunchAnimator.Controller by delegate
\ No newline at end of file +) : ActivityLaunchAnimator.Controller by delegate diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 48231e30ba6d..cb16d7c3471f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -51,7 +51,9 @@ private const val TAG = "DialogLaunchAnimator" * @see showFromDialog * @see createActivityLaunchController */ -class DialogLaunchAnimator @JvmOverloads constructor( +class DialogLaunchAnimator +@JvmOverloads +constructor( private val dreamManager: IDreamManager, private val interactionJankMonitor: InteractionJankMonitor, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), @@ -62,9 +64,10 @@ class DialogLaunchAnimator @JvmOverloads constructor( // We use the same interpolator for X and Y axis to make sure the dialog does not move out // of the screen bounds during the animation. - private val INTERPOLATORS = ActivityLaunchAnimator.INTERPOLATORS.copy( - positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator - ) + private val INTERPOLATORS = + ActivityLaunchAnimator.INTERPOLATORS.copy( + positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator + ) private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.tag_launch_animation_running } @@ -105,8 +108,10 @@ class DialogLaunchAnimator @JvmOverloads constructor( // If the view we are launching from belongs to another dialog, then this means the caller // intent is to launch a dialog from another dialog. - val animatedParent = openedDialogs - .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl } + val animatedParent = + openedDialogs.firstOrNull { + it.dialog.window.decorView.viewRootImpl == view.viewRootImpl + } val animateFrom = animatedParent?.dialogContentWithBackground ?: view // Make sure we don't run the launch animation from the same view twice at the same time. @@ -118,18 +123,19 @@ class DialogLaunchAnimator @JvmOverloads constructor( animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) - val animatedDialog = AnimatedDialog( - launchAnimator, - dreamManager, - interactionJankMonitor, - animateFrom, - onDialogDismissed = { openedDialogs.remove(it) }, - dialog = dialog, - animateBackgroundBoundsChange, - animatedParent, - isForTesting, - cuj - ) + val animatedDialog = + AnimatedDialog( + launchAnimator, + dreamManager, + interactionJankMonitor, + animateFrom, + onDialogDismissed = { openedDialogs.remove(it) }, + dialog = dialog, + animateBackgroundBoundsChange, + animatedParent, + isForTesting, + cuj + ) openedDialogs.add(animatedDialog) animatedDialog.start() @@ -146,13 +152,12 @@ class DialogLaunchAnimator @JvmOverloads constructor( animateFrom: Dialog, animateBackgroundBoundsChange: Boolean = false ) { - val view = openedDialogs - .firstOrNull { it.dialog == animateFrom } - ?.dialogContentWithBackground - ?: throw IllegalStateException( - "The animateFrom dialog was not animated using " + - "DialogLaunchAnimator.showFrom(View|Dialog)" - ) + val view = + openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground + ?: throw IllegalStateException( + "The animateFrom dialog was not animated using " + + "DialogLaunchAnimator.showFrom(View|Dialog)" + ) showFromView(dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange) } @@ -175,9 +180,11 @@ class DialogLaunchAnimator @JvmOverloads constructor( view: View, cujType: Int? = null ): ActivityLaunchAnimator.Controller? { - val animatedDialog = openedDialogs - .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl } - ?: return null + val animatedDialog = + openedDialogs.firstOrNull { + it.dialog.window.decorView.viewRootImpl == view.viewRootImpl + } + ?: return null // At this point, we know that the intent of the caller is to dismiss the dialog to show // an app, so we disable the exit animation into the touch surface because we will never @@ -240,7 +247,7 @@ class DialogLaunchAnimator @JvmOverloads constructor( } private fun disableDialogDismiss() { - dialog.setDismissOverride { /* Do nothing */ } + dialog.setDismissOverride { /* Do nothing */} } private fun enableDialogDismiss() { @@ -257,9 +264,9 @@ class DialogLaunchAnimator @JvmOverloads constructor( * 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. + * 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. */ @@ -295,8 +302,8 @@ private class AnimatedDialog( var touchSurface: View, /** - * A callback that will be called with this [AnimatedDialog] after the dialog was - * dismissed and the exit animation is done. + * A callback that will be called with this [AnimatedDialog] after the dialog was dismissed and + * the exit animation is done. */ private val onDialogDismissed: (AnimatedDialog) -> Unit, @@ -333,9 +340,7 @@ private class AnimatedDialog( */ var dialogContentWithBackground: ViewGroup? = null - /** - * The background color of [dialog], taking into consideration its window background color. - */ + /** The background color of [dialog], taking into consideration its window background color. */ private var originalDialogBackgroundColor = Color.BLACK /** @@ -353,11 +358,12 @@ private class AnimatedDialog( private var isOriginalDialogViewLaidOut = false /** A layout listener to animate the dialog height change. */ - private val backgroundLayoutListener = if (animateBackgroundBoundsChange) { - AnimatedBoundsLayoutListener() - } else { - null - } + private val backgroundLayoutListener = + if (animateBackgroundBoundsChange) { + AnimatedBoundsLayoutListener() + } else { + null + } /* * A layout listener in case the dialog (window) size changes (for instance because of a @@ -381,100 +387,117 @@ private class AnimatedDialog( val window = dialog.window!! val isWindowFullScreen = window.attributes.width == MATCH_PARENT && window.attributes.height == MATCH_PARENT - val dialogContentWithBackground = if (isWindowFullScreen) { - // If the dialog window is already fullscreen, then we look for the first ViewGroup that - // has a background (and is not the DecorView, which always has a background) and - // animate towards that ViewGroup given that this is probably what represents the actual - // dialog view. - var viewGroupWithBackground: ViewGroup? = null - for (i in 0 until decorView.childCount) { - viewGroupWithBackground = findFirstViewGroupWithBackground(decorView.getChildAt(i)) - if (viewGroupWithBackground != null) { - break + val dialogContentWithBackground = + if (isWindowFullScreen) { + // If the dialog window is already fullscreen, then we look for the first ViewGroup + // that has a background (and is not the DecorView, which always has a background) + // and animate towards that ViewGroup given that this is probably what represents + // the actual dialog view. + var viewGroupWithBackground: ViewGroup? = null + for (i in 0 until decorView.childCount) { + viewGroupWithBackground = + findFirstViewGroupWithBackground(decorView.getChildAt(i)) + if (viewGroupWithBackground != null) { + break + } } - } - // Animate that view with the background. Throw if we didn't find one, because otherwise - // it's not clear what we should animate. - viewGroupWithBackground - ?: throw IllegalStateException("Unable to find ViewGroup with background") - } else { - // We will make the dialog window (and therefore its DecorView) fullscreen to make it - // possible to animate outside its bounds. - // - // Before that, we add a new View as a child of the DecorView with the same size and - // gravity as that DecorView, then we add all original children of the DecorView to that - // new View. Finally we remove the background of the DecorView and add it to the new - // View, then we make the DecorView fullscreen. This new View now acts as a fake (non - // fullscreen) window. - // - // On top of that, we also add a fullscreen transparent background between the DecorView - // and the view that we added so that we can dismiss the dialog when this view is - // clicked. This is necessary because DecorView overrides onTouchEvent and therefore we - // can't set the click listener directly on the (now fullscreen) DecorView. - val fullscreenTransparentBackground = FrameLayout(dialog.context) - decorView.addView( - fullscreenTransparentBackground, - 0 /* index */, - FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - ) - - val dialogContentWithBackground = FrameLayout(dialog.context) - dialogContentWithBackground.background = decorView.background - - // Make the window background transparent. Note that setting the window (or DecorView) - // background drawable to null leads to issues with background color (not being - // transparent) or with insets that are not refreshed. Therefore we need to set it to - // something not null, hence we are using android.R.color.transparent here. - window.setBackgroundDrawableResource(android.R.color.transparent) - - // Close the dialog when clicking outside of it. - fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() } - dialogContentWithBackground.isClickable = true - - // Make sure the transparent and dialog backgrounds are not focusable by accessibility - // features. - fullscreenTransparentBackground.importantForAccessibility = - View.IMPORTANT_FOR_ACCESSIBILITY_NO - dialogContentWithBackground.importantForAccessibility = - View.IMPORTANT_FOR_ACCESSIBILITY_NO - - fullscreenTransparentBackground.addView( - dialogContentWithBackground, - FrameLayout.LayoutParams( - window.attributes.width, - window.attributes.height, - window.attributes.gravity + // Animate that view with the background. Throw if we didn't find one, because + // otherwise + // it's not clear what we should animate. + viewGroupWithBackground + ?: throw IllegalStateException("Unable to find ViewGroup with background") + } else { + // We will make the dialog window (and therefore its DecorView) fullscreen to make + // it possible to animate outside its bounds. + // + // Before that, we add a new View as a child of the DecorView with the same size and + // gravity as that DecorView, then we add all original children of the DecorView to + // that new View. Finally we remove the background of the DecorView and add it to + // the new View, then we make the DecorView fullscreen. This new View now acts as a + // fake (non fullscreen) window. + // + // On top of that, we also add a fullscreen transparent background between the + // DecorView and the view that we added so that we can dismiss the dialog when this + // view is clicked. This is necessary because DecorView overrides onTouchEvent and + // therefore we can't set the click listener directly on the (now fullscreen) + // DecorView. + val fullscreenTransparentBackground = FrameLayout(dialog.context) + decorView.addView( + fullscreenTransparentBackground, + 0 /* index */, + FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) ) - ) - // Move all original children of the DecorView to the new View we just added. - for (i in 1 until decorView.childCount) { - val view = decorView.getChildAt(1) - decorView.removeViewAt(1) - dialogContentWithBackground.addView(view) - } + val dialogContentWithBackground = FrameLayout(dialog.context) + dialogContentWithBackground.background = decorView.background + + // Make the window background transparent. Note that setting the window (or + // DecorView) background drawable to null leads to issues with background color (not + // being transparent) or with insets that are not refreshed. Therefore we need to + // set it to something not null, hence we are using android.R.color.transparent + // here. + window.setBackgroundDrawableResource(android.R.color.transparent) + + // Close the dialog when clicking outside of it. + fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() } + dialogContentWithBackground.isClickable = true + + // Make sure the transparent and dialog backgrounds are not focusable by + // accessibility + // features. + fullscreenTransparentBackground.importantForAccessibility = + View.IMPORTANT_FOR_ACCESSIBILITY_NO + dialogContentWithBackground.importantForAccessibility = + View.IMPORTANT_FOR_ACCESSIBILITY_NO + + fullscreenTransparentBackground.addView( + dialogContentWithBackground, + FrameLayout.LayoutParams( + window.attributes.width, + window.attributes.height, + window.attributes.gravity + ) + ) - // Make the window fullscreen and add a layout listener to ensure it stays fullscreen. - window.setLayout(MATCH_PARENT, MATCH_PARENT) - decorViewLayoutListener = View.OnLayoutChangeListener { - v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> - if (window.attributes.width != MATCH_PARENT || - window.attributes.height != MATCH_PARENT - ) { - // The dialog size changed, copy its size to dialogContentWithBackground and - // make the dialog window full screen again. - val layoutParams = dialogContentWithBackground.layoutParams - layoutParams.width = window.attributes.width - layoutParams.height = window.attributes.height - dialogContentWithBackground.layoutParams = layoutParams - window.setLayout(MATCH_PARENT, MATCH_PARENT) + // Move all original children of the DecorView to the new View we just added. + for (i in 1 until decorView.childCount) { + val view = decorView.getChildAt(1) + decorView.removeViewAt(1) + dialogContentWithBackground.addView(view) } - } - decorView.addOnLayoutChangeListener(decorViewLayoutListener) - dialogContentWithBackground - } + // Make the window fullscreen and add a layout listener to ensure it stays + // fullscreen. + window.setLayout(MATCH_PARENT, MATCH_PARENT) + decorViewLayoutListener = + View.OnLayoutChangeListener { + v, + left, + top, + right, + bottom, + oldLeft, + oldTop, + oldRight, + oldBottom -> + if ( + window.attributes.width != MATCH_PARENT || + window.attributes.height != MATCH_PARENT + ) { + // The dialog size changed, copy its size to dialogContentWithBackground + // and make the dialog window full screen again. + val layoutParams = dialogContentWithBackground.layoutParams + layoutParams.width = window.attributes.width + layoutParams.height = window.attributes.height + dialogContentWithBackground.layoutParams = layoutParams + window.setLayout(MATCH_PARENT, MATCH_PARENT) + } + } + decorView.addOnLayoutChangeListener(decorViewLayoutListener) + + dialogContentWithBackground + } this.dialogContentWithBackground = dialogContentWithBackground dialogContentWithBackground.setTag(R.id.tag_dialog_background, true) @@ -482,7 +505,8 @@ private class AnimatedDialog( originalDialogBackgroundColor = GhostedViewLaunchAnimatorController.findGradientDrawable(background) ?.color - ?.defaultColor ?: Color.BLACK + ?.defaultColor + ?: Color.BLACK // Make the background view invisible until we start the animation. We use the transition // visibility like GhostView does so that we don't mess up with the accessibility tree (see @@ -508,24 +532,26 @@ private class AnimatedDialog( } // Start the animation once the background view is properly laid out. - dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { - override fun onLayoutChange( - v: View, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int - ) { - dialogContentWithBackground.removeOnLayoutChangeListener(this) - - isOriginalDialogViewLaidOut = true - maybeStartLaunchAnimation() + dialogContentWithBackground.addOnLayoutChangeListener( + object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + dialogContentWithBackground.removeOnLayoutChangeListener(this) + + isOriginalDialogViewLaidOut = true + maybeStartLaunchAnimation() + } } - }) + ) // Disable the dim. We will enable it once we start the animation. window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) @@ -551,10 +577,12 @@ private class AnimatedDialog( // Create a ghost of the touch surface (which will make the touch surface invisible) and add // it to the host dialog. We trigger a one off synchronization to make sure that this is // done in sync between the two different windows. - synchronizeNextDraw(then = { - isTouchSurfaceGhostDrawn = true - maybeStartLaunchAnimation() - }) + synchronizeNextDraw( + then = { + isTouchSurfaceGhostDrawn = true + maybeStartLaunchAnimation() + } + ) GhostView.addGhost(touchSurface, decorView) // The ghost of the touch surface was just created, so the touch surface is currently @@ -616,14 +644,13 @@ private class AnimatedDialog( onLaunchAnimationEnd = { touchSurface.setTag(R.id.tag_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. + // We hide the touch surface when the dialog is showing. We will make this view + // visible again when dismissing the dialog. touchSurface.visibility = View.INVISIBLE isLaunching = false - // dismiss was called during the animation, dismiss again now to actually - // dismiss. + // dismiss was called during the animation, dismiss again now to actually dismiss. if (dismissRequested) { dialog.dismiss() } @@ -632,8 +659,9 @@ private class AnimatedDialog( // at the end of the launch animation, because the lauch animation already correctly // handles bounds changes. if (backgroundLayoutListener != null) { - dialogContentWithBackground!! - .addOnLayoutChangeListener(backgroundLayoutListener) + dialogContentWithBackground!!.addOnLayoutChangeListener( + backgroundLayoutListener + ) } cuj?.run { interactionJankMonitor.end(cujType) } } @@ -711,16 +739,19 @@ private class AnimatedDialog( dialogContentWithBackground.visibility = View.INVISIBLE if (backgroundLayoutListener != null) { - dialogContentWithBackground - .removeOnLayoutChangeListener(backgroundLayoutListener) + dialogContentWithBackground.removeOnLayoutChangeListener( + backgroundLayoutListener + ) } // Make sure that the removal of the ghost and making the touch surface visible is // done at the same time. - synchronizeNextDraw(then = { - onAnimationFinished(true /* instantDismiss */) - onDialogDismissed(this@AnimatedDialog) - }) + synchronizeNextDraw( + then = { + onAnimationFinished(true /* instantDismiss */) + onDialogDismissed(this@AnimatedDialog) + } + ) } ) } @@ -740,55 +771,56 @@ private class AnimatedDialog( endViewController.launchContainer = decorView val endState = endViewController.createAnimatorState() - val controller = object : LaunchAnimator.Controller { - override var launchContainer: ViewGroup - get() = startViewController.launchContainer - set(value) { - startViewController.launchContainer = value - endViewController.launchContainer = value - } + 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 createAnimatorState(): LaunchAnimator.State { + return startViewController.createAnimatorState() + } - override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { - // During launch, onLaunchAnimationStart will be used to remove the temporary touch - // surface ghost so it is important to call this before calling - // onLaunchAnimationStart on the controller (which will create its own ghost). - onLaunchAnimationStart() + override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { + // During launch, onLaunchAnimationStart will be used to remove the temporary + // touch surface ghost so it is important to call this before calling + // onLaunchAnimationStart on the controller (which will create its own ghost). + onLaunchAnimationStart() - startViewController.onLaunchAnimationStart(isExpandingFullyAbove) - endViewController.onLaunchAnimationStart(isExpandingFullyAbove) - } + startViewController.onLaunchAnimationStart(isExpandingFullyAbove) + endViewController.onLaunchAnimationStart(isExpandingFullyAbove) + } - override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { - startViewController.onLaunchAnimationEnd(isExpandingFullyAbove) - endViewController.onLaunchAnimationEnd(isExpandingFullyAbove) + override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { + startViewController.onLaunchAnimationEnd(isExpandingFullyAbove) + endViewController.onLaunchAnimationEnd(isExpandingFullyAbove) - onLaunchAnimationEnd() - } + 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) + 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) } @@ -821,7 +853,7 @@ private class AnimatedDialog( return (touchSurface.parent as? View)?.isShown ?: true } - /** A layout listener to animate the change of bounds of the dialog background. */ + /** A layout listener to animate the change of bounds of the dialog background. */ class AnimatedBoundsLayoutListener : View.OnLayoutChangeListener { companion object { private const val ANIMATION_DURATION = 500L @@ -866,32 +898,35 @@ private class AnimatedDialog( currentAnimator?.cancel() currentAnimator = null - val animator = ValueAnimator.ofFloat(0f, 1f).apply { - duration = ANIMATION_DURATION - interpolator = Interpolators.STANDARD - - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - currentAnimator = null + val animator = + ValueAnimator.ofFloat(0f, 1f).apply { + duration = ANIMATION_DURATION + interpolator = Interpolators.STANDARD + + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + currentAnimator = null + } + } + ) + + addUpdateListener { animatedValue -> + val progress = animatedValue.animatedFraction + + // Compute new bounds. + bounds.left = MathUtils.lerp(startLeft, left, progress).roundToInt() + bounds.top = MathUtils.lerp(startTop, top, progress).roundToInt() + bounds.right = MathUtils.lerp(startRight, right, progress).roundToInt() + bounds.bottom = MathUtils.lerp(startBottom, bottom, progress).roundToInt() + + // Set the new bounds. + view.left = bounds.left + view.top = bounds.top + view.right = bounds.right + view.bottom = bounds.bottom } - }) - - addUpdateListener { animatedValue -> - val progress = animatedValue.animatedFraction - - // Compute new bounds. - bounds.left = MathUtils.lerp(startLeft, left, progress).roundToInt() - bounds.top = MathUtils.lerp(startTop, top, progress).roundToInt() - bounds.right = MathUtils.lerp(startRight, right, progress).roundToInt() - bounds.bottom = MathUtils.lerp(startBottom, bottom, progress).roundToInt() - - // Set the new bounds. - view.left = bounds.left - view.top = bounds.top - view.right = bounds.right - view.bottom = bounds.bottom } - } currentAnimator = animator animator.start() 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 3f7e0f0fb527..47f448d503c6 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -105,9 +105,7 @@ open class GhostedViewLaunchAnimatorController( } // Perform a BFS to find the largest View with background. - val views = LinkedList<View>().apply { - add(view) - } + val views = LinkedList<View>().apply { add(view) } while (views.isNotEmpty()) { val v = views.removeFirst() @@ -161,10 +159,11 @@ open class GhostedViewLaunchAnimatorController( } override fun createAnimatorState(): LaunchAnimator.State { - val state = LaunchAnimator.State( - topCornerRadius = getCurrentTopCornerRadius(), - bottomCornerRadius = getCurrentBottomCornerRadius() - ) + val state = + LaunchAnimator.State( + topCornerRadius = getCurrentTopCornerRadius(), + bottomCornerRadius = getCurrentBottomCornerRadius() + ) fillGhostedViewState(state) return state } @@ -255,13 +254,14 @@ open class GhostedViewLaunchAnimatorController( launchContainer.getLocationOnScreen(launchContainerLocation) ghostViewMatrix.postScale( - scale, scale, + scale, + scale, ghostedViewState.centerX - launchContainerLocation[0], ghostedViewState.centerY - launchContainerLocation[1] ) ghostViewMatrix.postTranslate( - (leftChange + rightChange) / 2f, - (topChange + bottomChange) / 2f + (leftChange + rightChange) / 2f, + (topChange + bottomChange) / 2f ) ghostView.animationMatrix = ghostViewMatrix diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index a4c5c3025220..9668066be125 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -34,10 +34,7 @@ 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( - private val timings: Timings, - private val interpolators: Interpolators -) { +class LaunchAnimator(private val timings: Timings, private val interpolators: Interpolators) { companion object { internal const val DEBUG = false private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC) @@ -75,10 +72,10 @@ class LaunchAnimator( * with the opening window. * * This will be used to: - * - Get the associated [Context]. - * - Compute whether we are expanding fully above the launch container. - * - Get to overlay to which we initially put the window background layer, until the - * opening window is made visible (see [openingWindowSyncView]). + * - Get the associated [Context]. + * - Compute whether we are expanding fully above the launch container. + * - Get to overlay to which we initially put the window background layer, until the opening + * window is made visible (see [openingWindowSyncView]). * * 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 @@ -132,7 +129,6 @@ class LaunchAnimator( var bottom: Int = 0, var left: Int = 0, var right: Int = 0, - var topCornerRadius: Float = 0f, var bottomCornerRadius: Float = 0f ) { @@ -202,18 +198,20 @@ class LaunchAnimator( ) /** - * 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. + * Start a launch animation controlled by [controller] towards [endState]. An intermediary layer + * with [windowBackgroundColor] will fade in then (optionally) fade out above the expanding + * view, and should be the same background color as the opening (or closing) window. * - * TODO(b/184121838): Remove [drawHole] and instead make the StatusBar draw this hole instead. + * If [fadeOutWindowBackgroundLayer] is true, then this intermediary layer will fade out during + * the second half of the animation, and will have SRC blending mode (ultimately punching a hole + * in the [launch container][Controller.launchContainer]) iff [drawHole] is true. */ fun startAnimation( controller: Controller, endState: State, windowBackgroundColor: Int, - drawHole: Boolean = false + fadeOutWindowBackgroundLayer: Boolean = true, + drawHole: Boolean = false, ): Animation { val state = controller.createAnimatorState() @@ -238,8 +236,12 @@ class LaunchAnimator( val endBottomCornerRadius = endState.bottomCornerRadius fun maybeUpdateEndState() { - if (endTop != endState.top || endBottom != endState.bottom || - endLeft != endState.left || endRight != endState.right) { + if ( + endTop != endState.top || + endBottom != endState.bottom || + endLeft != endState.left || + endRight != endState.right + ) { endTop = endState.top endBottom = endState.bottom endLeft = endState.left @@ -256,10 +258,11 @@ class LaunchAnimator( // 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 - } + val windowBackgroundLayer = + GradientDrawable().apply { + setColor(windowBackgroundColor) + alpha = 0 + } // Update state. val animator = ValueAnimator.ofFloat(0f, 1f) @@ -270,38 +273,41 @@ class LaunchAnimator( // [Controller.openingWindowSyncView] once the opening app window starts to be visible. val openingWindowSyncView = controller.openingWindowSyncView val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay - val moveBackgroundLayerWhenAppIsVisible = openingWindowSyncView != null && - openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl + val moveBackgroundLayerWhenAppIsVisible = + openingWindowSyncView != null && + openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl val launchContainerOverlay = launchContainer.overlay var cancelled = false var movedBackgroundLayer = false - animator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { - if (DEBUG) { - Log.d(TAG, "Animation started") + 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) } - 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) + override fun onAnimationEnd(animation: Animator?) { + if (DEBUG) { + Log.d(TAG, "Animation ended") + } + controller.onLaunchAnimationEnd(isExpandingFullyAbove) + launchContainerOverlay.remove(windowBackgroundLayer) - if (moveBackgroundLayerWhenAppIsVisible) { - openingWindowSyncViewOverlay?.remove(windowBackgroundLayer) + if (moveBackgroundLayerWhenAppIsVisible) { + openingWindowSyncViewOverlay?.remove(windowBackgroundLayer) + } } } - }) + ) animator.addUpdateListener { animation -> if (cancelled) { @@ -333,12 +339,13 @@ class LaunchAnimator( // The expanding view can/should be hidden once it is completely covered by the opening // window. - state.visible = getProgress( - timings, - linearProgress, - timings.contentBeforeFadeOutDelay, - timings.contentBeforeFadeOutDuration - ) < 1 + state.visible = + getProgress( + timings, + linearProgress, + timings.contentBeforeFadeOutDelay, + timings.contentBeforeFadeOutDuration + ) < 1 if (moveBackgroundLayerWhenAppIsVisible && !state.visible && !movedBackgroundLayer) { // The expanding view is not visible, so the opening app is visible. If this is the @@ -352,17 +359,19 @@ class LaunchAnimator( ViewRootSync.synchronizeNextDraw(launchContainer, openingWindowSyncView, then = {}) } - val container = if (movedBackgroundLayer) { - openingWindowSyncView!! - } else { - controller.launchContainer - } + val container = + if (movedBackgroundLayer) { + openingWindowSyncView!! + } else { + controller.launchContainer + } applyStateToWindowBackgroundLayer( windowBackgroundLayer, state, linearProgress, container, + fadeOutWindowBackgroundLayer, drawHole ) controller.onLaunchAnimationProgress(state, progress, linearProgress) @@ -391,6 +400,7 @@ class LaunchAnimator( state: State, linearProgress: Float, launchContainer: View, + fadeOutWindowBackgroundLayer: Boolean, drawHole: Boolean ) { // Update position. @@ -415,23 +425,25 @@ class LaunchAnimator( // 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( - timings, - linearProgress, - timings.contentBeforeFadeOutDelay, - timings.contentBeforeFadeOutDuration - ) + val fadeInProgress = + getProgress( + timings, + linearProgress, + timings.contentBeforeFadeOutDelay, + timings.contentBeforeFadeOutDuration + ) if (fadeInProgress < 1) { val alpha = interpolators.contentBeforeFadeOutInterpolator.getInterpolation(fadeInProgress) drawable.alpha = (alpha * 0xFF).roundToInt() - } else { - val fadeOutProgress = getProgress( - timings, - linearProgress, - timings.contentAfterFadeInDelay, - timings.contentAfterFadeInDuration - ) + } else if (fadeOutWindowBackgroundLayer) { + val fadeOutProgress = + getProgress( + timings, + linearProgress, + timings.contentAfterFadeInDelay, + timings.contentAfterFadeInDuration + ) val alpha = 1 - interpolators.contentAfterFadeInInterpolator.getInterpolation(fadeOutProgress) drawable.alpha = (alpha * 0xFF).roundToInt() @@ -439,6 +451,8 @@ class LaunchAnimator( if (drawHole) { drawable.setXfermode(SRC_MODE) } + } else { + drawable.alpha = 0xFF } } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt index 80a3eb839940..7499302c06b2 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt @@ -27,4 +27,4 @@ interface LaunchableView { * [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked. */ fun setShouldBlockVisibilityChanges(block: Boolean) -}
\ No newline at end of file +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt index 47c11010c072..f9c6841f96b5 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt @@ -40,6 +40,7 @@ class RemoteTransitionAdapter { companion object { /** * Almost a copy of Transitions#setupStartState. + * * TODO: remove when there is proper cross-process transaction sync. */ @SuppressLint("NewApi") @@ -50,7 +51,8 @@ class RemoteTransitionAdapter { info: TransitionInfo, t: SurfaceControl.Transaction ) { - val isOpening = info.type == WindowManager.TRANSIT_OPEN || + val isOpening = + info.type == WindowManager.TRANSIT_OPEN || info.type == WindowManager.TRANSIT_TO_FRONT // Put animating stuff above this line and put static stuff below it. val zSplitLine = info.changes.size @@ -59,15 +61,19 @@ class RemoteTransitionAdapter { // Launcher animates leaf tasks directly, so always reparent all task leashes to root. t.reparent(leash, info.rootLeash) - t.setPosition(leash, (change.startAbsBounds.left - info.rootOffset.x).toFloat(), ( - change.startAbsBounds.top - info.rootOffset.y).toFloat()) + t.setPosition( + leash, + (change.startAbsBounds.left - info.rootOffset.x).toFloat(), + (change.startAbsBounds.top - info.rootOffset.y).toFloat() + ) t.show(leash) // Put all the OPEN/SHOW on top if (mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT) { if (isOpening) { t.setLayer(leash, zSplitLine + info.changes.size - layer) - if (change.flags - and TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT == 0) { + if ( + change.flags and TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT == 0 + ) { // if transferred, it should be left visible. t.setAlpha(leash, 0f) } @@ -75,8 +81,9 @@ class RemoteTransitionAdapter { // put on bottom and leave it visible t.setLayer(leash, zSplitLine - layer) } - } else if (mode == WindowManager.TRANSIT_CLOSE || - mode == WindowManager.TRANSIT_TO_BACK) { + } else if ( + mode == WindowManager.TRANSIT_CLOSE || mode == WindowManager.TRANSIT_TO_BACK + ) { if (isOpening) { // put on bottom and leave visible t.setLayer(leash, zSplitLine - layer) @@ -102,10 +109,15 @@ class RemoteTransitionAdapter { // making leashes means we have to handle them specially. return change.leash } - val leashSurface = SurfaceControl.Builder() + val leashSurface = + SurfaceControl.Builder() .setName(change.leash.toString() + "_transition-leash") - .setContainerLayer().setParent(if (change.parent == null) - info.rootLeash else info.getChange(change.parent!!)!!.leash).build() + .setContainerLayer() + .setParent( + if (change.parent == null) info.rootLeash + else info.getChange(change.parent!!)!!.leash + ) + .build() // Copied Transitions setup code (which expects bottom-to-top order, so we swap here) setupLeash(leashSurface, change, info.changes.size - order, info, t) t.reparent(change.leash, leashSurface) @@ -118,10 +130,10 @@ class RemoteTransitionAdapter { private fun newModeToLegacyMode(newMode: Int): Int { return when (newMode) { - WindowManager.TRANSIT_OPEN, WindowManager.TRANSIT_TO_FRONT - -> RemoteAnimationTarget.MODE_OPENING - WindowManager.TRANSIT_CLOSE, WindowManager.TRANSIT_TO_BACK - -> RemoteAnimationTarget.MODE_CLOSING + WindowManager.TRANSIT_OPEN, + WindowManager.TRANSIT_TO_FRONT -> RemoteAnimationTarget.MODE_OPENING + WindowManager.TRANSIT_CLOSE, + WindowManager.TRANSIT_TO_BACK -> RemoteAnimationTarget.MODE_CLOSING else -> RemoteAnimationTarget.MODE_CHANGING } } @@ -138,12 +150,13 @@ class RemoteTransitionAdapter { info: TransitionInfo, t: SurfaceControl.Transaction ): RemoteAnimationTarget { - val target = RemoteAnimationTarget( + val target = + RemoteAnimationTarget( /* taskId */ if (change.taskInfo != null) change.taskInfo!!.taskId else -1, /* mode */ newModeToLegacyMode(change.mode), /* leash */ createLeash(info, change, order, t), /* isTranslucent */ (change.flags and TransitionInfo.FLAG_TRANSLUCENT != 0 || - change.flags and TransitionInfo.FLAG_SHOW_WALLPAPER != 0), + change.flags and TransitionInfo.FLAG_SHOW_WALLPAPER != 0), /* clipRect */ null, /* contentInsets */ Rect(0, 0, 0, 0), /* prefixOrderIndex */ order, @@ -151,15 +164,16 @@ class RemoteTransitionAdapter { /* localBounds */ rectOffsetTo(change.endAbsBounds, change.endRelOffset), /* screenSpaceBounds */ Rect(change.endAbsBounds), /* windowConfig */ if (change.taskInfo != null) - change.taskInfo!!.configuration.windowConfiguration else - WindowConfiguration(), - /* isNotInRecents */ if (change.taskInfo != null) - !change.taskInfo!!.isRunning else true, + change.taskInfo!!.configuration.windowConfiguration + else WindowConfiguration(), + /* isNotInRecents */ if (change.taskInfo != null) !change.taskInfo!!.isRunning + else true, /* startLeash */ null, /* startBounds */ Rect(change.startAbsBounds), /* taskInfo */ change.taskInfo, /* allowEnterPip */ change.allowEnterPip, - /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE) + /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE + ) target.backgroundColor = change.backgroundColor return target } @@ -192,9 +206,7 @@ class RemoteTransitionAdapter { } @JvmStatic - fun adaptRemoteRunner( - runner: IRemoteAnimationRunner - ): IRemoteTransition.Stub { + fun adaptRemoteRunner(runner: IRemoteAnimationRunner): IRemoteTransition.Stub { return object : IRemoteTransition.Stub() { override fun startAnimation( token: IBinder, @@ -218,18 +230,24 @@ class RemoteTransitionAdapter { var displayH = 0f for (i in info.changes.indices.reversed()) { val change = info.changes[i] - if (change.taskInfo != null && - change.taskInfo!!.activityType - == WindowConfiguration.ACTIVITY_TYPE_HOME) { - isReturnToHome = (change.mode == WindowManager.TRANSIT_OPEN || + if ( + change.taskInfo != null && + change.taskInfo!!.activityType == + WindowConfiguration.ACTIVITY_TYPE_HOME + ) { + isReturnToHome = + (change.mode == WindowManager.TRANSIT_OPEN || change.mode == WindowManager.TRANSIT_TO_FRONT) launcherTask = change launcherLayer = info.changes.size - i } else if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) { wallpaper = change } - if (change.parent == null && change.endRotation >= 0 && - change.endRotation != change.startRotation) { + if ( + change.parent == null && + change.endRotation >= 0 && + change.endRotation != change.startRotation + ) { rotateDelta = change.endRotation - change.startRotation displayW = change.endAbsBounds.width().toFloat() displayH = change.endAbsBounds.height().toFloat() @@ -240,8 +258,13 @@ class RemoteTransitionAdapter { val counterLauncher = CounterRotator() val counterWallpaper = CounterRotator() if (launcherTask != null && rotateDelta != 0 && launcherTask.parent != null) { - counterLauncher.setup(t, info.getChange(launcherTask.parent!!)!!.leash, - rotateDelta, displayW, displayH) + counterLauncher.setup( + t, + info.getChange(launcherTask.parent!!)!!.leash, + rotateDelta, + displayW, + displayH + ) if (counterLauncher.surface != null) { t.setLayer(counterLauncher.surface!!, launcherLayer) } @@ -257,8 +280,10 @@ class RemoteTransitionAdapter { val mode = info.changes[i].mode // Only deal with independent layers if (!TransitionInfo.isIndependent(change, info)) continue - if (mode == WindowManager.TRANSIT_CLOSE || - mode == WindowManager.TRANSIT_TO_BACK) { + if ( + mode == WindowManager.TRANSIT_CLOSE || + mode == WindowManager.TRANSIT_TO_BACK + ) { t.setLayer(leash!!, info.changes.size * 3 - i) counterLauncher.addChild(t, leash) } @@ -273,8 +298,13 @@ class RemoteTransitionAdapter { counterLauncher.addChild(t, leashMap[launcherTask.leash]) } if (wallpaper != null && rotateDelta != 0 && wallpaper.parent != null) { - counterWallpaper.setup(t, info.getChange(wallpaper.parent!!)!!.leash, - rotateDelta, displayW, displayH) + counterWallpaper.setup( + t, + info.getChange(wallpaper.parent!!)!!.leash, + rotateDelta, + displayW, + displayH + ) if (counterWallpaper.surface != null) { t.setLayer(counterWallpaper.surface!!, -1) counterWallpaper.addChild(t, leashMap[wallpaper.leash]) @@ -282,37 +312,47 @@ class RemoteTransitionAdapter { } } t.apply() - val animationFinishedCallback = object : IRemoteAnimationFinishedCallback { - override fun onAnimationFinished() { - val finishTransaction = SurfaceControl.Transaction() - counterLauncher.cleanUp(finishTransaction) - counterWallpaper.cleanUp(finishTransaction) - // Release surface references now. This is apparently to free GPU memory - // while doing quick operations (eg. during CTS). - for (i in info.changes.indices.reversed()) { - info.changes[i].leash.release() - } - for (i in leashMap.size - 1 downTo 0) { - leashMap.valueAt(i).release() - } - try { - finishCallback.onTransitionFinished(null /* wct */, - finishTransaction) - } catch (e: RemoteException) { - Log.e("ActivityOptionsCompat", "Failed to call app controlled" + - " animation finished callback", e) + val animationFinishedCallback = + object : IRemoteAnimationFinishedCallback { + override fun onAnimationFinished() { + val finishTransaction = SurfaceControl.Transaction() + counterLauncher.cleanUp(finishTransaction) + counterWallpaper.cleanUp(finishTransaction) + // Release surface references now. This is apparently to free GPU + // memory while doing quick operations (eg. during CTS). + for (i in info.changes.indices.reversed()) { + info.changes[i].leash.release() + } + for (i in leashMap.size - 1 downTo 0) { + leashMap.valueAt(i).release() + } + try { + finishCallback.onTransitionFinished( + null /* wct */, + finishTransaction + ) + } catch (e: RemoteException) { + Log.e( + "ActivityOptionsCompat", + "Failed to call app controlled" + + " animation finished callback", + e + ) + } } - } - override fun asBinder(): IBinder? { - return null + override fun asBinder(): IBinder? { + return null + } } - } // TODO(bc-unlcok): Pass correct transit type. runner.onAnimationStart( - WindowManager.TRANSIT_OLD_NONE, - appsCompat, wallpapersCompat, nonAppsCompat, - animationFinishedCallback) + WindowManager.TRANSIT_OLD_NONE, + appsCompat, + wallpapersCompat, + nonAppsCompat, + animationFinishedCallback + ) } override fun mergeAnimation( @@ -329,18 +369,14 @@ class RemoteTransitionAdapter { } @JvmStatic - fun adaptRemoteAnimation( - adapter: RemoteAnimationAdapter - ): RemoteTransition { + fun adaptRemoteAnimation(adapter: RemoteAnimationAdapter): RemoteTransition { return RemoteTransition(adaptRemoteRunner(adapter.runner), adapter.callingApplication) } } - /** - * Utility class that takes care of counter-rotating surfaces during a transition animation. - */ + /** Utility class that takes care of counter-rotating surfaces during a transition animation. */ class CounterRotator { - /** Gets the surface with the counter-rotation. */ + /** Gets the surface with the counter-rotation. */ var surface: SurfaceControl? = null private set @@ -358,7 +394,8 @@ class RemoteTransitionAdapter { parentH: Float ) { if (rotateDelta == 0) return - val surface = SurfaceControl.Builder() + val surface = + SurfaceControl.Builder() .setName("Transition Unrotate") .setContainerLayer() .setParent(parent) @@ -378,21 +415,19 @@ class RemoteTransitionAdapter { t.show(surface) } - /** - * Adds a surface that needs to be counter-rotate. - */ + /** Adds a surface that needs to be counter-rotate. */ fun addChild(t: SurfaceControl.Transaction, child: SurfaceControl?) { if (surface == null) return t.reparent(child!!, surface) } /** - * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove the - * counter rotation surface. + * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove + * the counter rotation surface. */ fun cleanUp(finishTransaction: SurfaceControl.Transaction) { if (surface == null) return finishTransaction.remove(surface!!) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt index 0ee2bfea55c5..a96f893a8db4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt @@ -31,7 +31,7 @@ object ShadeInterpolation { } else { val oneMinusFrac = 1f - mappedFraction (1f - 0.5f * (1f - Math.cos((3.14159f * oneMinusFrac * oneMinusFrac).toDouble()))) - .toFloat() + .toFloat() } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index 093589f8c636..bba74793f3ed 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -41,12 +41,13 @@ class ViewHierarchyAnimator { private val DEFAULT_REMOVAL_INTERPOLATOR = Interpolators.STANDARD_ACCELERATE /** The properties used to animate the view bounds. */ - private val PROPERTIES = mapOf( - Bound.LEFT to createViewProperty(Bound.LEFT), - Bound.TOP to createViewProperty(Bound.TOP), - Bound.RIGHT to createViewProperty(Bound.RIGHT), - Bound.BOTTOM to createViewProperty(Bound.BOTTOM) - ) + private val PROPERTIES = + mapOf( + Bound.LEFT to createViewProperty(Bound.LEFT), + Bound.TOP to createViewProperty(Bound.TOP), + Bound.RIGHT to createViewProperty(Bound.RIGHT), + Bound.BOTTOM to createViewProperty(Bound.BOTTOM) + ) private fun createViewProperty(bound: Bound): IntProperty<View> { return object : IntProperty<View>(bound.label) { @@ -103,7 +104,8 @@ class ViewHierarchyAnimator { duration: Long, ephemeral: Boolean ): Boolean { - if (!isVisible( + if ( + !isVisible( rootView.visibility, rootView.left, rootView.top, @@ -131,11 +133,7 @@ class ViewHierarchyAnimator { duration: Long, ephemeral: Boolean ): View.OnLayoutChangeListener { - return createListener( - interpolator, - duration, - ephemeral - ) + return createListener(interpolator, duration, ephemeral) } /** @@ -171,7 +169,8 @@ class ViewHierarchyAnimator { duration: Long = DEFAULT_DURATION, includeMargins: Boolean = false ): Boolean { - if (isVisible( + if ( + isVisible( rootView.visibility, rootView.left, rootView.top, @@ -182,9 +181,13 @@ class ViewHierarchyAnimator { return false } - val listener = createAdditionListener( - origin, interpolator, duration, ignorePreviousValues = !includeMargins - ) + val listener = + createAdditionListener( + origin, + interpolator, + duration, + ignorePreviousValues = !includeMargins + ) addListener(rootView, listener, recursive = true) return true } @@ -257,24 +260,26 @@ class ViewHierarchyAnimator { return } - val startValues = processStartValues( - origin, - left, - top, - right, - bottom, - startLeft, - startTop, - startRight, - startBottom, - ignorePreviousValues - ) - val endValues = mapOf( - Bound.LEFT to left, - Bound.TOP to top, - Bound.RIGHT to right, - Bound.BOTTOM to bottom - ) + val startValues = + processStartValues( + origin, + left, + top, + right, + bottom, + startLeft, + startTop, + startRight, + startBottom, + ignorePreviousValues + ) + val endValues = + mapOf( + Bound.LEFT to left, + Bound.TOP to top, + Bound.RIGHT to right, + Bound.BOTTOM to bottom + ) val boundsToAnimate = mutableSetOf<Bound>() if (startValues.getValue(Bound.LEFT) != left) boundsToAnimate.add(Bound.LEFT) @@ -313,7 +318,8 @@ class ViewHierarchyAnimator { interpolator: Interpolator = DEFAULT_REMOVAL_INTERPOLATOR, duration: Long = DEFAULT_DURATION ): Boolean { - if (!isVisible( + if ( + !isVisible( rootView.visibility, rootView.left, rootView.top, @@ -327,11 +333,7 @@ class ViewHierarchyAnimator { val parent = rootView.parent as ViewGroup // Ensure that rootView's siblings animate nicely around the removal. - val listener = createUpdateListener( - interpolator, - duration, - ephemeral = true - ) + val listener = createUpdateListener(interpolator, duration, ephemeral = true) for (i in 0 until parent.childCount) { val child = parent.getChildAt(i) if (child == rootView) continue @@ -346,19 +348,21 @@ class ViewHierarchyAnimator { // them manually during the animation. parent.overlay.add(rootView) - val startValues = mapOf( - Bound.LEFT to rootView.left, - Bound.TOP to rootView.top, - Bound.RIGHT to rootView.right, - Bound.BOTTOM to rootView.bottom - ) - val endValues = processEndValuesForRemoval( - destination, - rootView.left, - rootView.top, - rootView.right, - rootView.bottom - ) + val startValues = + mapOf( + Bound.LEFT to rootView.left, + Bound.TOP to rootView.top, + Bound.RIGHT to rootView.right, + Bound.BOTTOM to rootView.bottom + ) + val endValues = + processEndValuesForRemoval( + destination, + rootView.left, + rootView.top, + rootView.right, + rootView.bottom + ) val boundsToAnimate = mutableSetOf<Bound>() if (rootView.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT) @@ -400,20 +404,24 @@ class ViewHierarchyAnimator { (animation.animatedValue as Float) * startAlphas[i] } } - animator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - rootView.animate() - .alpha(0f) - .setInterpolator(Interpolators.ALPHA_OUT) - .setDuration(duration / 2) - .withEndAction { parent.overlay.remove(rootView) } - .start() + animator.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + rootView + .animate() + .alpha(0f) + .setInterpolator(Interpolators.ALPHA_OUT) + .setDuration(duration / 2) + .withEndAction { parent.overlay.remove(rootView) } + .start() + } } - }) + ) animator.start() } else { // Fade out the view during the second half of the removal. - rootView.animate() + rootView + .animate() .alpha(0f) .setInterpolator(Interpolators.ALPHA_OUT) .setDuration(duration / 2) @@ -440,21 +448,23 @@ class ViewHierarchyAnimator { ) { for (i in 0 until rootView.childCount) { val child = rootView.getChildAt(i) - val childStartValues = mapOf( - Bound.LEFT to child.left, - Bound.TOP to child.top, - Bound.RIGHT to child.right, - Bound.BOTTOM to child.bottom - ) - val childEndValues = processChildEndValuesForRemoval( - destination, - child.left, - child.top, - child.right, - child.bottom, - endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT), - endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP) - ) + val childStartValues = + mapOf( + Bound.LEFT to child.left, + Bound.TOP to child.top, + Bound.RIGHT to child.right, + Bound.BOTTOM to child.bottom + ) + val childEndValues = + processChildEndValuesForRemoval( + destination, + child.left, + child.top, + child.right, + child.bottom, + endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT), + endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP) + ) val boundsToAnimate = mutableSetOf<Bound>() if (child.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT) @@ -500,6 +510,7 @@ class ViewHierarchyAnimator { * not newly introduced margins are included. * * Base case + * ``` * 1) origin=TOP * x---------x x---------x x---------x x---------x x---------x * x---------x | | | | | | @@ -518,11 +529,11 @@ class ViewHierarchyAnimator { * x -> x---x -> | | -> | | -> | | * x-----x x-------x | | * x---------x - * + * ``` * In case the start and end values differ in the direction of the origin, and * [ignorePreviousValues] is false, the previous values are used and a translation is * included in addition to the view expansion. - * + * ``` * origin=TOP_LEFT - (0,0,0,0) -> (30,30,70,70) * x * x--x @@ -531,6 +542,7 @@ class ViewHierarchyAnimator { * x----x | | * | | * x------x + * ``` */ private fun processStartValues( origin: Hotspot?, @@ -555,42 +567,54 @@ class ViewHierarchyAnimator { var bottom = startBottom if (origin != null) { - left = when (origin) { - Hotspot.CENTER -> (newLeft + newRight) / 2 - Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft) - Hotspot.TOP, Hotspot.BOTTOM -> newLeft - Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max( - startRight, - newRight - ) - } - top = when (origin) { - Hotspot.CENTER -> (newTop + newBottom) / 2 - Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop) - Hotspot.LEFT, Hotspot.RIGHT -> newTop - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max( - startBottom, - newBottom - ) - } - right = when (origin) { - Hotspot.CENTER -> (newLeft + newRight) / 2 - Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max( - startRight, - newRight - ) - Hotspot.TOP, Hotspot.BOTTOM -> newRight - Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft) - } - bottom = when (origin) { - Hotspot.CENTER -> (newTop + newBottom) / 2 - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max( - startBottom, - newBottom - ) - Hotspot.LEFT, Hotspot.RIGHT -> newBottom - Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop) - } + left = + when (origin) { + Hotspot.CENTER -> (newLeft + newRight) / 2 + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT -> min(startLeft, newLeft) + Hotspot.TOP, + Hotspot.BOTTOM -> newLeft + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT -> max(startRight, newRight) + } + top = + when (origin) { + Hotspot.CENTER -> (newTop + newBottom) / 2 + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT -> min(startTop, newTop) + Hotspot.LEFT, + Hotspot.RIGHT -> newTop + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT -> max(startBottom, newBottom) + } + right = + when (origin) { + Hotspot.CENTER -> (newLeft + newRight) / 2 + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT -> max(startRight, newRight) + Hotspot.TOP, + Hotspot.BOTTOM -> newRight + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT -> min(startLeft, newLeft) + } + bottom = + when (origin) { + Hotspot.CENTER -> (newTop + newBottom) / 2 + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT -> max(startBottom, newBottom) + Hotspot.LEFT, + Hotspot.RIGHT -> newBottom + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT -> min(startTop, newTop) + } } return mapOf( @@ -606,6 +630,7 @@ class ViewHierarchyAnimator { * view's starting bounds. * * Examples: + * ``` * 1) destination=TOP * x---------x x---------x x---------x x---------x x---------x * | | | | | | x---------x @@ -624,6 +649,7 @@ class ViewHierarchyAnimator { * | | -> | | -> | | -> x---x -> x * | | x-------x x-----x * x---------x + * ``` */ private fun processEndValuesForRemoval( destination: Hotspot, @@ -632,32 +658,54 @@ class ViewHierarchyAnimator { right: Int, bottom: Int ): Map<Bound, Int> { - val endLeft = when (destination) { - Hotspot.CENTER -> (left + right) / 2 - Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP -> - left - Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> right - } - val endTop = when (destination) { - Hotspot.CENTER -> (top + bottom) / 2 - Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT -> - top - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> bottom - } - val endRight = when (destination) { - Hotspot.CENTER -> (left + right) / 2 - Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT, - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM -> - right - Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> left - } - val endBottom = when (destination) { - Hotspot.CENTER -> (top + bottom) / 2 - Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, - Hotspot.BOTTOM_LEFT, Hotspot.LEFT -> - bottom - Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> top - } + val endLeft = + when (destination) { + Hotspot.CENTER -> (left + right) / 2 + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT, + Hotspot.TOP -> left + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT -> right + } + val endTop = + when (destination) { + Hotspot.CENTER -> (top + bottom) / 2 + Hotspot.LEFT, + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT, + Hotspot.RIGHT -> top + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT -> bottom + } + val endRight = + when (destination) { + Hotspot.CENTER -> (left + right) / 2 + Hotspot.TOP, + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM -> right + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT -> left + } + val endBottom = + when (destination) { + Hotspot.CENTER -> (top + bottom) / 2 + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT -> bottom + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT -> top + } return mapOf( Bound.LEFT to endLeft, @@ -675,6 +723,7 @@ class ViewHierarchyAnimator { * its center is at the [destination]. * * Examples: + * ``` * 1) destination=TOP * The child maintains its left and right positions, but is shifted up so that its * center is on the parent's end top edge. @@ -682,6 +731,7 @@ class ViewHierarchyAnimator { * The child shifts so that its center is on the parent's end bottom left corner. * 3) destination=CENTER * The child shifts so that its own center is on the parent's end center. + * ``` */ private fun processChildEndValuesForRemoval( destination: Hotspot, @@ -695,32 +745,54 @@ class ViewHierarchyAnimator { val halfWidth = (right - left) / 2 val halfHeight = (bottom - top) / 2 - val endLeft = when (destination) { - Hotspot.CENTER -> (parentWidth / 2) - halfWidth - Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> -halfWidth - Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth - halfWidth - Hotspot.TOP, Hotspot.BOTTOM -> left - } - val endTop = when (destination) { - Hotspot.CENTER -> (parentHeight / 2) - halfHeight - Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> -halfHeight - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> - parentHeight - halfHeight - Hotspot.LEFT, Hotspot.RIGHT -> top - } - val endRight = when (destination) { - Hotspot.CENTER -> (parentWidth / 2) + halfWidth - Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth + halfWidth - Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> halfWidth - Hotspot.TOP, Hotspot.BOTTOM -> right - } - val endBottom = when (destination) { - Hotspot.CENTER -> (parentHeight / 2) + halfHeight - Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> - parentHeight + halfHeight - Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> halfHeight - Hotspot.LEFT, Hotspot.RIGHT -> bottom - } + val endLeft = + when (destination) { + Hotspot.CENTER -> (parentWidth / 2) - halfWidth + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT -> -halfWidth + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT -> parentWidth - halfWidth + Hotspot.TOP, + Hotspot.BOTTOM -> left + } + val endTop = + when (destination) { + Hotspot.CENTER -> (parentHeight / 2) - halfHeight + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT -> -halfHeight + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT -> parentHeight - halfHeight + Hotspot.LEFT, + Hotspot.RIGHT -> top + } + val endRight = + when (destination) { + Hotspot.CENTER -> (parentWidth / 2) + halfWidth + Hotspot.TOP_RIGHT, + Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT -> parentWidth + halfWidth + Hotspot.BOTTOM_LEFT, + Hotspot.LEFT, + Hotspot.TOP_LEFT -> halfWidth + Hotspot.TOP, + Hotspot.BOTTOM -> right + } + val endBottom = + when (destination) { + Hotspot.CENTER -> (parentHeight / 2) + halfHeight + Hotspot.BOTTOM_RIGHT, + Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT -> parentHeight + halfHeight + Hotspot.TOP_LEFT, + Hotspot.TOP, + Hotspot.TOP_RIGHT -> halfHeight + Hotspot.LEFT, + Hotspot.RIGHT -> bottom + } return mapOf( Bound.LEFT to endLeft, @@ -790,44 +862,49 @@ class ViewHierarchyAnimator { duration: Long, ephemeral: Boolean ) { - val propertyValuesHolders = buildList { - bounds.forEach { bound -> - add( - PropertyValuesHolder.ofInt( - PROPERTIES[bound], - startValues.getValue(bound), - endValues.getValue(bound) - ) - ) - } - }.toTypedArray() + val propertyValuesHolders = + buildList { + bounds.forEach { bound -> + add( + PropertyValuesHolder.ofInt( + PROPERTIES[bound], + startValues.getValue(bound), + endValues.getValue(bound) + ) + ) + } + } + .toTypedArray() (view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel() val animator = ObjectAnimator.ofPropertyValuesHolder(view, *propertyValuesHolders) animator.interpolator = interpolator animator.duration = duration - animator.addListener(object : AnimatorListenerAdapter() { - var cancelled = false - - override fun onAnimationEnd(animation: Animator) { - view.setTag(R.id.tag_animator, null /* tag */) - bounds.forEach { view.setTag(it.overrideTag, null /* tag */) } - - // When an animation is cancelled, a new one might be taking over. We shouldn't - // unregister the listener yet. - if (ephemeral && !cancelled) { - // The duration is the same for the whole hierarchy, so it's safe to remove - // the listener recursively. We do this because some descendant views might - // not change bounds, and therefore not animate and leak the listener. - recursivelyRemoveListener(view) + animator.addListener( + object : AnimatorListenerAdapter() { + var cancelled = false + + override fun onAnimationEnd(animation: Animator) { + view.setTag(R.id.tag_animator, null /* tag */) + bounds.forEach { view.setTag(it.overrideTag, null /* tag */) } + + // When an animation is cancelled, a new one might be taking over. We + // shouldn't unregister the listener yet. + if (ephemeral && !cancelled) { + // The duration is the same for the whole hierarchy, so it's safe to + // remove the listener recursively. We do this because some descendant + // views might not change bounds, and therefore not animate and leak the + // listener. + recursivelyRemoveListener(view) + } } - } - override fun onAnimationCancel(animation: Animator?) { - cancelled = true + override fun onAnimationCancel(animation: Animator?) { + cancelled = true + } } - }) + ) bounds.forEach { bound -> setBound(view, bound, startValues.getValue(bound)) } @@ -838,7 +915,15 @@ class ViewHierarchyAnimator { /** An enum used to determine the origin of addition animations. */ enum class Hotspot { - CENTER, LEFT, TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT + CENTER, + LEFT, + TOP_LEFT, + TOP, + TOP_RIGHT, + RIGHT, + BOTTOM_RIGHT, + BOTTOM, + BOTTOM_LEFT } private enum class Bound(val label: String, val overrideTag: Int) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt index 76de7b503451..77640f1992e1 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt @@ -11,37 +11,36 @@ object ViewRootSync { /** * Synchronize the next draw between the view roots of [view] and [otherView], then run [then]. * - * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the - * next transactions) or disabled (temporarily, on low ram devices). In this case, [then] will - * be called without synchronizing. + * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the next + * transactions) or disabled (temporarily, on low ram devices). In this case, [then] will be + * called without synchronizing. */ - fun synchronizeNextDraw( - view: View, - otherView: View, - then: () -> Unit - ) { - if (!view.isAttachedToWindow || view.viewRootImpl == null || - !otherView.isAttachedToWindow || otherView.viewRootImpl == null || - view.viewRootImpl == otherView.viewRootImpl) { + fun synchronizeNextDraw(view: View, otherView: View, then: () -> Unit) { + if ( + !view.isAttachedToWindow || + view.viewRootImpl == null || + !otherView.isAttachedToWindow || + otherView.viewRootImpl == null || + view.viewRootImpl == otherView.viewRootImpl + ) { // No need to synchronize if either the touch surface or dialog view is not attached // to a window. then() return } - surfaceSyncer = SurfaceSyncer().apply { - val syncId = setupSync(Runnable { then() }) - addToSync(syncId, view) - addToSync(syncId, otherView) - markSyncReady(syncId) - } + surfaceSyncer = + SurfaceSyncer().apply { + val syncId = setupSync(Runnable { then() }) + addToSync(syncId, view) + addToSync(syncId, otherView) + markSyncReady(syncId) + } } - /** - * A Java-friendly API for [synchronizeNextDraw]. - */ + /** A Java-friendly API for [synchronizeNextDraw]. */ @JvmStatic fun synchronizeNextDraw(view: View, otherView: View, then: Runnable) { synchronizeNextDraw(view, otherView, then::run) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 5bd81a42a814..b894b10ff073 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.view.InsetsController; +import android.view.InsetsFrameProvider; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; @@ -105,7 +106,11 @@ public class WindowManagerWrapper { */ public void setProvidesInsetsTypes(WindowManager.LayoutParams params, int[] providesInsetsTypes) { - params.providesInsetsTypes = providesInsetsTypes; + final int length = providesInsetsTypes.length; + params.providedInsets = new InsetsFrameProvider[length]; + for (int i = 0; i < length; i++) { + params.providedInsets[i] = new InsetsFrameProvider(providesInsetsTypes[i]); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index acb080a2eaaa..550f393f4ca3 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -99,6 +99,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -110,6 +111,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -154,6 +156,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private static final String TAG = "GlobalActionsDialogLite"; + private static final String INTERACTION_JANK_TAG = "global_actions"; + private static final boolean SHOW_SILENT_TOGGLE = true; /* Valid settings for global actions keys. @@ -499,7 +503,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM); if (view != null) { - mDialogLaunchAnimator.showFromView(mDialog, view); + mDialogLaunchAnimator.showFromView(mDialog, view, + new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); } else { mDialog.show(); } 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 e372be3d18fc..42e9af846322 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -932,8 +932,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, } boolean isVolumeControlEnabled(@NonNull MediaDevice device) { - return isPlayBackInfoLocal() - || device.getDeviceType() != MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE; + return (isPlayBackInfoLocal() + || device.getDeviceType() != MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE) + && !device.isVolumeFixed(); } @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 36a46f067fb0..8701d4aba720 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -20,8 +20,10 @@ import android.content.Context import android.media.AudioManager import android.media.session.MediaSessionManager import android.view.View +import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.media.nearby.NearbyMediaDevicesManager @@ -46,6 +48,7 @@ class MediaOutputDialogFactory @Inject constructor( private val audioManager: AudioManager ) { companion object { + private const val INTERACTION_JANK_TAG = "media_output" var mediaOutputDialog: MediaOutputDialog? = null } @@ -54,16 +57,24 @@ class MediaOutputDialogFactory @Inject constructor( // Dismiss the previous dialog, if any. mediaOutputDialog?.dismiss() - val controller = MediaOutputController(context, packageName, - mediaSessionManager, lbm, starter, notifCollection, - dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager) + val controller = MediaOutputController( + context, packageName, + mediaSessionManager, lbm, starter, notifCollection, + dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager + ) val dialog = MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller, uiEventLogger) mediaOutputDialog = dialog // Show the dialog. if (view != null) { - dialogLaunchAnimator.showFromView(dialog, view) + dialogLaunchAnimator.showFromView( + dialog, view, + cuj = DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) } else { dialog.show() } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 79e6b060f1fc..730f88ab9f8f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -80,7 +80,7 @@ import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; -import android.view.InsetsState; +import android.view.InsetsFrameProvider; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsVisibilities; import android.view.KeyEvent; @@ -1631,12 +1631,14 @@ public class NavigationBar extends ViewController<NavigationBarView> implements | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.gravity = gravity; - lp.providedInternalInsets = new Insets[InsetsState.SIZE]; if (insetsHeight != -1) { - lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = - Insets.of(0, height - insetsHeight, 0, 0); + lp.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, insetsHeight)) + }; } else { - lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null; + lp.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_NAVIGATION_BAR) + }; } lp.token = new Binder(); lp.accessibilityTitle = userContext.getString(R.string.nav_bar); diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 05e8c021e3f5..90fc1d7ed49e 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -55,6 +55,7 @@ import android.view.WindowManager; import androidx.annotation.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.settingslib.Utils; @@ -62,6 +63,7 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.utils.PowerUtil; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.SysUISingleton; @@ -93,6 +95,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private static final String TAG_TEMPERATURE = "high_temp"; private static final String TAG_AUTO_SAVER = "auto_saver"; + private static final String INTERACTION_JANK_TAG = "start_power_saver"; + private static final int SHOWING_NOTHING = 0; private static final int SHOWING_WARNING = 1; private static final int SHOWING_INVALID_CHARGER = 3; @@ -707,7 +711,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { }); WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView(); if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) { - mDialogLaunchAnimator.showFromView(d, ref.get()); + mDialogLaunchAnimator.showFromView(d, ref.get(), + new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); } else { d.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt index 4c6fb5538c69..892c28306f46 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -45,8 +45,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT +import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.Dumpable import com.android.systemui.R +import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton @@ -81,6 +83,7 @@ class FgsManagerController @Inject constructor( ) : IForegroundServiceObserver.Stub(), Dumpable { companion object { + private const val INTERACTION_JANK_TAG = "active_background_apps" private val LOG_TAG = FgsManagerController::class.java.simpleName private const val DEFAULT_TASK_MANAGER_ENABLED = true private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false @@ -311,7 +314,15 @@ class FgsManagerController @Inject constructor( mainExecutor.execute { viewLaunchedFrom - ?.let { dialogLaunchAnimator.showFromView(dialog, it) } ?: dialog.show() + ?.let { + dialogLaunchAnimator.showFromView( + dialog, it, + cuj = DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) + } ?: dialog.show() } backgroundExecutor.execute { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9de132f64d0b..41724ef62683 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -151,15 +151,17 @@ public class QSPanel extends LinearLayout implements Tunable { mHorizontalContentContainer.setClipChildren(true); mHorizontalContentContainer.setClipToPadding(false); // Don't clip on the top, that way, secondary pages tiles can animate up + // Clipping coordinates should be relative to this view, not absolute (parent coordinates) mHorizontalContentContainer.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - if (left != oldLeft || right != oldRight || bottom != oldBottom) { - mClippingRect.left = left; - mClippingRect.right = right; - mClippingRect.bottom = bottom; + if ((right - left) != (oldRight - oldLeft) + || ((bottom - top) != (oldBottom - oldTop))) { + mClippingRect.right = right - left; + mClippingRect.bottom = bottom - top; mHorizontalContentContainer.setClipBounds(mClippingRect); } }); + mClippingRect.left = 0; mClippingRect.top = -1000; mHorizontalContentContainer.setClipBounds(mClippingRect); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 584de6e8c28f..87fcce455ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -80,9 +80,11 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.util.FrameworkStatsLog; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; @@ -108,6 +110,8 @@ class QSSecurityFooter extends ViewController<View> protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG_FORCE_VISIBLE = false; + private static final String INTERACTION_JANK_TAG = "managed_device_info"; + private final TextView mFooterText; private final ImageView mPrimaryFooterIcon; private Context mContext; @@ -557,7 +561,8 @@ class QSSecurityFooter extends ViewController<View> mDialog.setView(view); if (mView.isAggregatedVisible()) { - mDialogLaunchAnimator.showFromView(mDialog, mView); + mDialogLaunchAnimator.showFromView(mDialog, mView, new DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)); } else { mDialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 4afd39e36ce3..dce137f5b9f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -33,10 +33,12 @@ import android.widget.Button; import androidx.annotation.Nullable; import com.android.internal.app.MediaRouteDialogPresenter; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -63,6 +65,9 @@ import javax.inject.Inject; /** Quick settings tile: Cast **/ public class CastTile extends QSTileImpl<BooleanState> { + + private static final String INTERACTION_JANK_TAG = "cast"; + private static final Intent CAST_SETTINGS = new Intent(Settings.ACTION_CAST_SETTINGS); @@ -211,7 +216,9 @@ public class CastTile extends QSTileImpl<BooleanState> { mUiHandler.post(() -> { if (view != null) { - mDialogLaunchAnimator.showFromView(dialog, view); + mDialogLaunchAnimator.showFromView(dialog, view, + new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); } else { dialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index 1bbe411eee25..9fdf5940c392 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -24,10 +24,12 @@ import android.widget.Switch; import androidx.annotation.Nullable; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Prefs; import com.android.systemui.R; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -46,6 +48,8 @@ import javax.inject.Inject; public class DataSaverTile extends QSTileImpl<BooleanState> implements DataSaverController.Listener{ + private static final String INTERACTION_JANK_TAG = "start_data_saver"; + private final DataSaverController mDataSaverController; private final DialogLaunchAnimator mDialogLaunchAnimator; @@ -102,7 +106,9 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements dialog.setShowForAllUsers(true); if (view != null) { - mDialogLaunchAnimator.showFromView(dialog, view); + mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); } else { dialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index c006d69335da..3dcfbc04c463 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -39,11 +39,13 @@ import android.widget.Switch; import androidx.annotation.Nullable; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.notification.EnableZenModeDialog; import com.android.systemui.Prefs; import com.android.systemui.R; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -71,6 +73,8 @@ public class DndTile extends QSTileImpl<BooleanState> { private static final Intent ZEN_PRIORITY_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS); + private static final String INTERACTION_JANK_TAG = "start_zen_mode"; + private final ZenModeController mController; private final SharedPreferences mSharedPreferences; private final SettingObserver mSettingZenDuration; @@ -175,8 +179,9 @@ public class DndTile extends QSTileImpl<BooleanState> { mUiHandler.post(() -> { Dialog dialog = makeZenModeDialog(); if (view != null) { - mDialogLaunchAnimator.showFromView(dialog, view, - /* cuj= */ null, + mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG), /* animateBackgroundBoundsChange= */ false); } else { dialog.show(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index 45e43ee20d67..02d30c5224a0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -27,8 +27,10 @@ import android.widget.Switch; import androidx.annotation.Nullable; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -52,6 +54,8 @@ import javax.inject.Inject; public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> implements RecordingController.RecordingStateChangeCallback { private static final String TAG = "ScreenRecordTile"; + private static final String INTERACTION_JANK_TAG = "screen_record"; + private final RecordingController mController; private final KeyguardDismissUtil mKeyguardDismissUtil; private final KeyguardStateController mKeyguardStateController; @@ -165,7 +169,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> ActivityStarter.OnDismissAction dismissAction = () -> { if (shouldAnimateFromView) { - mDialogLaunchAnimator.showFromView(dialog, view); + mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)); } else { dialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt index 88aa734df2b3..260a3714a368 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt @@ -25,8 +25,10 @@ import android.provider.Settings import android.view.LayoutInflater import android.view.View import androidx.annotation.VisibleForTesting +import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.systemui.R +import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter @@ -67,6 +69,7 @@ class UserSwitchDialogController @VisibleForTesting constructor( ) companion object { + private const val INTERACTION_JANK_TAG = "switch_user" private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS) } @@ -89,14 +92,16 @@ class UserSwitchDialogController @VisibleForTesting constructor( if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS) val controller = dialogLaunchAnimator.createActivityLaunchController( - getButton(BUTTON_NEUTRAL)) + getButton(BUTTON_NEUTRAL) + ) if (controller == null) { dismiss() } activityStarter.postStartActivityDismissingKeyguard( - USER_SETTINGS_INTENT, 0, controller) + USER_SETTINGS_INTENT, 0, controller + ) } }, false /* dismissOnClick */) val gridFrame = LayoutInflater.from(this.context) @@ -107,7 +112,13 @@ class UserSwitchDialogController @VisibleForTesting constructor( adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid)) - dialogLaunchAnimator.showFromView(this, view) + dialogLaunchAnimator.showFromView( + this, view, + cuj = DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN) adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator)) } @@ -128,4 +139,4 @@ class UserSwitchDialogController @VisibleForTesting constructor( interface DialogShower : DialogInterface { fun showDialog(dialog: Dialog) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java index 718bc5caf414..f78b067279bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java @@ -22,18 +22,21 @@ package com.android.systemui.statusbar; public class StatusBarState { /** - * The status bar is in the "normal" shade mode. + * The status bar is in the "normal", unlocked mode or the device is still locked but we're + * accessing camera from power button double-tap shortcut. */ public static final int SHADE = 0; /** - * Status bar is currently the Keyguard. + * Status bar is currently the Keyguard. In single column mode, when you swipe from the top of + * the keyguard to expand QS immediately, it's still KEYGUARD state. */ public static final int KEYGUARD = 1; /** - * Status bar is in the special mode, where it is fully interactive but still locked. So - * dismissing the shade will still show the bouncer. + * Status bar is in the special mode, where it was transitioned from lockscreen to shade. + * Depending on user's security settings, dismissing the shade will either show the + * bouncer or go directly to unlocked {@link #SHADE} mode. */ public static final int SHADE_LOCKED = 2; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index d5023ffd8b37..808ba544eb5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -27,8 +27,11 @@ import android.hardware.fingerprint.FingerprintManager; import android.metrics.LogMaker; import android.os.Handler; import android.os.PowerManager; +import android.os.Process; import android.os.SystemClock; import android.os.Trace; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; import android.util.Log; import androidx.annotation.Nullable; @@ -61,6 +64,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; @@ -83,6 +87,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock"; private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2; + private static final VibrationEffect SUCCESS_VIBRATION_EFFECT = + VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + private static final VibrationEffect ERROR_VIBRATION_EFFECT = + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, @@ -158,6 +168,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final NotificationShadeWindowController mNotificationShadeWindowController; private final SessionTracker mSessionTracker; private final int mConsecutiveFpFailureThreshold; + private final boolean mShouldVibrate; private int mMode; private BiometricSourceType mBiometricType; private KeyguardViewController mKeyguardViewController; @@ -174,6 +185,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final AuthController mAuthController; private final StatusBarStateController mStatusBarStateController; private final LatencyTracker mLatencyTracker; + private final VibratorHelper mVibratorHelper; private long mLastFpFailureUptimeMillis; private int mNumConsecutiveFpFailures; @@ -278,7 +290,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp KeyguardUnlockAnimationController keyguardUnlockAnimationController, SessionTracker sessionTracker, LatencyTracker latencyTracker, - ScreenOffAnimationController screenOffAnimationController) { + ScreenOffAnimationController screenOffAnimationController, + VibratorHelper vibrator) { mPowerManager = powerManager; mShadeController = shadeController; mUpdateMonitor = keyguardUpdateMonitor; @@ -297,6 +310,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mHandler = handler; mConsecutiveFpFailureThreshold = resources.getInteger( R.integer.fp_consecutive_failure_time_ms); + mShouldVibrate = !(resources.getBoolean( + com.android.internal.R.bool.system_server_plays_face_haptics)); mKeyguardBypassController = keyguardBypassController; mKeyguardBypassController.setUnlockController(this); mMetricsLogger = metricsLogger; @@ -305,6 +320,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mSessionTracker = sessionTracker; mScreenOffAnimationController = screenOffAnimationController; + mVibratorHelper = vibrator; + dumpManager.registerDumpable(getClass().getName(), this); } @@ -407,8 +424,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } public void startWakeAndUnlock(BiometricSourceType biometricSourceType, - boolean isStrongBiometric) { - startWakeAndUnlock(calculateMode(biometricSourceType, isStrongBiometric)); + boolean isStrongBiometric) { + int mode = calculateMode(biometricSourceType, isStrongBiometric); + if (BiometricSourceType.FACE == biometricSourceType && (mode == MODE_WAKE_AND_UNLOCK + || mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING + || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER)) { + vibrateSuccess(); + } + startWakeAndUnlock(mode); } public void startWakeAndUnlock(@WakeAndUnlockMode int mode) { @@ -652,6 +675,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mNumConsecutiveFpFailures = 0; } } + + if (biometricSourceType == BiometricSourceType.FACE) { + vibrateError(); + } + cleanup(); } @@ -674,9 +702,34 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp startWakeAndUnlock(MODE_SHOW_BOUNCER); UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId()); } + + if (biometricSourceType == BiometricSourceType.FACE) { + vibrateError(); + } + cleanup(); } + private void vibrateSuccess() { + if (mShouldVibrate) { + mVibratorHelper.vibrate(Process.myUid(), + "com.android.systemui", + SUCCESS_VIBRATION_EFFECT, + getClass().getSimpleName() + "::success", + HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); + } + } + + private void vibrateError() { + if (mShouldVibrate) { + mVibratorHelper.vibrate(Process.myUid(), + "com.android.systemui", + ERROR_VIBRATION_EFFECT, + getClass().getSimpleName() + "::error", + HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); + } + } + private void cleanup() { releaseBiometricWakeLock(); } 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 cd6340bf7458..94a4fd4bdd60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -1163,15 +1163,28 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardMediaController.refreshMediaPosition(); if (splitShadeChanged) { - // when we switch from split shade to regular shade we want to enforce setting qs to - // the default state: expanded for split shade and collapsed otherwise - if (!isOnKeyguard() && mPanelExpanded) { - setQsExpanded(mSplitShadeEnabled); - } - updateClockAppearance(); - updateQsState(); - mNotificationStackScrollLayoutController.updateFooter(); + onSplitShadeEnabledChanged(); + } + } + + private void onSplitShadeEnabledChanged() { + // when we switch between split shade and regular shade we want to enforce setting qs to + // the default state: expanded for split shade and collapsed otherwise + if (!isOnKeyguard() && mPanelExpanded) { + setQsExpanded(mSplitShadeEnabled); } + if (isOnKeyguard() && mQsExpanded && mSplitShadeEnabled) { + // In single column keyguard - when you swipe from the top - QS is fully expanded and + // StatusBarState is KEYGUARD. That state doesn't make sense for split shade, + // where notifications are always visible and we effectively go to fully expanded + // shade, that is SHADE_LOCKED. + // Also we might just be switching from regular expanded shade, so we don't want + // to force state transition if it's already correct. + mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED, /* force= */false); + } + updateClockAppearance(); + updateQsState(); + mNotificationStackScrollLayoutController.updateFooter(); } private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) { 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 edc45ee46dc2..feed3347f3e2 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 @@ -571,4 +571,24 @@ public class MediaOutputControllerTest extends SysuiTestCase { assertThat(mMediaOutputController.getNotificationIcon()).isNull(); } + + @Test + public void isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse() { + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); + + when(mMediaDevice1.isVolumeFixed()).thenReturn(true); + + assertThat(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).isFalse(); + } + + @Test + public void isVolumeControlEnabled_isCastWithVolumeNotFixed_returnsTrue() { + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); + + when(mMediaDevice1.isVolumeFixed()).thenReturn(false); + + assertThat(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).isTrue(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 26e4d9dfb003..a56990f40b90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -222,7 +222,7 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { mReceiver.onReceive(mContext, intent); - verify(mDialogLaunchAnimator).showFromView(any(), eq(mView)); + verify(mDialogLaunchAnimator).showFromView(any(), eq(mView), any()); mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index e237a5ce03fe..60cfd7249919 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -13,18 +13,24 @@ */ package com.android.systemui.qs +import android.graphics.Rect import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import android.testing.ViewUtils import android.view.View -import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.tileimpl.QSIconViewImpl +import com.android.systemui.qs.tileimpl.QSTileViewImpl import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -36,37 +42,40 @@ import org.mockito.MockitoAnnotations @RunWithLooper @SmallTest class QSPanelTest : SysuiTestCase() { - private lateinit var mTestableLooper: TestableLooper - private lateinit var mQsPanel: QSPanel + private lateinit var testableLooper: TestableLooper + private lateinit var qsPanel: QSPanel - private lateinit var mParentView: ViewGroup - - private lateinit var mFooter: View + private lateinit var footer: View @Before @Throws(Exception::class) fun setup() { MockitoAnnotations.initMocks(this) - mTestableLooper = TestableLooper.get(this) + testableLooper = TestableLooper.get(this) + + testableLooper.runWithLooper { + qsPanel = QSPanel(context, null) + qsPanel.mUsingMediaPlayer = true - mTestableLooper.runWithLooper { - mQsPanel = QSPanel(mContext, null) - mQsPanel.initialize() + qsPanel.initialize() // QSPanel inflates a footer inside of it, mocking it here - mFooter = LinearLayout(mContext).apply { id = R.id.qs_footer } - mQsPanel.addView(mFooter) - mQsPanel.onFinishInflate() + footer = LinearLayout(context).apply { id = R.id.qs_footer } + qsPanel.addView(footer, MATCH_PARENT, 100) + qsPanel.onFinishInflate() // Provides a parent with non-zero size for QSPanel - mParentView = FrameLayout(mContext).apply { - addView(mQsPanel) - } + ViewUtils.attachView(qsPanel) } } + @After + fun tearDown() { + ViewUtils.detachView(qsPanel) + } + @Test fun testHasCollapseAccessibilityAction() { - val info = AccessibilityNodeInfo(mQsPanel) - mQsPanel.onInitializeAccessibilityNodeInfo(info) + val info = AccessibilityNodeInfo(qsPanel) + qsPanel.onInitializeAccessibilityNodeInfo(info) assertThat(info.actions and AccessibilityNodeInfo.ACTION_COLLAPSE).isNotEqualTo(0) assertThat(info.actions and AccessibilityNodeInfo.ACTION_EXPAND).isEqualTo(0) @@ -75,9 +84,79 @@ class QSPanelTest : SysuiTestCase() { @Test fun testCollapseActionCallsRunnable() { val mockRunnable = mock(Runnable::class.java) - mQsPanel.setCollapseExpandAction(mockRunnable) + qsPanel.setCollapseExpandAction(mockRunnable) - mQsPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE, null) + qsPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE, null) verify(mockRunnable).run() } + + @Test + fun testTilesFooterVisibleRTLLandscapeMedia() { + qsPanel.layoutDirection = View.LAYOUT_DIRECTION_RTL + // We need at least a tile so the layout has a height + qsPanel.tileLayout?.addTile( + QSPanelControllerBase.TileRecord( + mock(QSTile::class.java), + QSTileViewImpl(context, QSIconViewImpl(context)) + ) + ) + + val mediaView = FrameLayout(context) + mediaView.addView(View(context), MATCH_PARENT, 800) + + qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) + qsPanel.measure( + /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), + /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) + ) + qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) + + val tiles = qsPanel.tileLayout as View + // Tiles are effectively to the right of media + assertThat(mediaView isLeftOf tiles) + assertThat(tiles.isVisibleToUser).isTrue() + + assertThat(mediaView isLeftOf footer) + assertThat(footer.isVisibleToUser).isTrue() + } + + @Test + fun testTilesFooterVisibleLandscapeMedia() { + qsPanel.layoutDirection = View.LAYOUT_DIRECTION_LTR + // We need at least a tile so the layout has a height + qsPanel.tileLayout?.addTile( + QSPanelControllerBase.TileRecord( + mock(QSTile::class.java), + QSTileViewImpl(context, QSIconViewImpl(context)) + ) + ) + + val mediaView = FrameLayout(context) + mediaView.addView(View(context), MATCH_PARENT, 800) + + qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) + qsPanel.measure( + /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), + /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) + ) + qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) + + val tiles = qsPanel.tileLayout as View + // Tiles are effectively to the left of media + assertThat(tiles isLeftOf mediaView) + assertThat(tiles.isVisibleToUser).isTrue() + + assertThat(footer isLeftOf mediaView) + assertThat(footer.isVisibleToUser).isTrue() + } + + private infix fun View.isLeftOf(other: View): Boolean { + val rect = Rect() + getBoundsOnScreen(rect) + val thisRight = rect.right + + other.getBoundsOnScreen(rect) + + return thisRight <= rect.left + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index a3c353b5a666..c1c0f78ecd6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -731,7 +731,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mTestableLooper.processAllMessages(); - verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView)); + verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView), any()); } @Test @@ -768,7 +768,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class); mTestableLooper.processAllMessages(); - verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any()); + verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any(), any()); AlertDialog dialog = dialogCaptor.getValue(); dialog.create(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt index 80273e963676..9d908fdfb976 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt @@ -42,6 +42,7 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatcher import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt import org.mockito.Mockito.argThat import org.mockito.Mockito.never @@ -94,7 +95,7 @@ class UserSwitchDialogControllerTest : SysuiTestCase() { @Test fun showDialog_callsDialogShow() { controller.showDialog(launchView) - verify(dialogLaunchAnimator).showFromView(dialog, launchView) + verify(dialogLaunchAnimator).showFromView(eq(dialog), eq(launchView), any(), anyBoolean()) verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 5d898b65b60c..e5b6286fcd7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -51,6 +51,7 @@ import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -115,6 +116,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { private LatencyTracker mLatencyTracker; @Mock private ScreenOffAnimationController mScreenOffAnimationController; + @Mock + private VibratorHelper mVibratorHelper; private BiometricUnlockController mBiometricUnlockController; @Before @@ -136,7 +139,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mMetricsLogger, mDumpManager, mPowerManager, mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle, mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController, - mSessionTracker, mLatencyTracker, mScreenOffAnimationController); + mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index d47644f047a2..54bfd55e7b3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -1006,6 +1006,16 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() { + mStatusBarStateController.setState(KEYGUARD); + mNotificationPanelViewController.setQsExpanded(true); + + enableSplitShade(true); + + assertThat(mStatusBarStateController.getState()).isEqualTo(SHADE_LOCKED); + } + + @Test public void testSwitchesToCorrectClockInSinglePaneShade() { mStatusBarStateController.setState(KEYGUARD); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 9ae6750dbcbf..d0c58fd0545f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -99,7 +99,10 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, isStrongBiometric, null /* taskStackListener */, lockoutCache, - allowBackgroundAuthentication, true /* shouldVibrate */, + allowBackgroundAuthentication, + context.getResources().getBoolean( + com.android.internal.R.bool.system_server_plays_face_haptics) + /* shouldVibrate */, isKeyguardBypassEnabled); setRequestId(requestId); mUsageStats = usageStats; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5b74145afb80..4360d034b5e0 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -120,6 +120,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; import android.view.InsetsFlags; +import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; @@ -1079,17 +1080,18 @@ public class DisplayPolicy { return WindowManagerGlobal.ADD_INVALID_TYPE; } - if (attrs.providesInsetsTypes != null) { + if (attrs.providedInsets != null) { // Recents component is allowed to add inset types. if (!mService.mAtmService.isCallerRecents(callingUid)) { mContext.enforcePermission( android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); } - enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes); + enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providedInsets); - for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) { - switch (insetType) { + for (InsetsFrameProvider provider : attrs.providedInsets) { + @InternalInsetsType int insetsType = provider.type; + switch (insetsType) { case ITYPE_STATUS_BAR: if ((mStatusBar != null && mStatusBar.isAlive()) || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) { @@ -1155,12 +1157,19 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowContainer, inOutFrame) -> { if (!mNavButtonForcedVisible) { - final Insets[] providedInternalInsets = win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets; - if (providedInternalInsets != null - && providedInternalInsets.length > ITYPE_NAVIGATION_BAR - && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { - inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]); + final LayoutParams lp = + win.getLayoutingAttrs(displayFrames.mRotation); + if (lp.providedInsets != null) { + for (InsetsFrameProvider provider : + win.getLayoutingAttrs(displayFrames.mRotation) + .providedInsets) { + if (provider.type != ITYPE_NAVIGATION_BAR) { + continue; + } + calculateInsetsFrame(displayFrames, win, inOutFrame, + provider.source, provider.insetsSize + ); + } } inOutFrame.inset(win.mGivenContentInsets); } @@ -1204,21 +1213,9 @@ public class DisplayPolicy { if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; default: - if (attrs.providesInsetsTypes != null) { - for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { - final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = - win.getAttrs().providedInternalImeInsets != null - ? (displayFrames, windowContainer, inOutFrame) -> { - final Insets[] providedInternalImeInsets = - win.getLayoutingAttrs(displayFrames.mRotation) - .providedInternalImeInsets; - if (providedInternalImeInsets != null - && providedInternalImeInsets.length > insetsType - && providedInternalImeInsets[insetsType] != null) { - inOutFrame.inset(providedInternalImeInsets[insetsType]); - } - } : null; - switch (insetsType) { + if (attrs.providedInsets != null) { + for (InsetsFrameProvider provider : attrs.providedInsets) { + switch (provider.type) { case ITYPE_STATUS_BAR: mStatusBarAlt = win; mStatusBarAltPosition = getAltBarPosition(attrs); @@ -1236,41 +1233,24 @@ public class DisplayPolicy { mExtraNavBarAltPosition = getAltBarPosition(attrs); break; } - mDisplayContent.setInsetProvider(insetsType, win, - win.getAttrs().providedInternalInsets != null ? (displayFrames, - windowContainer, inOutFrame) -> { - final Insets[] providedInternalInsets = win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets; - if (providedInternalInsets != null - && providedInternalInsets.length > insetsType - && providedInternalInsets[insetsType] != null) { - inOutFrame.inset(providedInternalInsets[insetsType]); - } - inOutFrame.inset(win.mGivenContentInsets); - } : null, imeFrameProvider); - if (mNavigationBar == null && (insetsType == ITYPE_NAVIGATION_BAR - || insetsType == ITYPE_EXTRA_NAVIGATION_BAR)) { - mDisplayContent.setInsetProvider(ITYPE_LEFT_GESTURES, win, - (displayFrames, windowState, inOutFrame) -> { - final int leftSafeInset = - Math.max(displayFrames.mDisplayCutoutSafe.left,0); - inOutFrame.left = 0; - inOutFrame.top = 0; - inOutFrame.bottom = displayFrames.mDisplayHeight; - inOutFrame.right = - leftSafeInset + mLeftGestureInset; - }); - mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win, - (displayFrames, windowState, inOutFrame) -> { - final int rightSafeInset = - Math.min(displayFrames.mDisplayCutoutSafe.right, - displayFrames.mUnrestricted.right); - inOutFrame.left = rightSafeInset - mRightGestureInset; - inOutFrame.top = 0; - inOutFrame.bottom = displayFrames.mDisplayHeight; - inOutFrame.right = displayFrames.mDisplayWidth; - }); - } + final TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider = + provider.insetsSize != null + ? (displayFrames, windowContainer, inOutFrame) -> { + inOutFrame.inset(win.mGivenContentInsets); + calculateInsetsFrame(displayFrames, windowContainer, + inOutFrame, provider.source, + provider.insetsSize); + } : null; + final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = + provider.imeInsetsSize != null + ? (displayFrames, windowContainer, inOutFrame) -> { + inOutFrame.inset(win.mGivenContentInsets); + calculateInsetsFrame(displayFrames, windowContainer, + inOutFrame, provider.source, + provider.imeInsetsSize); + } : null; + mDisplayContent.setInsetProvider(provider.type, win, frameProvider, + imeFrameProvider); mInsetsSourceWindowsExceptIme.add(win); } } @@ -1278,6 +1258,29 @@ public class DisplayPolicy { } } + private void calculateInsetsFrame(DisplayFrames df, WindowContainer coutainer, Rect inOutFrame, + int source, Insets insetsSize) { + if (source == InsetsFrameProvider.SOURCE_DISPLAY) { + inOutFrame.set(df.mUnrestricted); + } else if (source == InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS) { + inOutFrame.set(coutainer.getBounds()); + } + if (insetsSize == null || insetsSize.equals(Insets.NONE)) { + return; + } + // Only one side of the provider shall be applied. Check in the order of left - top - + // right - bottom, only the first non-zero value will be applied. + if (insetsSize.left != 0) { + inOutFrame.right = inOutFrame.left + insetsSize.left; + } else if (insetsSize.top != 0) { + inOutFrame.bottom = inOutFrame.top + insetsSize.top; + } else if (insetsSize.right != 0) { + inOutFrame.left = inOutFrame.right - insetsSize.right; + } else if (insetsSize.bottom != 0) { + inOutFrame.top = inOutFrame.bottom - insetsSize.bottom; + } + } + @WindowManagerPolicy.AltBarPosition private int getAltBarPosition(WindowManager.LayoutParams params) { switch (params.gravity) { @@ -1316,10 +1319,11 @@ public class DisplayPolicy { }; } - private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) { + private static void enforceSingleInsetsTypeCorrespondingToWindowType( + InsetsFrameProvider[] providers) { int count = 0; - for (int insetsType : insetsTypes) { - switch (insetsType) { + for (InsetsFrameProvider provider : providers) { + switch (provider.type) { case ITYPE_NAVIGATION_BAR: case ITYPE_STATUS_BAR: case ITYPE_CLIMATE_BAR: @@ -1978,25 +1982,20 @@ public class DisplayPolicy { && lp.paramsForRotation[rotation] != null) { lp = lp.paramsForRotation[rotation]; } - final Insets providedInternalInsets; - if (lp.providedInternalInsets != null - && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR - && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { - providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; - } else { - providedInternalInsets = Insets.NONE; - } - if (position == NAV_BAR_LEFT) { - if (lp.width > providedInternalInsets.right) { - return lp.width - providedInternalInsets.right; - } else { - return 0; + Insets providedInsetsSize = null; + if (lp.providedInsets != null) { + for (InsetsFrameProvider provider : lp.providedInsets) { + if (provider.type != ITYPE_NAVIGATION_BAR) { + continue; + } + providedInsetsSize = provider.insetsSize; } - } else if (position == NAV_BAR_RIGHT) { - if (lp.width > providedInternalInsets.left) { - return lp.width - providedInternalInsets.left; - } else { - return 0; + } + if (providedInsetsSize != null) { + if (position == NAV_BAR_LEFT) { + return providedInsetsSize.left; + } else if (position == NAV_BAR_RIGHT) { + return providedInsetsSize.right; } } return lp.width; @@ -2043,18 +2042,20 @@ public class DisplayPolicy { return 0; } LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); - final Insets providedInternalInsets; - if (lp.providedInternalInsets != null - && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR - && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { - providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; - } else { - providedInternalInsets = Insets.NONE; - } - if (lp.height < providedInternalInsets.top) { - return 0; + Insets providedInsetsSize = null; + if (lp.providedInsets != null) { + for (InsetsFrameProvider provider : lp.providedInsets) { + if (provider.type != ITYPE_NAVIGATION_BAR) { + continue; + } + providedInsetsSize = provider.insetsSize; + if (providedInsetsSize != null) { + return providedInsetsSize.bottom; + } + break; + } } - return lp.height - providedInternalInsets.top; + return lp.height; } /** diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 9e0d7b57264e..db2ee2de06ca 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -55,6 +55,7 @@ import android.view.InsetsAnimationControlCallbacks; import android.view.InsetsAnimationControlImpl; import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; +import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -322,14 +323,14 @@ class InsetsPolicy { } // If not one of the types above, check whether an internal inset mapping is specified. - if (attrs.providesInsetsTypes != null) { - for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { - switch (insetsType) { + if (attrs.providedInsets != null) { + for (InsetsFrameProvider provider : attrs.providedInsets) { + switch (provider.type) { case ITYPE_STATUS_BAR: case ITYPE_NAVIGATION_BAR: case ITYPE_CLIMATE_BAR: case ITYPE_EXTRA_NAVIGATION_BAR: - return insetsType; + return provider.type; } } } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 178e299d317a..610bf0594c66 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -42,6 +42,7 @@ import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; +import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -513,12 +514,13 @@ abstract class InsetsSourceProvider { if (mWindowContainer.asWindowState() == null) { return false; } - final int[] provides = ((WindowState) mWindowContainer).mAttrs.providesInsetsTypes; - if (provides == null) { + final InsetsFrameProvider[] providers = + ((WindowState) mWindowContainer).mAttrs.providedInsets; + if (providers == null) { return false; } - for (int i = 0; i < provides.length; i++) { - if (provides[i] == ITYPE_IME) { + for (int i = 0; i < providers.length; i++) { + if (providers[i].type == ITYPE_IME) { return true; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e46444b9b8a7..a48140eb0e04 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2254,9 +2254,21 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalArgumentException( "Window type can not be changed after the window is added."); } - if (!Arrays.equals(win.mAttrs.providesInsetsTypes, attrs.providesInsetsTypes)) { - throw new IllegalArgumentException( - "Insets types can not be changed after the window is added."); + if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) { + if (win.mAttrs.providedInsets == null || attrs.providedInsets == null + || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) { + throw new IllegalArgumentException( + "Insets types can not be changed after the window is added."); + } else { + final int insetsTypes = attrs.providedInsets.length; + for (int i = 0; i < insetsTypes; i++) { + if (win.mAttrs.providedInsets[i].type != attrs.providedInsets[i].type) { + throw new IllegalArgumentException( + "Insets types can not be changed after the window is " + + "added."); + } + } + } } flagChanges = win.mAttrs.flags ^ attrs.flags; diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 758a56f3d2ad..e9171c0c3514 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -393,14 +393,14 @@ public class SystemConfigTest { + " <library \n" + " name=\"foo\"\n" + " file=\"" + mFooJar + "\"\n" - + " on-bootclasspath-before=\"A\"\n" + + " on-bootclasspath-before=\"Q\"\n" + " on-bootclasspath-since=\"W\"\n" + " />\n\n" + " </permissions>"; parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo("A"); + assertThat(entry.onBootclasspathBefore).isEqualTo("Q"); assertThat(entry.onBootclasspathSince).isEqualTo("W"); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 45ae81a71c44..34575ae2fe46 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -43,6 +43,7 @@ import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.Pair; import android.view.DisplayInfo; +import android.view.InsetsFrameProvider; import android.view.InsetsState; import android.view.PrivacyIndicatorBounds; import android.view.RoundedCorners; @@ -153,7 +154,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); - win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES}; + win.mAttrs.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_STATUS_BAR), + new InsetsFrameProvider(ITYPE_TOP_GESTURES) + }; win.getFrame().set(0, 0, 500, 100); addWindow(win); @@ -182,7 +186,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { public void addingWindow_InWindowTypeWithPredefinedInsets() { mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar"); - win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; + win.mAttrs.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_STATUS_BAR) + }; win.getFrame().set(0, 0, 500, 100); addWindow(win); @@ -199,12 +205,19 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void addingWindow_throwsException_WithMultipleInsetTypes() { WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); - win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; + win1.mAttrs.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_STATUS_BAR), + new InsetsFrameProvider(ITYPE_NAVIGATION_BAR) + }; expectThrows(IllegalArgumentException.class, () -> addWindow(win1)); WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); - win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; + + win2.mAttrs.providedInsets = new InsetsFrameProvider[] { + new InsetsFrameProvider(ITYPE_CLIMATE_BAR), + new InsetsFrameProvider(ITYPE_EXTRA_NAVIGATION_BAR) + }; expectThrows(IllegalArgumentException.class, () -> addWindow(win2)); } diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml index d51a4ddd43e8..5550eab61a33 100644 --- a/tests/WindowInsetsTests/res/layout/controller_activity.xml +++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml @@ -88,7 +88,7 @@ <TextView android:id="@+id/textViewControllableInsets" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" /> diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml index d6355f5a0464..516d4584426e 100644 --- a/tests/WindowInsetsTests/res/values/strings.xml +++ b/tests/WindowInsetsTests/res/values/strings.xml @@ -22,7 +22,7 @@ <!-- The item positions should match the flag values respectively. --> <string-array name="behaviors"> - <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item> + <item>BEHAVIOR_SHOW_BARS_BY_TOUCH (deprecated)</item> <item>BEHAVIOR_DEFAULT</item> <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item> </string-array> diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java index 95fd959e5587..e6b60cfbe84f 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java @@ -83,7 +83,51 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn final View contentView = findViewById(R.id.content); contentView.setOnApplyWindowInsetsListener(this); contentView.getWindowInsetsController().addOnControllableInsetsChangedListener( - (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types)); + (c, types) -> mTextControllableInsets.setText( + "ControllableInsetsTypes:\n" + insetsTypesToString(types))); + } + + private static String insetsTypesToString(int types) { + if (types == 0) { + return "none"; + } + final StringBuilder sb = new StringBuilder(); + if ((types & Type.statusBars()) != 0) { + types &= ~Type.statusBars(); + sb.append("statusBars "); + } + if ((types & Type.navigationBars()) != 0) { + types &= ~Type.navigationBars(); + sb.append("navigationBars "); + } + if ((types & Type.captionBar()) != 0) { + types &= ~Type.captionBar(); + sb.append("captionBar "); + } + if ((types & Type.ime()) != 0) { + types &= ~Type.ime(); + sb.append("ime "); + } + if ((types & Type.systemGestures()) != 0) { + types &= ~Type.systemGestures(); + sb.append("systemGestures "); + } + if ((types & Type.mandatorySystemGestures()) != 0) { + types &= ~Type.mandatorySystemGestures(); + sb.append("mandatorySystemGestures "); + } + if ((types & Type.tappableElement()) != 0) { + types &= ~Type.tappableElement(); + sb.append("tappableElement "); + } + if ((types & Type.displayCutout()) != 0) { + types &= ~Type.displayCutout(); + sb.append("displayCutout "); + } + if (types != 0) { + sb.append("unknownTypes:").append(types); + } + return sb.toString(); } @Override |