diff options
79 files changed, 3657 insertions, 474 deletions
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index aba4b2c0d591..23f2bdb7fa54 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -49,6 +49,12 @@ public class TaskInfo { private static final String TAG = "TaskInfo"; /** + * The value to use when the property has not a specific value. + * @hide + */ + public static final int PROPERTY_VALUE_UNSET = -1; + + /** * The id of the user the task was running as if this is a leaf task. The id of the current * running user of the system otherwise. * @hide @@ -230,6 +236,40 @@ public class TaskInfo { public boolean topActivityEligibleForLetterboxEducation; /** + * Whether the double tap is enabled + * @hide + */ + public boolean isLetterboxDoubleTapEnabled; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or + * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise. + * @hide + */ + public int topActivityLetterboxVerticalPosition; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or + * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise. + * @hide + */ + public int topActivityLetterboxHorizontalPosition; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current width of the letterboxed + * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise + * @hide + */ + public int topActivityLetterboxWidth; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current height of the letterboxed + * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise + * @hide + */ + public int topActivityLetterboxHeight; + + /** * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity * supports), this is what the system actually uses for resizability based on other policy and * developer options. @@ -407,7 +447,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { return hasCameraCompatControl() || topActivityInSizeCompat - || topActivityEligibleForLetterboxEducation; + || topActivityEligibleForLetterboxEducation + || isLetterboxDoubleTapEnabled; } /** @@ -447,6 +488,12 @@ public class TaskInfo { && isResizeable == that.isResizeable && supportsMultiWindow == that.supportsMultiWindow && displayAreaFeatureId == that.displayAreaFeatureId + && isLetterboxDoubleTapEnabled == that.isLetterboxDoubleTapEnabled + && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition + && topActivityLetterboxWidth == that.topActivityLetterboxWidth + && topActivityLetterboxHeight == that.topActivityLetterboxHeight + && topActivityLetterboxHorizontalPosition + == that.topActivityLetterboxHorizontalPosition && Objects.equals(positionInParent, that.positionInParent) && Objects.equals(pictureInPictureParams, that.pictureInPictureParams) && Objects.equals(shouldDockBigOverlays, that.shouldDockBigOverlays) @@ -475,6 +522,12 @@ public class TaskInfo { && topActivityInSizeCompat == that.topActivityInSizeCompat && topActivityEligibleForLetterboxEducation == that.topActivityEligibleForLetterboxEducation + && isLetterboxDoubleTapEnabled == that.isLetterboxDoubleTapEnabled + && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition + && topActivityLetterboxHorizontalPosition + == that.topActivityLetterboxHorizontalPosition + && topActivityLetterboxWidth == that.topActivityLetterboxWidth + && topActivityLetterboxHeight == that.topActivityLetterboxHeight && cameraCompatControlState == that.cameraCompatControlState // Bounds are important if top activity has compat controls. && (!hasCompatUI() || configuration.windowConfiguration.getBounds() @@ -529,6 +582,11 @@ public class TaskInfo { mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); displayAreaFeatureId = source.readInt(); cameraCompatControlState = source.readInt(); + isLetterboxDoubleTapEnabled = source.readBoolean(); + topActivityLetterboxVerticalPosition = source.readInt(); + topActivityLetterboxHorizontalPosition = source.readInt(); + topActivityLetterboxWidth = source.readInt(); + topActivityLetterboxHeight = source.readInt(); } /** @@ -576,6 +634,11 @@ public class TaskInfo { dest.writeTypedObject(mTopActivityLocusId, flags); dest.writeInt(displayAreaFeatureId); dest.writeInt(cameraCompatControlState); + dest.writeBoolean(isLetterboxDoubleTapEnabled); + dest.writeInt(topActivityLetterboxVerticalPosition); + dest.writeInt(topActivityLetterboxHorizontalPosition); + dest.writeInt(topActivityLetterboxWidth); + dest.writeInt(topActivityLetterboxHeight); } @Override @@ -611,6 +674,12 @@ public class TaskInfo { + " topActivityInSizeCompat=" + topActivityInSizeCompat + " topActivityEligibleForLetterboxEducation= " + topActivityEligibleForLetterboxEducation + + " topActivityLetterboxed= " + isLetterboxDoubleTapEnabled + + " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition + + " topActivityLetterboxHorizontalPosition= " + + topActivityLetterboxHorizontalPosition + + " topActivityLetterboxWidth=" + topActivityLetterboxWidth + + " topActivityLetterboxHeight=" + topActivityLetterboxHeight + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 77072890a1eb..856bde870bcf 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -70,6 +70,12 @@ public final class ContentCaptureOptions implements Parcelable { public final int logHistorySize; /** + * Disable flush when receiving a VIEW_TREE_APPEARING event. + * @hide + */ + public final boolean disableFlushForViewTreeAppearing; + + /** * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). */ @@ -90,7 +96,8 @@ public final class ContentCaptureOptions implements Parcelable { public ContentCaptureOptions(int loggingLevel) { this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, - /* logHistorySize= */ 0, /* whitelistedComponents= */ null); + /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, + /* whitelistedComponents= */ null); } /** @@ -98,10 +105,23 @@ public final class ContentCaptureOptions implements Parcelable { */ public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, - @SuppressLint("NullableCollection") + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + @Nullable ArraySet<ComponentName> whitelistedComponents) { + this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, logHistorySize, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); + } + + /** @hide */ + public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable ArraySet<ComponentName> whitelistedComponents) { this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents); + textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, + whitelistedComponents); } /** @hide */ @@ -111,11 +131,14 @@ public final class ContentCaptureOptions implements Parcelable { ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, - ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents); + ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); } private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, @Nullable ArraySet<ComponentName> whitelistedComponents) { this.lite = lite; this.loggingLevel = loggingLevel; @@ -123,6 +146,7 @@ public final class ContentCaptureOptions implements Parcelable { this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; this.logHistorySize = logHistorySize; + this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; this.whitelistedComponents = whitelistedComponents; } @@ -171,7 +195,8 @@ public final class ContentCaptureOptions implements Parcelable { .append(", maxBufferSize=").append(maxBufferSize) .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) - .append(", logHistorySize=").append(logHistorySize); + .append(", logHistorySize=").append(logHistorySize) + .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { string.append(", whitelisted=").append(whitelistedComponents); } @@ -189,6 +214,7 @@ public final class ContentCaptureOptions implements Parcelable { pw.print(", idle="); pw.print(idleFlushingFrequencyMs); pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); pw.print(", logSize="); pw.print(logHistorySize); + pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { pw.print(", whitelisted="); pw.print(whitelistedComponents); } @@ -209,6 +235,7 @@ public final class ContentCaptureOptions implements Parcelable { parcel.writeInt(idleFlushingFrequencyMs); parcel.writeInt(textChangeFlushingFrequencyMs); parcel.writeInt(logHistorySize); + parcel.writeBoolean(disableFlushForViewTreeAppearing); parcel.writeArraySet(whitelistedComponents); } @@ -226,12 +253,13 @@ public final class ContentCaptureOptions implements Parcelable { final int idleFlushingFrequencyMs = parcel.readInt(); final int textChangeFlushingFrequencyMs = parcel.readInt(); final int logHistorySize = parcel.readInt(); + final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); @SuppressWarnings("unchecked") final ArraySet<ComponentName> whitelistedComponents = (ArraySet<ComponentName>) parcel.readArraySet(null); return new ContentCaptureOptions(loggingLevel, maxBufferSize, idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, - whitelistedComponents); + disableFlushForViewTreeAppearing, whitelistedComponents); } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 59b5286f6fc5..34c7b8b9889f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -82,11 +82,19 @@ public final class ContentCaptureContext implements Parcelable { @SystemApi public static final int FLAG_RECONNECTED = 0x4; + /** + * Flag used to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final int FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING = 1 << 3; + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_DISABLED_BY_APP, FLAG_DISABLED_BY_FLAG_SECURE, - FLAG_RECONNECTED + FLAG_RECONNECTED, + FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING }) @Retention(RetentionPolicy.SOURCE) @interface ContextCreationFlags{} @@ -252,7 +260,8 @@ public final class ContentCaptureContext implements Parcelable { * Gets the flags associated with this context. * * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE}, - * {@link #FLAG_DISABLED_BY_APP} and {@link #FLAG_RECONNECTED}. + * {@link #FLAG_DISABLED_BY_APP}, {@link #FLAG_RECONNECTED} and {@link + * #FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING}. * * @hide */ diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 497f0668107f..668351b949c1 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -51,6 +51,7 @@ import android.view.WindowManager; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; @@ -343,6 +344,14 @@ public final class ContentCaptureManager { */ public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout"; + /** + * Sets to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = + "disable_flush_for_view_tree_appearing"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -373,6 +382,8 @@ public final class ContentCaptureManager { public static final int DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS = 1_000; /** @hide */ public static final int DEFAULT_LOG_HISTORY_SIZE = 10; + /** @hide */ + public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false; private final Object mLock = new Object(); @@ -448,6 +459,7 @@ public final class ContentCaptureManager { mOptions = Objects.requireNonNull(options, "options cannot be null"); ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel); + setFlushViewTreeAppearingEventDisabled(mOptions.disableFlushForViewTreeAppearing); if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); @@ -687,6 +699,38 @@ public final class ContentCaptureManager { } /** + * Explicitly sets enable or disable flush for view tree appearing event. + * + * @hide + */ + @VisibleForTesting + public void setFlushViewTreeAppearingEventDisabled(boolean disabled) { + if (sDebug) { + Log.d(TAG, "setFlushViewTreeAppearingEventDisabled(): setting to " + disabled); + } + + synchronized (mLock) { + if (disabled) { + mFlags |= ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } else { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } + } + } + + /** + * Gets whether content capture is needed to flush for view tree appearing event. + * + * @hide + */ + public boolean getFlushViewTreeAppearingEventDisabled() { + synchronized (mLock) { + return (mFlags & ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING) + != 0; + } + } + + /** * Gets whether content capture is enabled for the given user. * * <p>This method is typically used by the content capture service settings page, so it can diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 2134d819943e..bdec1970eda9 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -170,6 +170,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int FLUSH_REASON_TEXT_CHANGE_TIMEOUT = 6; /** @hide */ public static final int FLUSH_REASON_SESSION_CONNECTED = 7; + /** @hide */ + public static final int FLUSH_REASON_FORCE_FLUSH = 8; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARING = 9; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARED = 10; /** @hide */ @IntDef(prefix = { "FLUSH_REASON_" }, value = { @@ -179,7 +185,10 @@ public abstract class ContentCaptureSession implements AutoCloseable { FLUSH_REASON_SESSION_FINISHED, FLUSH_REASON_IDLE_TIMEOUT, FLUSH_REASON_TEXT_CHANGE_TIMEOUT, - FLUSH_REASON_SESSION_CONNECTED + FLUSH_REASON_SESSION_CONNECTED, + FLUSH_REASON_FORCE_FLUSH, + FLUSH_REASON_VIEW_TREE_APPEARING, + FLUSH_REASON_VIEW_TREE_APPEARED }) @Retention(RetentionPolicy.SOURCE) public @interface FlushReason{} @@ -614,6 +623,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "TEXT_CHANGE"; case FLUSH_REASON_SESSION_CONNECTED: return "CONNECTED"; + case FLUSH_REASON_FORCE_FLUSH: + return "FORCE_FLUSH"; + case FLUSH_REASON_VIEW_TREE_APPEARING: + return "VIEW_TREE_APPEARING"; + case FLUSH_REASON_VIEW_TREE_APPEARED: + return "VIEW_TREE_APPEARED"; default: return "UNKOWN-" + reason; } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 1f5e462d71fd..9848acd8dfcc 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -458,6 +458,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { case ContentCaptureEvent.TYPE_SESSION_FINISHED: flushReason = FLUSH_REASON_SESSION_FINISHED; break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARING; + break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARED; + break; default: flushReason = FLUSH_REASON_FULL; } @@ -764,7 +770,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { /** Public because is also used by ViewRootImpl */ public void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; - mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH)); + final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled(); + + mHandler.post(() -> sendEvent( + new ContentCaptureEvent(sessionId, type), + disableFlush ? !started : FORCE_FLUSH)); } void notifySessionResumed(int sessionId) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 33f9219dc2fd..0eca3d093e52 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -771,6 +771,11 @@ we rely on gravity to determine the effective orientation. --> <bool name="config_deskDockEnablesAccelerometer">true</bool> + <!-- Control whether nosensor and locked orientation requests are respected from the app when + config_deskDockEnablesAccelerometer is set to false. + TODO(b/274763533): Consider making true by default and removing this. --> + <bool name="config_deskRespectsNoSensorAndLockedWithoutAccelerometer">false</bool> + <!-- Car dock behavior --> <!-- The number of degrees to rotate the display when the device is in a car dock. @@ -5333,6 +5338,10 @@ <!-- Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps. --> <bool name="config_letterboxIsVerticalReachabilityEnabled">false</bool> + <!-- Whether book mode automatic horizontal reachability positioning is allowed for letterboxed + fullscreen apps --> + <bool name="config_letterboxIsAutomaticReachabilityInBookModeEnabled">false</bool> + <!-- Default horizontal position of the letterboxed app window when reachability is enabled and an app is fullscreen in landscape device orientation. When reachability is enabled, the position can change between left, center and right. This config defines the diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a0d69e22e2c5..138bffe2d9d8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1739,6 +1739,7 @@ <java-symbol type="bool" name="config_carDockEnablesAccelerometer" /> <java-symbol type="bool" name="config_customUserSwitchUi" /> <java-symbol type="bool" name="config_deskDockEnablesAccelerometer" /> + <java-symbol type="bool" name="config_deskRespectsNoSensorAndLockedWithoutAccelerometer" /> <java-symbol type="bool" name="config_disableMenuKeyInLockScreen" /> <java-symbol type="bool" name="config_enableCarDockHomeLaunch" /> <java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" /> @@ -4475,6 +4476,7 @@ <java-symbol type="dimen" name="config_letterboxTabletopModePositionMultiplier" /> <java-symbol type="bool" name="config_letterboxIsHorizontalReachabilityEnabled" /> <java-symbol type="bool" name="config_letterboxIsVerticalReachabilityEnabled" /> + <java-symbol type="bool" name="config_letterboxIsAutomaticReachabilityInBookModeEnabled" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForHorizontalReachability" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForVerticalReachability" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForBookModeReachability" /> diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index eae1bbc930d4..17ed4c478350 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.testng.Assert.assertThrows; @@ -54,4 +56,19 @@ public class ContentCaptureManagerTest { assertThrows(NullPointerException.class, () -> manager.removeData(null)); } + + @Test + @SuppressWarnings("GuardedBy") + public void testFlushViewTreeAppearingEventDisabled_setAndGet() { + final IContentCaptureManager mockService = mock(IContentCaptureManager.class); + final ContentCaptureOptions options = new ContentCaptureOptions(null); + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mockService, options); + + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + manager.setFlushViewTreeAppearingEventDisabled(true); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isTrue(); + manager.setFlushViewTreeAppearingEventDisabled(false); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + } } diff --git a/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml new file mode 100644 index 000000000000..c400dc676325 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml @@ -0,0 +1,699 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="30dp" android:width="30dp" android:viewportHeight="30" + android:viewportWidth="30"> + <group android:name="_R_G" android:scaleX="-1" android:translateX="30"> + <group android:name="_R_G_L_0_G" android:translateX="-135" android:translateY="-135" + android:pivotX="150" android:pivotY="150" android:scaleX="0.1" + android:scaleY="0.1"> + <group android:name="_R_G_L_0_G_L_1_G" android:translateX="134.624" + android:translateY="87.514" android:pivotX="11.625" android:pivotY="6.39" + android:scaleX="10" android:scaleY="10"> + <group android:name="_R_G_L_0_G_L_1_G_D_0_P_0_G_0_T_0" + android:translateX="11.625" android:translateY="6.464" + android:scaleX="1" android:scaleY="1"> + <path android:name="_R_G_L_0_G_L_1_G_D_0_P_0" + android:fillColor="@color/letterbox_reachability_education_item_color" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-1.54 5.39 C-3.87,4.71 -5.49,2.54 -5.49,0.11 C-5.49,-2.92 -3.03,-5.38 0,-5.38 C3.03,-5.38 5.49,-2.92 5.49,0.11 C5.49,2.11 4.41,3.95 2.66,4.92 C2.66,4.92 1.69,3.17 1.69,3.17 C2.8,2.55 3.49,1.38 3.49,0.11 C3.49,-1.82 1.93,-3.38 0,-3.38 C-1.93,-3.38 -3.49,-1.82 -3.49,0.11 C-3.49,1.65 -2.46,3.03 -0.98,3.47 C-0.98,3.47 -1.54,5.39 -1.54,5.39c "/> + </group> + </group> + <group android:name="_R_G_L_0_G_L_0_G" android:translateX="138" + android:translateY="138" android:pivotX="12" android:pivotY="12" + android:scaleX="10" android:scaleY="10"> + <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" + android:fillColor="@color/letterbox_reachability_education_item_color" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c "/> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_0_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="750" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="1083" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="2250" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="2583" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="3750" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="4083" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="5250" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="5583" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_L_1_G_D_0_P_0_G_0_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="1083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="1083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="2250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="2250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="2583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="2583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="3750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="3750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="4083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="4083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="5250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="5250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="5583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="5583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="500" + android:startOffset="0" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="500" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="750" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="833" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="1083" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="1167" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="2000" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="2250" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="2333" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="2583" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="2667" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="3500" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="3750" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="3833" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="4083" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="4167" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="5000" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="5250" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="5333" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="5583" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="6000" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml new file mode 100644 index 000000000000..a807a770aa22 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml @@ -0,0 +1,699 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="30dp" android:width="30dp" android:viewportHeight="30" + android:viewportWidth="30"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="-135" android:translateY="-135" + android:pivotX="150" android:pivotY="150" android:scaleX="0.1" + android:scaleY="0.1"> + <group android:name="_R_G_L_0_G_L_1_G" android:translateX="134.624" + android:translateY="87.514" android:pivotX="11.625" android:pivotY="6.39" + android:scaleX="10" android:scaleY="10"> + <group android:name="_R_G_L_0_G_L_1_G_D_0_P_0_G_0_T_0" + android:translateX="11.625" android:translateY="6.464" + android:scaleX="1" android:scaleY="1"> + <path android:name="_R_G_L_0_G_L_1_G_D_0_P_0" + android:fillColor="@color/letterbox_reachability_education_item_color" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-1.54 5.39 C-3.87,4.71 -5.49,2.54 -5.49,0.11 C-5.49,-2.92 -3.03,-5.38 0,-5.38 C3.03,-5.38 5.49,-2.92 5.49,0.11 C5.49,2.11 4.41,3.95 2.66,4.92 C2.66,4.92 1.69,3.17 1.69,3.17 C2.8,2.55 3.49,1.38 3.49,0.11 C3.49,-1.82 1.93,-3.38 0,-3.38 C-1.93,-3.38 -3.49,-1.82 -3.49,0.11 C-3.49,1.65 -2.46,3.03 -0.98,3.47 C-0.98,3.47 -1.54,5.39 -1.54,5.39c "/> + </group> + </group> + <group android:name="_R_G_L_0_G_L_0_G" android:translateX="138" + android:translateY="138" android:pivotX="12" android:pivotY="12" + android:scaleX="10" android:scaleY="10"> + <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" + android:fillColor="@color/letterbox_reachability_education_item_color" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c "/> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_0_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="750" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="1083" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="2250" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="2583" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="3750" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="4083" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="5250" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="0.1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="fillAlpha" android:duration="83" + android:startOffset="5583" android:valueFrom="0.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_L_1_G_D_0_P_0_G_0_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="500" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="1083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="1083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="1167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="2000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="2250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="2250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="2333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="2583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="2583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="2667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="3500" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="3750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="3750" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="3833" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="4083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="4083" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="833" + android:startOffset="4167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="5000" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="5250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="5250" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="250" + android:startOffset="5333" android:valueFrom="1" + android:valueTo="1.4000000000000001" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleX" android:duration="83" + android:startOffset="5583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="83" + android:startOffset="5583" android:valueFrom="1.4000000000000001" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.999,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="500" + android:startOffset="0" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="500" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="750" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="833" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="1083" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="1167" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="2000" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="2250" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="2333" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="2583" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="2667" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="3500" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="3750" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="3833" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="4083" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="833" + android:startOffset="4167" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="5000" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="5250" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="250" + android:startOffset="5333" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="83" + android:startOffset="5583" + android:valueFrom="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,8 14.13,8 C14.13,7.3 13.88,6.71 13.4,6.23 C12.92,5.74 12.33,5.5 11.63,5.5 C10.93,5.5 10.33,5.74 9.85,6.23 C9.37,6.71 9.13,7.3 9.13,8 C9.13,8 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,8 11.13,8 C11.13,7.85 11.17,7.73 11.26,7.64 C11.35,7.55 11.48,7.5 11.63,7.5 C11.78,7.5 11.9,7.55 11.99,7.64 C12.08,7.73 12.13,7.85 12.13,8 C12.13,8 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueTo="M19.81 13.64 C19.62,13.23 19.33,12.93 18.93,12.75 C18.93,12.75 15.23,10.95 15.23,10.95 C15.16,10.9 15.09,10.86 15.01,10.84 C14.99,10.83 14.96,10.83 14.94,10.83 C14.88,10.81 14.83,10.8 14.78,10.8 C14.78,10.8 14.13,10.8 14.13,10.8 C14.13,10.8 14.13,8.9 14.13,8.9 C14.13,8.9 14.13,6.5 14.13,6.5 C14.13,5.8 13.88,5.21 13.4,4.72 C12.92,4.24 12.33,4 11.63,4 C10.93,4 10.33,4.24 9.85,4.72 C9.37,5.21 9.13,5.8 9.13,6.5 C9.13,6.5 9.13,8.95 9.13,8.95 C9.13,8.95 9.13,11.4 9.13,11.4 C9.13,11.4 9.13,14.65 9.13,14.65 C9.13,14.65 7.18,14.2 7.18,14.2 C6.86,14.12 6.56,14.14 6.26,14.26 C5.97,14.39 5.71,14.57 5.48,14.8 C5.48,14.8 4.08,16.25 4.08,16.25 C4.08,16.25 9.23,21.4 9.23,21.4 C9.41,21.58 9.63,21.73 9.88,21.84 C10.13,21.95 10.39,22 10.68,22 C10.68,22 17.08,22 17.08,22 C17.56,22 17.99,21.85 18.38,21.54 C18.76,21.23 18.99,20.83 19.08,20.35 C19.08,20.35 19.98,14.9 19.98,14.9 C20.06,14.47 20,14.05 19.81,13.64c M17.08 20 C17.08,20 10.68,20 10.68,20 C10.68,20 6.88,16.2 6.88,16.2 C6.88,16.2 11.13,17.1 11.13,17.1 C11.13,17.1 11.13,6.5 11.13,6.5 C11.13,6.35 11.17,6.23 11.26,6.14 C11.35,6.05 11.48,6 11.63,6 C11.78,6 11.9,6.05 11.99,6.14 C12.08,6.23 12.13,6.35 12.13,6.5 C12.13,6.5 12.13,12.5 12.13,12.5 C12.13,12.5 13.88,12.5 13.88,12.5 C13.88,12.5 18.02,14.55 18.02,14.55 C18.02,14.55 17.08,20 17.08,20c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="6000" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/libs/WindowManager/Shell/res/layout/reachability_ui_layout.xml b/libs/WindowManager/Shell/res/layout/reachability_ui_layout.xml new file mode 100644 index 000000000000..1e36fb62f8da --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/reachability_ui_layout.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.wm.shell.compatui.ReachabilityEduLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:focusable="false" + android:focusableInTouchMode="false" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.wm.shell.compatui.ReachabilityEduHandLayout + style="@style/ReachabilityEduHandLayout" + android:text="@string/letterbox_reachability_reposition_text" + app:drawableTopCompat="@drawable/reachability_education_ic_right_hand" + android:layout_gravity="center_horizontal|top" + android:layout_marginTop="@dimen/letterbox_reachability_education_dialog_margin" + android:id="@+id/reachability_move_up_button" + android:maxWidth="@dimen/letterbox_reachability_education_item_width" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.android.wm.shell.compatui.ReachabilityEduHandLayout + style="@style/ReachabilityEduHandLayout" + android:text="@string/letterbox_reachability_reposition_text" + app:drawableTopCompat="@drawable/reachability_education_ic_right_hand" + android:layout_gravity="center_vertical|right" + android:layout_marginTop="@dimen/letterbox_reachability_education_dialog_margin" + android:id="@+id/reachability_move_right_button" + android:maxWidth="@dimen/letterbox_reachability_education_item_width" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + + <com.android.wm.shell.compatui.ReachabilityEduHandLayout + style="@style/ReachabilityEduHandLayout" + android:text="@string/letterbox_reachability_reposition_text" + app:drawableTopCompat="@drawable/reachability_education_ic_left_hand" + android:layout_gravity="center_vertical|left" + android:layout_marginTop="@dimen/letterbox_reachability_education_dialog_margin" + android:id="@+id/reachability_move_left_button" + android:maxWidth="@dimen/letterbox_reachability_education_item_width" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.android.wm.shell.compatui.ReachabilityEduHandLayout + style="@style/ReachabilityEduHandLayout" + android:text="@string/letterbox_reachability_reposition_text" + app:drawableTopCompat="@drawable/reachability_education_ic_right_hand" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginTop="@dimen/letterbox_reachability_education_dialog_margin" + android:id="@+id/reachability_move_down_button" + android:maxWidth="@dimen/letterbox_reachability_education_item_width" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + +</com.android.wm.shell.compatui.ReachabilityEduLayout> diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml index 2aad4c1c1805..fbb5caa508de 100644 --- a/libs/WindowManager/Shell/res/values/attrs.xml +++ b/libs/WindowManager/Shell/res/values/attrs.xml @@ -1,5 +1,5 @@ <!-- - ~ Copyright (C) 2022 The Android Open Source Project + ~ Copyright (C) 2023 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 6fb70006e67f..7d5760e47189 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -45,6 +45,9 @@ <!-- Letterbox Dialog --> <color name="letterbox_dialog_background">@android:color/system_neutral1_900</color> + <!-- Reachability Education color for hand icon and text--> + <color name="letterbox_reachability_education_item_color">#BFC8CC</color> + <!-- GM2 colors --> <color name="GM2_grey_200">#E8EAED</color> <color name="GM2_grey_700">#5F6368</color> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 680ad5101366..04b53f266a09 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -327,6 +327,15 @@ <!-- The vertical padding for the buttons in the letterbox restart dialog --> <dimen name="letterbox_restart_dialog_vertical_padding">8dp</dimen> + <!-- The margin between the reachability dialog container and its parent. --> + <dimen name="letterbox_reachability_education_dialog_margin">16dp</dimen> + + <!-- The width of each item in the reachability education --> + <dimen name="letterbox_reachability_education_item_width">118dp</dimen> + + <!-- The size of the icon in the item of reachability education --> + <dimen name="letterbox_reachability_education_item_image_size">24dp</dimen> + <!-- The width of the brand image on staring surface. --> <dimen name="starting_surface_brand_image_width">200dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 6399232919d2..523657b80317 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -214,6 +214,17 @@ <!-- Checkbox text for asking to not show the restart confirmation dialog again. [CHAR LIMIT=NONE] --> <string name="letterbox_restart_dialog_checkbox_title">Don\u2019t show again</string> + <!-- When an app is letterboxed, it is initially centered on the screen but the user can + double tap to move the app to a different position. With a double-tap on the right, + the app moves the right of the screen and with a double-tap on the left the app moves + on the left. The same happens if the app has space to be moved to the top or bottom of + the screen. This time the double-tap can happen on the top or bottom of the screen. + To teach the user about this feature, we display an education explaining how the double-tap + works and how the app can be moved on the screen. + This is the text we show to the user below an animated icon visualizing the double-tap + action. [CHAR LIMIT=NONE] --> + <string name="letterbox_reachability_reposition_text">Double-tap to move this app</string> + <!-- Freeform window caption strings --> <!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] --> <string name="maximize_button_text">Maximize</string> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index bc2e71d1c013..d0782ad9b37e 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -144,4 +144,20 @@ @*android:string/config_bodyFontFamily </item> </style> + + <style name="ReachabilityEduHandLayout" parent="Theme.AppCompat"> + <item name="android:focusable">false</item> + <item name="android:focusableInTouchMode">false</item> + <item name="android:background">@android:color/transparent</item> + <item name="android:contentDescription">@string/restart_button_description</item> + <item name="android:visibility">invisible</item> + <item name="android:lineSpacingExtra">-1sp</item> + <item name="android:textSize">12sp</item> + <item name="android:textAlignment">center</item> + <item name="android:textColor">@color/letterbox_reachability_education_item_color</item> + <item name="android:textAppearance"> + @*android:style/TextAppearance.DeviceDefault.Body2 + </item> + </style> + </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index ffc56b6f6106..b4acd6046182 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -727,6 +727,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange getRefBounds2(mTempRect); t.setPosition(leash2, mTempRect.left, mTempRect.top) .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); + // Make right or bottom side surface always higher than left or top side to avoid weird + // animation when dismiss split. e.g. App surface fling above on decor surface. + t.setLayer(leash1, 1); + t.setLayer(leash2, 2); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java index 06f0a70d3d0f..902c41c8fd2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java @@ -39,22 +39,41 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi "enable_letterbox_restart_confirmation_dialog"; private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = - "enable_letterbox_reachability_education"; + "enable_letterbox_education_for_reachability"; private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG = true; - private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = false; + private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = true; /** - * The name of the {@link SharedPreferences} that holds which user has seen the Restart - * confirmation dialog. + * The name of the {@link SharedPreferences} that holds information about compat ui. */ - private static final String DONT_SHOW_RESTART_DIALOG_PREF_NAME = "dont_show_restart_dialog"; + private static final String COMPAT_UI_SHARED_PREFERENCES = "dont_show_restart_dialog"; /** - * The {@link SharedPreferences} instance for {@link #DONT_SHOW_RESTART_DIALOG_PREF_NAME}. + * The name of the {@link SharedPreferences} that holds which user has seen the Letterbox + * Education dialog. */ - private final SharedPreferences mSharedPreferences; + private static final String HAS_SEEN_LETTERBOX_EDUCATION_SHARED_PREFERENCES = + "has_seen_letterbox_education"; + + /** + * Key prefix for the {@link SharedPreferences} entries related to the reachability + * education. + */ + private static final String HAS_SEEN_REACHABILITY_EDUCATION_KEY_PREFIX = + "has_seen_reachability_education"; + + /** + * The {@link SharedPreferences} instance for the restart dialog and the reachability + * education. + */ + private final SharedPreferences mCompatUISharedPreferences; + + /** + * The {@link SharedPreferences} instance for the letterbox education dialog. + */ + private final SharedPreferences mLetterboxEduSharedPreferences; // Whether the extended restart dialog is enabled private boolean mIsRestartDialogEnabled; @@ -88,8 +107,10 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor, this); - mSharedPreferences = context.getSharedPreferences(DONT_SHOW_RESTART_DIALOG_PREF_NAME, + mCompatUISharedPreferences = context.getSharedPreferences(getCompatUISharedPreferenceName(), Context.MODE_PRIVATE); + mLetterboxEduSharedPreferences = context.getSharedPreferences( + getHasSeenLetterboxEducationSharedPreferencedName(), Context.MODE_PRIVATE); } /** @@ -122,20 +143,53 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi mIsReachabilityEducationOverrideEnabled = enabled; } - boolean getDontShowRestartDialogAgain(TaskInfo taskInfo) { - final int userId = taskInfo.userId; - final String packageName = taskInfo.topActivity.getPackageName(); - return mSharedPreferences.getBoolean( - getDontShowAgainRestartKey(userId, packageName), /* default= */ false); + void setDontShowRestartDialogAgain(TaskInfo taskInfo) { + mCompatUISharedPreferences.edit().putBoolean( + getDontShowAgainRestartKey(taskInfo.userId, taskInfo.topActivity.getPackageName()), + true).apply(); } - void setDontShowRestartDialogAgain(TaskInfo taskInfo) { - final int userId = taskInfo.userId; - final String packageName = taskInfo.topActivity.getPackageName(); - mSharedPreferences.edit().putBoolean(getDontShowAgainRestartKey(userId, packageName), + boolean shouldShowRestartDialogAgain(TaskInfo taskInfo) { + return !mCompatUISharedPreferences.getBoolean(getDontShowAgainRestartKey(taskInfo.userId, + taskInfo.topActivity.getPackageName()), /* default= */ false); + } + + void setDontShowReachabilityEducationAgain(TaskInfo taskInfo) { + mCompatUISharedPreferences.edit().putBoolean( + getDontShowAgainReachabilityEduKey(taskInfo.userId, + taskInfo.topActivity.getPackageName()), true).apply(); + } + + boolean shouldShowReachabilityEducation(@NonNull TaskInfo taskInfo) { + return getHasSeenLetterboxEducation(taskInfo.userId) + && !mCompatUISharedPreferences.getBoolean( + getDontShowAgainReachabilityEduKey(taskInfo.userId, + taskInfo.topActivity.getPackageName()), /* default= */false); + } + + boolean getHasSeenLetterboxEducation(int userId) { + return mLetterboxEduSharedPreferences + .getBoolean(getDontShowLetterboxEduKey(userId), /* default= */ false); + } + + void setSeenLetterboxEducation(int userId) { + mLetterboxEduSharedPreferences.edit().putBoolean(getDontShowLetterboxEduKey(userId), true).apply(); } + protected String getCompatUISharedPreferenceName() { + return COMPAT_UI_SHARED_PREFERENCES; + } + + protected String getHasSeenLetterboxEducationSharedPreferencedName() { + return HAS_SEEN_LETTERBOX_EDUCATION_SHARED_PREFERENCES; + } + + /** + * Updates the {@link DeviceConfig} state for the CompatUI + * @param properties Contains the complete collection of properties which have changed for a + * single namespace. This includes only those which were added, updated, + */ @Override public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) { @@ -152,6 +206,14 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi } } + private static String getDontShowAgainReachabilityEduKey(int userId, String packageName) { + return HAS_SEEN_REACHABILITY_EDUCATION_KEY_PREFIX + "_" + packageName + "@" + userId; + } + + private static String getDontShowLetterboxEduKey(int userId) { + return String.valueOf(userId); + } + private String getDontShowAgainRestartKey(int userId, String packageName) { return packageName + "@" + userId; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 6950f24512b1..4d83247e5c03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -112,6 +112,12 @@ public class CompatUIController implements OnDisplaysChangedListener, @Nullable private LetterboxEduWindowManager mActiveLetterboxEduLayout; + /** + * The active Reachability UI layout. + */ + @Nullable + private ReachabilityEduWindowManager mActiveReachabilityEduLayout; + /** Avoid creating display context frequently for non-default display. */ private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0); @@ -195,6 +201,7 @@ public class CompatUIController implements OnDisplaysChangedListener, createOrUpdateCompatLayout(taskInfo, taskListener); createOrUpdateLetterboxEduLayout(taskInfo, taskListener); createOrUpdateRestartDialogLayout(taskInfo, taskListener); + createOrUpdateReachabilityEduLayout(taskInfo, taskListener, false); } @Override @@ -308,7 +315,7 @@ public class CompatUIController implements OnDisplaysChangedListener, private void onRestartButtonClicked( Pair<TaskInfo, ShellTaskOrganizer.TaskListener> taskInfoState) { if (mCompatUIConfiguration.isRestartDialogEnabled() - && !mCompatUIConfiguration.getDontShowRestartDialogAgain( + && mCompatUIConfiguration.shouldShowRestartDialogAgain( taskInfoState.first)) { // We need to show the dialog mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId); @@ -355,13 +362,15 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellTaskOrganizer.TaskListener taskListener) { return new LetterboxEduWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), - mTransitionsLazy.get(), - this::onLetterboxEduDismissed, - mDockStateReader); + mTransitionsLazy.get(), this::onLetterboxEduDismissed, mDockStateReader, + mCompatUIConfiguration); } - private void onLetterboxEduDismissed() { + private void onLetterboxEduDismissed( + Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) { mActiveLetterboxEduLayout = null; + // We need to update the UI + createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second, true); } private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo, @@ -419,6 +428,47 @@ public class CompatUIController implements OnDisplaysChangedListener, onCompatInfoChanged(stateInfo.first, stateInfo.second); } + private void createOrUpdateReachabilityEduLayout(TaskInfo taskInfo, + ShellTaskOrganizer.TaskListener taskListener, boolean forceUpdate) { + if (mActiveReachabilityEduLayout != null) { + mActiveReachabilityEduLayout.forceUpdate(forceUpdate); + // UI already exists, update the UI layout. + if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) { + // The layout is no longer eligible to be shown, remove from active layouts. + mActiveReachabilityEduLayout = null; + } + return; + } + // Create a new UI layout. + final Context context = getOrCreateDisplayContext(taskInfo.displayId); + if (context == null) { + return; + } + ReachabilityEduWindowManager newLayout = createReachabilityEduWindowManager(context, + taskInfo, taskListener); + if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) { + // The new layout is eligible to be shown, make it the active layout. + if (mActiveReachabilityEduLayout != null) { + // Release the previous layout since at most one can be active. + // Since letterbox reachability education is only shown once to the user, + // releasing the previous layout is only a precaution. + mActiveReachabilityEduLayout.release(); + } + mActiveReachabilityEduLayout = newLayout; + } + } + + @VisibleForTesting + ReachabilityEduWindowManager createReachabilityEduWindowManager(Context context, + TaskInfo taskInfo, + ShellTaskOrganizer.TaskListener taskListener) { + return new ReachabilityEduWindowManager(context, taskInfo, mSyncQueue, mCallback, + taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), + mCompatUIConfiguration, mMainExecutor); + } + + private void removeLayouts(int taskId) { final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId); if (layout != null) { @@ -438,6 +488,11 @@ public class CompatUIController implements OnDisplaysChangedListener, mTaskIdToRestartDialogWindowManagerMap.remove(taskId); mSetOfTaskIdsShowingRestartDialog.remove(taskId); } + if (mActiveReachabilityEduLayout != null + && mActiveReachabilityEduLayout.getTaskId() == taskId) { + mActiveReachabilityEduLayout.release(); + mActiveReachabilityEduLayout = null; + } } private Context getOrCreateDisplayContext(int displayId) { @@ -490,6 +545,9 @@ public class CompatUIController implements OnDisplaysChangedListener, callback.accept(layout); } } + if (mActiveReachabilityEduLayout != null && condition.test(mActiveReachabilityEduLayout)) { + callback.accept(mActiveReachabilityEduLayout); + } } /** An implementation of {@link OnInsetsChangedListener} for a given display id. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java index cfb2accbcecd..346cd940e678 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -384,7 +384,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana // Cannot be wrap_content as this determines the actual window size width, height, TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + getWindowManagerLayoutParamsFlags(), PixelFormat.TRANSLUCENT); winParams.token = new Binder(); winParams.setTitle(getClass().getSimpleName() + mTaskId); @@ -392,6 +392,13 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana return winParams; } + /** + * @return Flags to use for the {@link WindowManager} layout + */ + protected int getWindowManagerLayoutParamsFlags() { + return FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL; + } + protected final String getTag() { return getClass().getSimpleName(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java index bfdbfe3d6ea0..0c21c8ccd686 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java @@ -18,12 +18,13 @@ package com.android.wm.shell.compatui; import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; -import android.content.SharedPreferences; import android.graphics.Rect; import android.provider.Settings; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; @@ -38,10 +39,12 @@ import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.transition.Transitions; +import java.util.function.Consumer; + /** * Window manager for the Letterbox Education. */ -public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { +class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { /** * The Letterbox Education should be the topmost child of the Task in case there can be more @@ -49,19 +52,6 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { */ public static final int Z_ORDER = Integer.MAX_VALUE; - /** - * The name of the {@link SharedPreferences} that holds which user has seen the Letterbox - * Education dialog. - */ - @VisibleForTesting - static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME = - "has_seen_letterbox_education"; - - /** - * The {@link SharedPreferences} instance for {@link #HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME}. - */ - private final SharedPreferences mSharedPreferences; - private final DialogAnimationController<LetterboxEduDialogLayout> mAnimationController; private final Transitions mTransitions; @@ -73,6 +63,10 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { */ private final int mUserId; + private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback; + + private final CompatUIConfiguration mCompatUIConfiguration; + // Remember the last reported state in case visibility changes due to keyguard or IME updates. private boolean mEligibleForLetterboxEducation; @@ -80,7 +74,8 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { @VisibleForTesting LetterboxEduDialogLayout mLayout; - private final Runnable mOnDismissCallback; + @NonNull + private TaskInfo mTaskInfo; /** * The vertical margin between the dialog container and the task stable bounds (excluding @@ -90,33 +85,35 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { private final DockStateReader mDockStateReader; - public LetterboxEduWindowManager(Context context, TaskInfo taskInfo, + LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, Transitions transitions, - Runnable onDismissCallback, DockStateReader dockStateReader) { + Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback, + DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration) { this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions, onDismissCallback, new DialogAnimationController<>(context, /* tag */ "LetterboxEduWindowManager"), - dockStateReader); + dockStateReader, compatUIConfiguration); } @VisibleForTesting LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, - DisplayLayout displayLayout, Transitions transitions, Runnable onDismissCallback, + DisplayLayout displayLayout, Transitions transitions, + Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback, DialogAnimationController<LetterboxEduDialogLayout> animationController, - DockStateReader dockStateReader) { + DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration) { super(context, taskInfo, syncQueue, taskListener, displayLayout); + mTaskInfo = taskInfo; mTransitions = transitions; mOnDismissCallback = onDismissCallback; mAnimationController = animationController; mUserId = taskInfo.userId; - mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation; - mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME, - Context.MODE_PRIVATE); mDialogVerticalMargin = (int) mContext.getResources().getDimension( R.dimen.letterbox_education_dialog_margin); mDockStateReader = dockStateReader; + mCompatUIConfiguration = compatUIConfiguration; + mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation; } @Override @@ -142,8 +139,8 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { // the controller will create a new instance of this class since this one isn't eligible). // - If the layout isn't null then it was previously showing, and we shouldn't check if the // user has seen the letterbox education before. - return mEligibleForLetterboxEducation && !isTaskbarEduShowing() - && (mLayout != null || !getHasSeenLetterboxEducation()) + return mEligibleForLetterboxEducation && !isTaskbarEduShowing() && (mLayout != null + || !mCompatUIConfiguration.getHasSeenLetterboxEducation(mUserId)) && !mDockStateReader.isDocked(); } @@ -192,7 +189,6 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { // Dialog has already been released. return; } - setSeenLetterboxEducation(); mLayout.setDismissOnClickListener(this::onDismiss); // Focus on the dialog title for accessibility. mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); @@ -202,10 +198,11 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { if (mLayout == null) { return; } + mCompatUIConfiguration.setSeenLetterboxEducation(mUserId); mLayout.setDismissOnClickListener(null); mAnimationController.startExitAnimation(mLayout, () -> { release(); - mOnDismissCallback.run(); + mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener())); }); } @@ -218,6 +215,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { + mTaskInfo = taskInfo; mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation; return super.updateCompatInfo(taskInfo, taskListener, canShow); @@ -248,18 +246,6 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { taskBounds.height()); } - private boolean getHasSeenLetterboxEducation() { - return mSharedPreferences.getBoolean(getPrefKey(), /* default= */ false); - } - - private void setSeenLetterboxEducation() { - mSharedPreferences.edit().putBoolean(getPrefKey(), true).apply(); - } - - private String getPrefKey() { - return String.valueOf(mUserId); - } - @VisibleForTesting boolean isTaskbarEduShowing() { return Settings.Secure.getInt(mContext.getContentResolver(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduHandLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduHandLayout.java new file mode 100644 index 000000000000..6081ef1ca307 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduHandLayout.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import android.content.Context; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; + +import androidx.appcompat.widget.AppCompatTextView; + +/** + * Custom layout for Reachability Education hand. + */ +public class ReachabilityEduHandLayout extends AppCompatTextView { + + private Drawable mHandDrawable; + + public ReachabilityEduHandLayout(Context context) { + this(context, null); + } + + public ReachabilityEduHandLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ReachabilityEduHandLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mHandDrawable = getCompoundDrawables()[/* top */ 1]; + } + + void hide() { + stopAnimation(); + setAlpha(0); + setVisibility(View.INVISIBLE); + } + + void startAnimation() { + if (mHandDrawable instanceof Animatable) { + final Animatable animatedBg = (Animatable) mHandDrawable; + animatedBg.start(); + } + } + + void stopAnimation() { + if (mHandDrawable instanceof Animatable) { + final Animatable animatedBg = (Animatable) mHandDrawable; + animatedBg.stop(); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduLayout.java new file mode 100644 index 000000000000..6a72d28521b8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduLayout.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.app.TaskInfo; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.FrameLayout; + +import com.android.wm.shell.R; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * Container for reachability education which handles all the show/hide animations. + */ +public class ReachabilityEduLayout extends FrameLayout { + + private static final float ALPHA_FULL_TRANSPARENT = 0f; + + private static final float ALPHA_FULL_OPAQUE = 1f; + + private static final long VISIBILITY_SHOW_ANIMATION_DURATION_MS = 167; + + private static final long VISIBILITY_SHOW_ANIMATION_DELAY_MS = 250; + + private static final long VISIBILITY_SHOW_DOUBLE_TAP_ANIMATION_DELAY_MS = 80; + + private static final long MARGINS_ANIMATION_DURATION_MS = 250; + + private ReachabilityEduWindowManager mWindowManager; + + private ReachabilityEduHandLayout mMoveLeftButton; + private ReachabilityEduHandLayout mMoveRightButton; + private ReachabilityEduHandLayout mMoveUpButton; + private ReachabilityEduHandLayout mMoveDownButton; + + private int mLastLeftMargin = TaskInfo.PROPERTY_VALUE_UNSET; + private int mLastRightMargin = TaskInfo.PROPERTY_VALUE_UNSET; + private int mLastTopMargin = TaskInfo.PROPERTY_VALUE_UNSET; + private int mLastBottomMargin = TaskInfo.PROPERTY_VALUE_UNSET; + + private boolean mIsLayoutActive; + + public ReachabilityEduLayout(Context context) { + this(context, null); + } + + public ReachabilityEduLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ReachabilityEduLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public ReachabilityEduLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(ReachabilityEduWindowManager windowManager) { + mWindowManager = windowManager; + } + + void handleVisibility(boolean isActivityLetterboxed, int letterboxVerticalPosition, + int letterboxHorizontalPosition, int availableWidth, int availableHeight, + boolean isDoubleTap) { + // If the app is not letterboxed we hide all the buttons. + if (!mIsLayoutActive || !isActivityLetterboxed || ( + letterboxHorizontalPosition == TaskInfo.PROPERTY_VALUE_UNSET + && letterboxVerticalPosition == TaskInfo.PROPERTY_VALUE_UNSET)) { + hideAllImmediately(); + } else if (letterboxHorizontalPosition != TaskInfo.PROPERTY_VALUE_UNSET) { + handleLetterboxHorizontalPosition(availableWidth, letterboxHorizontalPosition, + isDoubleTap); + } else { + handleLetterboxVerticalPosition(availableHeight, letterboxVerticalPosition, + isDoubleTap); + } + } + + void hideAllImmediately() { + mMoveLeftButton.hide(); + mMoveRightButton.hide(); + mMoveUpButton.hide(); + mMoveDownButton.hide(); + mLastLeftMargin = TaskInfo.PROPERTY_VALUE_UNSET; + mLastRightMargin = TaskInfo.PROPERTY_VALUE_UNSET; + mLastTopMargin = TaskInfo.PROPERTY_VALUE_UNSET; + mLastBottomMargin = TaskInfo.PROPERTY_VALUE_UNSET; + } + + void setIsLayoutActive(boolean isLayoutActive) { + this.mIsLayoutActive = isLayoutActive; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mMoveLeftButton = findViewById(R.id.reachability_move_left_button); + mMoveRightButton = findViewById(R.id.reachability_move_right_button); + mMoveUpButton = findViewById(R.id.reachability_move_up_button); + mMoveDownButton = findViewById(R.id.reachability_move_down_button); + mMoveLeftButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + mMoveRightButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + mMoveUpButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + mMoveDownButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + } + + private Animator marginAnimator(View view, Function<LayoutParams, Integer> marginSupplier, + BiConsumer<LayoutParams, Integer> marginConsumer, int from, int to) { + final LayoutParams layoutParams = ((LayoutParams) view.getLayoutParams()); + ValueAnimator animator = ValueAnimator.ofInt(marginSupplier.apply(layoutParams), from, to); + animator.addUpdateListener(valueAnimator -> { + marginConsumer.accept(layoutParams, (Integer) valueAnimator.getAnimatedValue()); + view.requestLayout(); + }); + animator.setDuration(MARGINS_ANIMATION_DURATION_MS); + return animator; + } + + private void handleLetterboxHorizontalPosition(int availableWidth, + int letterboxHorizontalPosition, boolean isDoubleTap) { + mMoveUpButton.hide(); + mMoveDownButton.hide(); + mLastTopMargin = TaskInfo.PROPERTY_VALUE_UNSET; + mLastBottomMargin = TaskInfo.PROPERTY_VALUE_UNSET; + // We calculate the available space on the left and right + final int horizontalGap = availableWidth / 2; + final int leftAvailableSpace = letterboxHorizontalPosition * horizontalGap; + final int rightAvailableSpace = availableWidth - leftAvailableSpace; + // We show the button if we have enough space + if (leftAvailableSpace >= mMoveLeftButton.getMeasuredWidth()) { + int newLeftMargin = (horizontalGap - mMoveLeftButton.getMeasuredWidth()) / 2; + if (mLastLeftMargin == TaskInfo.PROPERTY_VALUE_UNSET) { + mLastLeftMargin = newLeftMargin; + } + if (mLastLeftMargin != newLeftMargin) { + marginAnimator(mMoveLeftButton, layoutParams -> layoutParams.leftMargin, + (layoutParams, margin) -> layoutParams.leftMargin = margin, + mLastLeftMargin, newLeftMargin).start(); + } else { + final LayoutParams leftParams = ((LayoutParams) mMoveLeftButton.getLayoutParams()); + leftParams.leftMargin = mLastLeftMargin; + mMoveLeftButton.setLayoutParams(leftParams); + } + showItem(mMoveLeftButton, isDoubleTap); + } else { + mMoveLeftButton.hide(); + mLastLeftMargin = TaskInfo.PROPERTY_VALUE_UNSET; + } + if (rightAvailableSpace >= mMoveRightButton.getMeasuredWidth()) { + int newRightMargin = (horizontalGap - mMoveRightButton.getMeasuredWidth()) / 2; + if (mLastRightMargin == TaskInfo.PROPERTY_VALUE_UNSET) { + mLastRightMargin = newRightMargin; + } + if (mLastRightMargin != newRightMargin) { + marginAnimator(mMoveRightButton, layoutParams -> layoutParams.rightMargin, + (layoutParams, margin) -> layoutParams.rightMargin = margin, + mLastRightMargin, newRightMargin).start(); + } else { + final LayoutParams rightParams = + ((LayoutParams) mMoveRightButton.getLayoutParams()); + rightParams.rightMargin = mLastRightMargin; + mMoveRightButton.setLayoutParams(rightParams); + } + showItem(mMoveRightButton, isDoubleTap); + } else { + mMoveRightButton.hide(); + mLastRightMargin = TaskInfo.PROPERTY_VALUE_UNSET; + } + } + + private void handleLetterboxVerticalPosition(int availableHeight, + int letterboxVerticalPosition, boolean isDoubleTap) { + mMoveLeftButton.hide(); + mMoveRightButton.hide(); + mLastLeftMargin = TaskInfo.PROPERTY_VALUE_UNSET; + mLastRightMargin = TaskInfo.PROPERTY_VALUE_UNSET; + // We calculate the available space on the left and right + final int verticalGap = availableHeight / 2; + final int topAvailableSpace = letterboxVerticalPosition * verticalGap; + final int bottomAvailableSpace = availableHeight - topAvailableSpace; + if (topAvailableSpace >= mMoveUpButton.getMeasuredHeight()) { + int newTopMargin = (verticalGap - mMoveUpButton.getMeasuredHeight()) / 2; + if (mLastTopMargin == TaskInfo.PROPERTY_VALUE_UNSET) { + mLastTopMargin = newTopMargin; + } + if (mLastTopMargin != newTopMargin) { + marginAnimator(mMoveUpButton, layoutParams -> layoutParams.topMargin, + (layoutParams, margin) -> layoutParams.topMargin = margin, + mLastTopMargin, newTopMargin).start(); + } else { + final LayoutParams topParams = ((LayoutParams) mMoveUpButton.getLayoutParams()); + topParams.topMargin = mLastTopMargin; + mMoveUpButton.setLayoutParams(topParams); + } + showItem(mMoveUpButton, isDoubleTap); + } else { + mMoveUpButton.hide(); + mLastTopMargin = TaskInfo.PROPERTY_VALUE_UNSET; + } + if (bottomAvailableSpace >= mMoveDownButton.getMeasuredHeight()) { + int newBottomMargin = (verticalGap - mMoveDownButton.getMeasuredHeight()) / 2; + if (mLastBottomMargin == TaskInfo.PROPERTY_VALUE_UNSET) { + mLastBottomMargin = newBottomMargin; + } + if (mLastBottomMargin != newBottomMargin) { + marginAnimator(mMoveDownButton, layoutParams -> layoutParams.bottomMargin, + (layoutParams, margin) -> layoutParams.bottomMargin = margin, + mLastBottomMargin, newBottomMargin).start(); + } else { + final LayoutParams bottomParams = + ((LayoutParams) mMoveDownButton.getLayoutParams()); + bottomParams.bottomMargin = mLastBottomMargin; + mMoveDownButton.setLayoutParams(bottomParams); + } + showItem(mMoveDownButton, isDoubleTap); + } else { + mMoveDownButton.hide(); + mLastBottomMargin = TaskInfo.PROPERTY_VALUE_UNSET; + } + } + + private void showItem(ReachabilityEduHandLayout view, boolean fromDoubleTap) { + if (view.getVisibility() == View.VISIBLE) { + // Already visible we just start animation + view.startAnimation(); + return; + } + view.setVisibility(View.VISIBLE); + final long delay = fromDoubleTap ? VISIBILITY_SHOW_DOUBLE_TAP_ANIMATION_DELAY_MS + : VISIBILITY_SHOW_ANIMATION_DELAY_MS; + AlphaAnimation alphaAnimation = new AlphaAnimation(ALPHA_FULL_TRANSPARENT, + ALPHA_FULL_OPAQUE); + alphaAnimation.setDuration(VISIBILITY_SHOW_ANIMATION_DURATION_MS); + alphaAnimation.setStartOffset(delay); + alphaAnimation.setFillAfter(true); + alphaAnimation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + // We trigger the hand animation + view.setAlpha(ALPHA_FULL_OPAQUE); + view.startAnimation(); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + view.startAnimation(alphaAnimation); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java new file mode 100644 index 000000000000..6223efa831b1 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.compatui.CompatUIController.CompatUICallback; + +/** + * Window manager for the reachability education + */ +class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { + + /** + * The Compat UI should be below the Letterbox Education. + */ + private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1; + + // The time to wait before hiding the education + private static final long DISAPPEAR_DELAY_MS = 4000L; + + private final CompatUICallback mCallback; + + private final CompatUIConfiguration mCompatUIConfiguration; + + private final ShellExecutor mMainExecutor; + + @NonNull + private TaskInfo mTaskInfo; + + private boolean mIsActivityLetterboxed; + + private int mLetterboxVerticalPosition; + + private int mLetterboxHorizontalPosition; + + private int mTopActivityLetterboxWidth; + + private int mTopActivityLetterboxHeight; + + private long mNextHideTime = -1L; + + private boolean mForceUpdate = false; + + // We decided to force the visualization of the double-tap animated icons every time the user + // double-taps. We detect a double-tap checking the previous and current state of + // mLetterboxVerticalPosition and mLetterboxHorizontalPosition saving the result in this + // variable. + private boolean mHasUserDoubleTapped; + + // When the size of the letterboxed app changes and the icons are visible + // we need to animate them. + private boolean mHasLetterboxSizeChanged; + + @Nullable + @VisibleForTesting + ReachabilityEduLayout mLayout; + + ReachabilityEduWindowManager(Context context, TaskInfo taskInfo, + SyncTransactionQueue syncQueue, CompatUICallback callback, + ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, + CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor) { + super(context, taskInfo, syncQueue, taskListener, displayLayout); + mCallback = callback; + mTaskInfo = taskInfo; + mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled; + mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition; + mLetterboxHorizontalPosition = taskInfo.topActivityLetterboxHorizontalPosition; + mTopActivityLetterboxWidth = taskInfo.topActivityLetterboxWidth; + mTopActivityLetterboxHeight = taskInfo.topActivityLetterboxHeight; + mCompatUIConfiguration = compatUIConfiguration; + mMainExecutor = mainExecutor; + } + + @Override + protected int getZOrder() { + return Z_ORDER; + } + + @Override + protected @Nullable View getLayout() { + return mLayout; + } + + @Override + protected void removeLayout() { + mLayout = null; + } + + @Override + protected boolean eligibleToShowLayout() { + return mCompatUIConfiguration.isReachabilityEducationEnabled() + && mIsActivityLetterboxed + && (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1); + } + + @Override + protected View createLayout() { + mLayout = inflateLayout(); + mLayout.inject(this); + + updateVisibilityOfViews(); + + return mLayout; + } + + @VisibleForTesting + ReachabilityEduLayout inflateLayout() { + return (ReachabilityEduLayout) LayoutInflater.from(mContext).inflate( + R.layout.reachability_ui_layout, null); + } + + @Override + public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, + boolean canShow) { + mTaskInfo = taskInfo; + final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed; + final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition; + final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition; + final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth; + final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight; + mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled; + mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition; + mLetterboxHorizontalPosition = taskInfo.topActivityLetterboxHorizontalPosition; + mTopActivityLetterboxWidth = taskInfo.topActivityLetterboxWidth; + mTopActivityLetterboxHeight = taskInfo.topActivityLetterboxHeight; + + mHasUserDoubleTapped = + mLetterboxVerticalPosition != prevLetterboxVerticalPosition + || prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition; + if (mHasUserDoubleTapped) { + // In this case we disable the reachability for the following launch of + // the current application. Anyway because a double tap event happened, + // the reachability education is displayed + mCompatUIConfiguration.setDontShowReachabilityEducationAgain(taskInfo); + } + if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) { + return false; + } + + mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth + || prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight; + + if (mForceUpdate || prevIsActivityLetterboxed != mIsActivityLetterboxed + || prevLetterboxVerticalPosition != mLetterboxVerticalPosition + || prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition + || prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth + || prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight) { + updateVisibilityOfViews(); + mForceUpdate = false; + } + + return true; + } + + void forceUpdate(boolean forceUpdate) { + mForceUpdate = forceUpdate; + } + + @Override + protected void onParentBoundsChanged() { + if (mLayout == null) { + return; + } + // Both the layout dimensions and dialog margins depend on the parent bounds. + WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams(); + mLayout.setLayoutParams(windowLayoutParams); + relayout(windowLayoutParams); + } + + /** Gets the layout params. */ + protected WindowManager.LayoutParams getWindowLayoutParams() { + View layout = getLayout(); + if (layout == null) { + return new WindowManager.LayoutParams(); + } + // Measure how big the hint is since its size depends on the text size. + final Rect taskBounds = getTaskBounds(); + layout.measure(View.MeasureSpec.makeMeasureSpec(taskBounds.width(), + View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(taskBounds.height(), + View.MeasureSpec.EXACTLY)); + return getWindowLayoutParams(layout.getMeasuredWidth(), layout.getMeasuredHeight()); + } + + /** + * @return Flags to use for the WindowManager layout + */ + @Override + protected int getWindowManagerLayoutParamsFlags() { + return FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE; + } + + @Override + @VisibleForTesting + public void updateSurfacePosition() { + if (mLayout == null) { + return; + } + updateSurfacePosition(0, 0); + } + + void updateHideTime() { + mNextHideTime = SystemClock.uptimeMillis() + DISAPPEAR_DELAY_MS; + } + + private void updateVisibilityOfViews() { + if (mLayout == null) { + return; + } + if (shouldUpdateEducation()) { + if (!mHasLetterboxSizeChanged) { + mLayout.setIsLayoutActive(true); + } + int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth; + int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight; + mLayout.handleVisibility(mIsActivityLetterboxed, mLetterboxVerticalPosition, + mLetterboxHorizontalPosition, availableWidth, availableHeight, + mHasUserDoubleTapped); + if (!mHasLetterboxSizeChanged) { + updateHideTime(); + mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS); + } + mHasUserDoubleTapped = false; + } else { + hideReachability(); + } + } + + private void hideReachability() { + if (mLayout != null) { + mLayout.setIsLayoutActive(false); + } + if (mLayout == null || !shouldHideEducation()) { + return; + } + mLayout.hideAllImmediately(); + // We need this in case the icons disappear after the timeout without an explicit + // double tap of the user. + mCompatUIConfiguration.setDontShowReachabilityEducationAgain(mTaskInfo); + } + + private boolean shouldUpdateEducation() { + return mForceUpdate || mHasUserDoubleTapped || mHasLetterboxSizeChanged + || mCompatUIConfiguration.shouldShowReachabilityEducation(mTaskInfo); + } + + private boolean shouldHideEducation() { + return SystemClock.uptimeMillis() >= mNextHideTime; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java index 2440838844c4..aab123a843ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java @@ -130,7 +130,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { protected boolean eligibleToShowLayout() { // We don't show this dialog if the user has explicitly selected so clicking on a checkbox. return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null - || !mCompatUIConfiguration.getDontShowRestartDialogAgain(mTaskInfo)); + || mCompatUIConfiguration.shouldShowRestartDialogAgain(mTaskInfo)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java index d03d075b38af..ff5138d4bb91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java @@ -212,24 +212,25 @@ public class PipSizeSpecHandler { */ @Override public Size getSizeForAspectRatio(Size size, float aspectRatio) { - // getting the percentage of the max size that current size takes float currAspectRatio = (float) size.getWidth() / size.getHeight(); + + // getting the percentage of the max size that current size takes Size currentMaxSize = getMaxSize(currAspectRatio); float currentPercent = (float) size.getWidth() / currentMaxSize.getWidth(); // getting the max size for the target aspect ratio Size updatedMaxSize = getMaxSize(aspectRatio); - int width = (int) (updatedMaxSize.getWidth() * currentPercent); - int height = (int) (updatedMaxSize.getHeight() * currentPercent); + int width = Math.round(updatedMaxSize.getWidth() * currentPercent); + int height = Math.round(updatedMaxSize.getHeight() * currentPercent); // adjust the dimensions if below allowed min edge size if (width < getMinEdgeSize() && aspectRatio <= 1) { width = getMinEdgeSize(); - height = (int) (width / aspectRatio); + height = Math.round(width / aspectRatio); } else if (height < getMinEdgeSize() && aspectRatio > 1) { height = getMinEdgeSize(); - width = (int) (height * aspectRatio); + width = Math.round(height * aspectRatio); } // reduce the dimensions of the updated size to the calculated percentage @@ -365,7 +366,7 @@ public class PipSizeSpecHandler { mContext = context; boolean enablePipSizeLargeScreen = SystemProperties - .getBoolean("persist.wm.debug.enable_pip_size_large_screen", false); + .getBoolean("persist.wm.debug.enable_pip_size_large_screen", true); // choose between two implementations of size spec logic if (enablePipSizeLargeScreen) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index a841b7f96d3c..d6f4d6daaa83 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -220,12 +220,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mCallbacks.onNoLongerSupportMultiWindow(); return; } - mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); + if (taskInfo.topActivity == null && mChildrenTaskInfo.contains(taskInfo.taskId) + && mChildrenTaskInfo.get(taskInfo.taskId).topActivity != null) { + // If top activity become null, it means the task is about to vanish, we use this + // signal to remove it from children list earlier for smooth dismiss transition. + mChildrenTaskInfo.remove(taskInfo.taskId); + mChildrenLeashes.remove(taskInfo.taskId); + } else { + mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); + } mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); - if (!ENABLE_SHELL_TRANSITIONS) { - updateChildTaskSurface( - taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); + if (!ENABLE_SHELL_TRANSITIONS && mChildrenLeashes.contains(taskInfo.taskId)) { + updateChildTaskSurface(taskInfo, mChildrenLeashes.get(taskInfo.taskId), + false /* firstAppeared */); } } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java index 47c9e06e8681..3f79df6a82c8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java @@ -31,14 +31,12 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; -import android.content.Context; -import android.content.SharedPreferences; import android.graphics.Insets; import android.graphics.Rect; import android.testing.AndroidTestingRunner; +import android.util.Pair; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.SurfaceControlViewHost; @@ -53,6 +51,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.SyncTransactionQueue; @@ -67,6 +66,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.function.Consumer; + /** * Tests for {@link LetterboxEduWindowManager}. * @@ -80,8 +81,10 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { private static final int USER_ID_1 = 1; private static final int USER_ID_2 = 2; - private static final String PREF_KEY_1 = String.valueOf(USER_ID_1); - private static final String PREF_KEY_2 = String.valueOf(USER_ID_2); + private static final String TEST_COMPAT_UI_SHARED_PREFERENCES = "test_compat_ui_configuration"; + + private static final String TEST_HAS_SEEN_LETTERBOX_SHARED_PREFERENCES = + "test_has_seen_letterbox"; private static final int TASK_ID = 1; @@ -103,46 +106,34 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private SurfaceControlViewHost mViewHost; @Mock private Transitions mTransitions; - @Mock private Runnable mOnDismissCallback; + @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback; @Mock private DockStateReader mDockStateReader; - private SharedPreferences mSharedPreferences; - @Nullable - private Boolean mInitialPrefValue1 = null; - @Nullable - private Boolean mInitialPrefValue2 = null; + private CompatUIConfiguration mCompatUIConfiguration; + private TestShellExecutor mExecutor; @Before public void setUp() { MockitoAnnotations.initMocks(this); - - mSharedPreferences = mContext.getSharedPreferences( - LetterboxEduWindowManager.HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME, - Context.MODE_PRIVATE); - if (mSharedPreferences.contains(PREF_KEY_1)) { - mInitialPrefValue1 = mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false); - mSharedPreferences.edit().remove(PREF_KEY_1).apply(); - } - if (mSharedPreferences.contains(PREF_KEY_2)) { - mInitialPrefValue2 = mSharedPreferences.getBoolean(PREF_KEY_2, /* default= */ false); - mSharedPreferences.edit().remove(PREF_KEY_2).apply(); - } + mExecutor = new TestShellExecutor(); + mCompatUIConfiguration = new CompatUIConfiguration(mContext, mExecutor) { + + @Override + protected String getCompatUISharedPreferenceName() { + return TEST_COMPAT_UI_SHARED_PREFERENCES; + } + + @Override + protected String getHasSeenLetterboxEducationSharedPreferencedName() { + return TEST_HAS_SEEN_LETTERBOX_SHARED_PREFERENCES; + } + }; } @After public void tearDown() { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - if (mInitialPrefValue1 == null) { - editor.remove(PREF_KEY_1); - } else { - editor.putBoolean(PREF_KEY_1, mInitialPrefValue1); - } - if (mInitialPrefValue2 == null) { - editor.remove(PREF_KEY_2); - } else { - editor.putBoolean(PREF_KEY_2, mInitialPrefValue2); - } - editor.apply(); + mContext.deleteSharedPreferences(TEST_COMPAT_UI_SHARED_PREFERENCES); + mContext.deleteSharedPreferences(TEST_HAS_SEEN_LETTERBOX_SHARED_PREFERENCES); } @Test @@ -166,8 +157,8 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { @Test public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() { - LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ - true, USER_ID_1, /* isTaskbarEduShowing= */ true); + LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true, + USER_ID_1, /* isTaskbarEduShowing= */ true); assertFalse(windowManager.createLayout(/* canShow= */ true)); @@ -180,7 +171,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { assertTrue(windowManager.createLayout(/* canShow= */ false)); - assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + assertFalse(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); assertNull(windowManager.mLayout); } @@ -201,7 +192,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { spyOn(dialogTitle); // The education shouldn't be marked as seen until enter animation is done. - assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + assertFalse(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); // Clicking the layout does nothing until enter animation is done. layout.performClick(); verify(mAnimationController, never()).startExitAnimation(any(), any()); @@ -210,7 +201,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { verifyAndFinishEnterAnimation(layout); - assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + assertFalse(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); verify(dialogTitle).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); // Exit animation should start following a click on the layout. layout.performClick(); @@ -218,13 +209,16 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { // Window manager isn't released until exit animation is done. verify(windowManager, never()).release(); + // After dismissed the user has seen the dialog + assertTrue(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); + // Verify multiple clicks are ignored. layout.performClick(); verifyAndFinishExitAnimation(layout); verify(windowManager).release(); - verify(mOnDismissCallback).run(); + verify(mOnDismissCallback).accept(any()); } @Test @@ -236,7 +230,10 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { assertNotNull(windowManager.mLayout); verifyAndFinishEnterAnimation(windowManager.mLayout); - assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + + // We dismiss + windowManager.mLayout.findViewById(R.id.letterbox_education_dialog_dismiss_button) + .performClick(); windowManager.release(); windowManager = createWindowManager(/* eligible= */ true, @@ -254,7 +251,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { assertNotNull(windowManager.mLayout); verifyAndFinishEnterAnimation(windowManager.mLayout); - assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + assertTrue(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); } @Test @@ -271,7 +268,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { mRunOnIdleCaptor.getValue().run(); verify(mAnimationController, never()).startEnterAnimation(any(), any()); - assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false)); + assertFalse(mCompatUIConfiguration.getHasSeenLetterboxEducation(USER_ID_1)); } @Test @@ -297,7 +294,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { mTaskListener, /* canShow= */ true)); verify(windowManager).release(); - verify(mOnDismissCallback, never()).run(); + verify(mOnDismissCallback, never()).accept(any()); verify(mAnimationController, never()).startExitAnimation(any(), any()); assertNull(windowManager.mLayout); } @@ -395,8 +392,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { } private LetterboxEduWindowManager createWindowManager(boolean eligible, boolean isDocked) { - return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ - false, isDocked); + return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ false, isDocked); } private LetterboxEduWindowManager createWindowManager(boolean eligible, int userId, @@ -410,9 +406,8 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext, createTaskInfo(eligible, userId), mSyncTransactionQueue, mTaskListener, - createDisplayLayout(), mTransitions, mOnDismissCallback, - mAnimationController, mDockStateReader); - + createDisplayLayout(), mTransitions, mOnDismissCallback, mAnimationController, + mDockStateReader, mCompatUIConfiguration); spyOn(windowManager); doReturn(mViewHost).when(windowManager).createSurfaceViewHost(); doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java new file mode 100644 index 000000000000..0be08ba74d86 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduLayoutTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +import android.testing.AndroidTestingRunner; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link LetterboxEduDialogLayout}. + * + * Build/Install/Run: + * atest WMShellUnitTests:ReachabilityEduLayoutTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class ReachabilityEduLayoutTest extends ShellTestCase { + + private ReachabilityEduLayout mLayout; + private View mMoveUpButton; + private View mMoveDownButton; + private View mMoveLeftButton; + private View mMoveRightButton; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mLayout = (ReachabilityEduLayout) LayoutInflater.from(mContext) + .inflate(R.layout.reachability_ui_layout, null); + mMoveLeftButton = mLayout.findViewById(R.id.reachability_move_left_button); + mMoveRightButton = mLayout.findViewById(R.id.reachability_move_right_button); + mMoveUpButton = mLayout.findViewById(R.id.reachability_move_up_button); + mMoveDownButton = mLayout.findViewById(R.id.reachability_move_down_button); + } + + @Test + public void testOnFinishInflate() { + assertNotNull(mMoveUpButton); + assertNotNull(mMoveDownButton); + assertNotNull(mMoveLeftButton); + assertNotNull(mMoveRightButton); + } + + @Test + public void handleVisibility_activityNotLetterboxed_buttonsAreHidden() { + mLayout.handleVisibility(/* isActivityLetterboxed */ false, + /* letterboxVerticalPosition */ -1, /* letterboxHorizontalPosition */ -1, + /* availableWidth */ 0, /* availableHeight */ 0, /* fromDoubleTap */ false); + assertEquals(View.INVISIBLE, mMoveUpButton.getVisibility()); + assertEquals(View.INVISIBLE, mMoveDownButton.getVisibility()); + assertEquals(View.INVISIBLE, mMoveLeftButton.getVisibility()); + assertEquals(View.INVISIBLE, mMoveRightButton.getVisibility()); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java new file mode 100644 index 000000000000..91e1e199719c --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link ReachabilityEduWindowManager}. + * + * Build/Install/Run: + * atest WMShellUnitTests:ReachabilityEduWindowManagerTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class ReachabilityEduWindowManagerTest extends ShellTestCase { + + private static final int USER_ID = 1; + private static final int TASK_ID = 1; + + @Mock + private SyncTransactionQueue mSyncTransactionQueue; + @Mock + private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock + private CompatUIController.CompatUICallback mCallback; + @Mock + private CompatUIConfiguration mCompatUIConfiguration; + @Mock + private DisplayLayout mDisplayLayout; + + private TestShellExecutor mExecutor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mExecutor = new TestShellExecutor(); + } + + @After + public void tearDown() { + } + + @Test + public void testCreateLayout_notEligible_doesNotCreateLayout() { + final ReachabilityEduWindowManager windowManager = createReachabilityEduWindowManager( + createTaskInfo(/* userId= */ USER_ID, /*isLetterboxDoubleTapEnabled */ false)); + + assertFalse(windowManager.createLayout(/* canShow= */ true)); + + assertNull(windowManager.mLayout); + } + + @Test + public void testCreateLayout_letterboxPositionChanged_doubleTapIsDetected() { + // Initial left position + final TaskInfo initialTaskInfo = createTaskInfoForHorizontalTapping(USER_ID, 0, 1000); + final ReachabilityEduWindowManager windowManager = + createReachabilityEduWindowManager(initialTaskInfo); + // Move to the right + final TaskInfo newPositionTaskInfo = createTaskInfoForHorizontalTapping(USER_ID, 1, 1000); + windowManager.updateCompatInfo(newPositionTaskInfo, mTaskListener, /* canShow */ true); + + verify(mCompatUIConfiguration).setDontShowReachabilityEducationAgain(newPositionTaskInfo); + } + + + private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) { + return new ReachabilityEduWindowManager(mContext, taskInfo, + mSyncTransactionQueue, mCallback, mTaskListener, mDisplayLayout, + mCompatUIConfiguration, mExecutor); + } + + private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled) { + return createTaskInfo(userId, /* isLetterboxDoubleTapEnabled */ isLetterboxDoubleTapEnabled, + /* topActivityLetterboxVerticalPosition */ -1, + /* topActivityLetterboxHorizontalPosition */ -1, + /* topActivityLetterboxWidth */ -1, + /* topActivityLetterboxHeight */ -1); + } + + private static TaskInfo createTaskInfoForHorizontalTapping(int userId, + int topActivityLetterboxHorizontalPosition, int topActivityLetterboxWidth) { + return createTaskInfo(userId, /* isLetterboxDoubleTapEnabled */ true, + /* topActivityLetterboxVerticalPosition */ -1, + topActivityLetterboxHorizontalPosition, topActivityLetterboxWidth, + /* topActivityLetterboxHeight */ -1); + } + + private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled, + int topActivityLetterboxVerticalPosition, int topActivityLetterboxHorizontalPosition, + int topActivityLetterboxWidth, int topActivityLetterboxHeight) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.userId = userId; + taskInfo.taskId = TASK_ID; + taskInfo.isLetterboxDoubleTapEnabled = isLetterboxDoubleTapEnabled; + taskInfo.topActivityLetterboxVerticalPosition = topActivityLetterboxVerticalPosition; + taskInfo.topActivityLetterboxHorizontalPosition = topActivityLetterboxHorizontalPosition; + taskInfo.topActivityLetterboxWidth = topActivityLetterboxWidth; + taskInfo.topActivityLetterboxHeight = topActivityLetterboxHeight; + return taskInfo; + } +} diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index a285462eef74..d101a1bdfb37 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -39,6 +39,9 @@ #include "VectorDrawable.h" #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/FunctorDrawable.h" +#ifdef __ANDROID__ +#include "renderthread/CanvasContext.h" +#endif namespace android { namespace uirenderer { @@ -434,7 +437,19 @@ struct DrawPoints final : Op { size_t count; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { - c->drawPoints(mode, count, pod<SkPoint>(this), paint); + if (paint.isAntiAlias()) { + c->drawPoints(mode, count, pod<SkPoint>(this), paint); + } else { + c->save(); +#ifdef __ANDROID__ + auto pixelSnap = renderthread::CanvasContext::getActiveContext()->getPixelSnapMatrix(); + auto transform = c->getLocalToDevice(); + transform.postConcat(pixelSnap); + c->setMatrix(transform); +#endif + c->drawPoints(mode, count, pod<SkPoint>(this), paint); + c->restore(); + } } }; struct DrawVertices final : Op { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index a80c613697f2..fe414201094b 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -53,6 +53,14 @@ public: bool isSurfaceReady() override; bool isContextReady() override; + const SkM44& getPixelSnapMatrix() const override { + // Small (~1/16th) nudge to ensure that pixel-aligned non-AA'd draws fill the + // desired fragment + static const SkScalar kOffset = 0.063f; + static const SkM44 sSnapMatrix = SkM44::Translate(kOffset, kOffset); + return sSnapMatrix; + } + static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); protected: diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index cc2565d88d5e..18e0b91f0253 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -180,6 +180,10 @@ void SkiaVulkanPipeline::onContextDestroyed() { } } +const SkM44& SkiaVulkanPipeline::getPixelSnapMatrix() const { + return mVkSurface->getPixelSnapMatrix(); +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index a6e685d08aeb..7c8f65b87605 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -49,6 +49,7 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; + const SkM44& getPixelSnapMatrix() const override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 602554ab82f3..f56d19bfcea0 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -793,6 +793,10 @@ SkISize CanvasContext::getNextFrameSize() const { return size; } +const SkM44& CanvasContext::getPixelSnapMatrix() const { + return mRenderPipeline->getPixelSnapMatrix(); +} + void CanvasContext::prepareAndDraw(RenderNode* node) { ATRACE_CALL(); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 951ee216ce35..d85e579e371c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -195,6 +195,9 @@ public: SkISize getNextFrameSize() const; + // Returns the matrix to use to nudge non-AA'd points/lines towards the fragment center + const SkM44& getPixelSnapMatrix() const; + // Called when SurfaceStats are available. static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId, ASurfaceControlStats* stats); diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index ef58bc553c23..54adec2cf6f6 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -88,6 +88,8 @@ public: virtual void setPictureCapturedCallback( const std::function<void(sk_sp<SkPicture>&&)>& callback) = 0; + virtual const SkM44& getPixelSnapMatrix() const = 0; + virtual ~IRenderPipeline() {} }; diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 7dd3561cb220..666f32939206 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -63,6 +63,18 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { return SkMatrix::I(); } +static SkM44 GetPixelSnapMatrix(SkISize windowSize, int transform) { + // Small (~1/16th) nudge to ensure that pixel-aligned non-AA'd draws fill the + // desired fragment + static const SkScalar kOffset = 0.063f; + SkMatrix preRotation = GetPreTransformMatrix(windowSize, transform); + SkMatrix invert; + LOG_ALWAYS_FATAL_IF(!preRotation.invert(&invert)); + return SkM44::Translate(kOffset, kOffset) + .postConcat(SkM44(preRotation)) + .preConcat(SkM44(invert)); +} + static bool ConnectAndSetWindowDefaults(ANativeWindow* window) { ATRACE_CALL(); @@ -178,6 +190,8 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode outWindowInfo->preTransform = GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform); + outWindowInfo->pixelSnapMatrix = + GetPixelSnapMatrix(outWindowInfo->size, outWindowInfo->transform); err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { @@ -406,6 +420,7 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { } mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform); + mWindowInfo.pixelSnapMatrix = GetPixelSnapMatrix(mWindowInfo.size, mWindowInfo.transform); } uint32_t idx; diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index beb71b727f51..b8ccf7810b5d 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -45,6 +45,8 @@ public: } const SkMatrix& getCurrentPreTransform() { return mWindowInfo.preTransform; } + const SkM44& getPixelSnapMatrix() const { return mWindowInfo.pixelSnapMatrix; } + private: /* * All structs/methods in this private section are specifically for use by the VulkanManager @@ -101,6 +103,7 @@ private: SkISize actualSize; // transform to be applied to the SkSurface to map the coordinates to the provided transform SkMatrix preTransform; + SkM44 pixelSnapMatrix; }; VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, GrDirectContext* grContext); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 541f69c62a1f..6742ea980ad5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -102,11 +102,6 @@ constructor( } } - override fun onInit() { - mView.setAlphaInDuration(sysuiContext.resources.getInteger( - R.integer.auth_ripple_alpha_in_duration).toLong()) - } - @VisibleForTesting public override fun onViewAttached() { authController.addCallback(authControllerCallback) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index 84094626193d..b0071340cf1a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -54,12 +54,11 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at private var lockScreenColorVal = Color.WHITE private val fadeDuration = 83L private val retractDuration = 400L - private var alphaInDuration: Long = 0 private val dwellShader = DwellRippleShader() private val dwellPaint = Paint() private val rippleShader = RippleShader() private val ripplePaint = Paint() - private var unlockedRippleAnimator: AnimatorSet? = null + private var unlockedRippleAnimator: Animator? = null private var fadeDwellAnimator: Animator? = null private var retractDwellAnimator: Animator? = null private var dwellPulseOutAnimator: Animator? = null @@ -85,12 +84,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at } init { - rippleShader.color = 0xffffffff.toInt() // default color rippleShader.rawProgress = 0f rippleShader.pixelDensity = resources.displayMetrics.density rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH updateRippleFadeParams() ripplePaint.shader = rippleShader + setLockScreenColor(0xffffffff.toInt()) // default color dwellShader.color = 0xffffffff.toInt() // default color dwellShader.progress = 0f @@ -111,10 +110,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at dwellRadius = sensorRadius * 1.5f } - fun setAlphaInDuration(duration: Long) { - alphaInDuration = duration - } - /** * Animate dwell ripple inwards back to radius 0 */ @@ -253,7 +248,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at override fun onAnimationEnd(animation: Animator?) { drawDwell = false - resetRippleAlpha() } }) start() @@ -277,22 +271,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at } } - val alphaInAnimator = ValueAnimator.ofInt(0, 62).apply { - duration = alphaInDuration - addUpdateListener { animator -> - rippleShader.color = ColorUtils.setAlphaComponent( - rippleShader.color, - animator.animatedValue as Int - ) - invalidate() - } - } - - unlockedRippleAnimator = AnimatorSet().apply { - playTogether( - rippleAnimator, - alphaInAnimator - ) + unlockedRippleAnimator = rippleAnimator.apply { addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator?) { drawRipple = true @@ -310,17 +289,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at unlockedRippleAnimator?.start() } - fun resetRippleAlpha() { - rippleShader.color = ColorUtils.setAlphaComponent( - rippleShader.color, - 255 - ) - } - fun setLockScreenColor(color: Int) { lockScreenColorVal = color - rippleShader.color = lockScreenColorVal - resetRippleAlpha() + rippleShader.color = ColorUtils.setAlphaComponent( + lockScreenColorVal, + 62 + ) } fun updateDwellRippleColor(isDozing: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index cc56c488aa4f..2bfd9fd5b963 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -233,7 +233,7 @@ object Flags { /** Whether to inflate the bouncer view on a background thread. */ // TODO(b/272091103): Tracking Bug @JvmField - val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true) + val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = false) /** Whether to inflate the bouncer view on a background thread. */ // TODO(b/273341787): Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt index b49d60dbdde1..dc0de2cef349 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt @@ -39,15 +39,15 @@ constructor( ) { companion object { - @VisibleForTesting val RESTART_NAP_KEY = "restart_nap_after_start" + @VisibleForTesting val RESTART_SLEEP_KEY = "restart_nap_after_start" } private var inited = false val listener = object : StatusBarStateController.StateListener { - override fun onDreamingChanged(isDreaming: Boolean) { - storeSleepState(isDreaming) + override fun onDozingChanged(isDozing: Boolean) { + storeSleepState(isDozing) } } @@ -67,9 +67,13 @@ constructor( fun maybeRestartSleep() { bgExecutor.executeDelayed( { - if (settings.getBool(RESTART_NAP_KEY, false)) { + if (settings.getBool(RESTART_SLEEP_KEY, false)) { Log.d("RestartDozeListener", "Restarting sleep state") - powerManager.wakeUp(systemClock.uptimeMillis()) + powerManager.wakeUp( + systemClock.uptimeMillis(), + PowerManager.WAKE_REASON_APPLICATION, + "RestartDozeListener" + ) powerManager.goToSleep(systemClock.uptimeMillis()) } }, @@ -78,6 +82,6 @@ constructor( } private fun storeSleepState(sleeping: Boolean) { - bgExecutor.execute { settings.putBool(RESTART_NAP_KEY, sleeping) } + bgExecutor.execute { settings.putBool(RESTART_SLEEP_KEY, sleeping) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index 95d38b1fdd82..0a3b3d3796cc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -309,6 +309,10 @@ constructor( /** Tell the bouncer to start the pre hide animation. */ fun startDisappearAnimation(runnable: Runnable) { + if (willRunDismissFromKeyguard()) { + runnable.run() + return + } val finishRunnable = Runnable { runnable.run() repository.setPrimaryStartDisappearAnimation(null) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index b23247c30256..df93d235245c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -81,9 +81,7 @@ constructor( ) .map { if (willRunDismissFromKeyguard) { - ScrimAlpha( - notificationsAlpha = 1f, - ) + ScrimAlpha() } else if (leaveShadeOpen) { ScrimAlpha( behindAlpha = 1f, diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt index 9c7b48d2514d..0176c4273a68 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt @@ -39,7 +39,6 @@ import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -69,7 +68,6 @@ import com.android.systemui.util.time.SystemClock import com.android.systemui.util.traceSection import java.io.PrintWriter import java.util.TreeMap -import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope @@ -95,8 +93,7 @@ constructor( private val mediaHostStatesManager: MediaHostStatesManager, private val activityStarter: ActivityStarter, private val systemClock: SystemClock, - @Main private val mainExecutor: DelayableExecutor, - @Background private val backgroundExecutor: Executor, + @Main executor: DelayableExecutor, private val mediaManager: MediaDataManager, configurationController: ConfigurationController, falsingCollector: FalsingCollector, @@ -253,7 +250,7 @@ constructor( MediaCarouselScrollHandler( mediaCarousel, pageIndicator, - mainExecutor, + executor, this::onSwipeToDismiss, this::updatePageIndicatorLocation, this::updateSeekbarListening, @@ -615,50 +612,10 @@ constructor( MediaPlayerData.visiblePlayerKeys() .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex) if (existingPlayer == null) { - setupNewPlayer(key, data, isSsReactivated, curVisibleMediaKey) - } else { - existingPlayer.bindPlayer(data, key) - MediaPlayerData.addMediaPlayer( - key, - data, - existingPlayer, - systemClock, - isSsReactivated, - debugLogger - ) - val packageName = MediaPlayerData.smartspaceMediaData?.packageName ?: String() - // In case of recommendations hits. - // Check the playing status of media player and the package name. - // To make sure we scroll to the right app's media player. - if ( - isReorderingAllowed || - shouldScrollToKey && - data.isPlaying == true && - packageName == data.packageName - ) { - reorderAllPlayers(curVisibleMediaKey, key) - } else { - needsReordering = true - } - updatePageIndicator() - mediaCarouselScrollHandler.onPlayersChanged() - mediaFrame.requiresRemeasuring = true - } - return existingPlayer == null - } - - private fun setupNewPlayer( - key: String, - data: MediaData, - isSsReactivated: Boolean, - curVisibleMediaKey: MediaPlayerData.MediaSortKey?, - ) { - backgroundExecutor.execute { - val mediaViewHolder = createMediaViewHolderInBg() - // Add the new player in the main thread. - mainExecutor.execute { val newPlayer = mediaControlPanelFactory.get() - newPlayer.attachPlayer(mediaViewHolder) + newPlayer.attachPlayer( + MediaViewHolder.create(LayoutInflater.from(context), mediaContent) + ) newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams( @@ -688,16 +645,36 @@ constructor( } else { needsReordering = true } - updatePageIndicator() - mediaCarouselScrollHandler.onPlayersChanged() - mediaFrame.requiresRemeasuring = true + } else { + existingPlayer.bindPlayer(data, key) + MediaPlayerData.addMediaPlayer( + key, + data, + existingPlayer, + systemClock, + isSsReactivated, + debugLogger + ) + val packageName = MediaPlayerData.smartspaceMediaData?.packageName ?: String() + // In case of recommendations hits. + // Check the playing status of media player and the package name. + // To make sure we scroll to the right app's media player. + if ( + isReorderingAllowed || + shouldScrollToKey && + data.isPlaying == true && + packageName == data.packageName + ) { + reorderAllPlayers(curVisibleMediaKey, key) + } else { + needsReordering = true + } } + updatePageIndicator() + mediaCarouselScrollHandler.onPlayersChanged() + mediaFrame.requiresRemeasuring = true + return existingPlayer == null } - } - - private fun createMediaViewHolderInBg(): MediaViewHolder { - return MediaViewHolder.create(LayoutInflater.from(context), mediaContent) - } private fun addSmartspaceMediaRecommendations( key: String, @@ -731,14 +708,15 @@ constructor( debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey) } } + val newRecs = mediaControlPanelFactory.get() - val recommendationViewHolder = + newRecs.attachRecommendation( RecommendationViewHolder.create( LayoutInflater.from(context), mediaContent, mediaFlags.isRecommendationCardUpdateEnabled() ) - newRecs.attachRecommendation(recommendationViewHolder) + ) newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams( @@ -762,6 +740,17 @@ constructor( reorderAllPlayers(curVisibleMediaKey) updatePageIndicator() mediaFrame.requiresRemeasuring = true + // Check postcondition: mediaContent should have the same number of children as there + // are + // elements in mediaPlayers. + if (MediaPlayerData.players().size != mediaContent.childCount) { + Log.e( + TAG, + "Size of players list and number of views in carousel are out of sync. " + + "Players size is ${MediaPlayerData.players().size}. " + + "View count is ${mediaContent.childCount}." + ) + } } fun removePlayer( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index c91f855a853d..36eab3949318 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -646,12 +646,15 @@ public class MediaControlPanel { } else { mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId); if (device.getIntent() != null) { - if (device.getIntent().isActivity()) { - mActivityStarter.startActivity( - device.getIntent().getIntent(), true); + PendingIntent deviceIntent = device.getIntent(); + boolean showOverLockscreen = mKeyguardStateController.isShowing() + && mActivityIntentHelper.wouldPendingShowOverLockscreen( + deviceIntent, mLockscreenUserManager.getCurrentUserId()); + if (deviceIntent.isActivity() && !showOverLockscreen) { + mActivityStarter.postStartActivityDismissingKeyguard(deviceIntent); } else { try { - device.getIntent().send(); + deviceIntent.send(); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Device pending intent was canceled"); } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt index 9bccb7df4ed0..89f66b7daaf8 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt @@ -19,12 +19,11 @@ package com.android.systemui.mediaprojection.appselector.view import android.content.Context import android.content.res.Configuration import android.graphics.Rect +import android.view.WindowInsets.Type import android.view.WindowManager -import com.android.internal.R as AndroidR import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen -import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener @@ -62,17 +61,12 @@ constructor( val width = windowMetrics.bounds.width() var height = maximumWindowHeight - // TODO(b/271410803): Read isTransientTaskbar from Launcher val isLargeScreen = isLargeScreen(context) - val isTransientTaskbar = - QuickStepContract.isGesturalMode( - context.resources.getInteger( - com.android.internal.R.integer.config_navBarInteractionMode - ) - ) - if (isLargeScreen && !isTransientTaskbar) { + if (isLargeScreen) { val taskbarSize = - context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height) + windowManager.currentWindowMetrics.windowInsets + .getInsets(Type.tappableElement()) + .bottom height -= taskbarSize } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index c4784d6211bb..ad8ae75edc23 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -961,6 +961,7 @@ public final class NotificationPanelViewController implements Dumpable { onTrackingStopped(false); instantCollapse(); } else { + mView.animate().cancel(); mView.animate() .alpha(0f) .setStartDelay(0) @@ -3087,7 +3088,9 @@ public final class NotificationPanelViewController implements Dumpable { */ public void startFoldToAodAnimation(Runnable startAction, Runnable endAction, Runnable cancelAction) { - mView.animate() + final ViewPropertyAnimator viewAnimator = mView.animate(); + viewAnimator.cancel(); + viewAnimator .translationX(0) .alpha(1f) .setDuration(ANIMATION_DURATION_FOLD_TO_AOD) @@ -3106,9 +3109,14 @@ public final class NotificationPanelViewController implements Dumpable { @Override public void onAnimationEnd(Animator animation) { endAction.run(); + + viewAnimator.setListener(null); + viewAnimator.setUpdateListener(null); } - }).setUpdateListener(anim -> mKeyguardStatusViewController.animateFoldToAod( - anim.getAnimatedFraction())).start(); + }) + .setUpdateListener(anim -> + mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction())) + .start(); } /** Cancels fold to AOD transition and resets view state. */ @@ -3307,6 +3315,7 @@ public final class NotificationPanelViewController implements Dumpable { } public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { + mView.animate().cancel(); return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( endAction); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index 5adb58bca886..63179dac7b8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -77,12 +77,6 @@ public class CrossFadeHelper { */ public static void fadeOut(View view, float fadeOutAmount, boolean remap) { view.animate().cancel(); - - // Don't fade out if already not visible. - if (view.getAlpha() == 0.0f) { - return; - } - if (fadeOutAmount == 1.0f && view.getVisibility() != View.GONE) { view.setVisibility(View.INVISIBLE); } else if (view.getVisibility() == View.INVISIBLE) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt index 2db4596c80a7..e287f19b2455 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt @@ -21,12 +21,15 @@ import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.anyLong import org.mockito.Mockito.never @@ -58,36 +61,37 @@ class RestartDozeListenerTest : SysuiTestCase() { } @Test - fun testStoreDreamState_onDreamingStarted() { - listener.onDreamingChanged(true) + fun testStoreDreamState_onDozingStarted() { + listener.onDozingChanged(true) executor.runAllReady() - assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isTrue() + assertThat(settings.getBool(RestartDozeListener.RESTART_SLEEP_KEY)).isTrue() } @Test - fun testStoreDreamState_onDreamingStopped() { - listener.onDreamingChanged(false) + fun testStoreDozeState_onDozingStopped() { + listener.onDozingChanged(false) executor.runAllReady() - assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isFalse() + assertThat(settings.getBool(RestartDozeListener.RESTART_SLEEP_KEY)).isFalse() } @Test - fun testRestoreDreamState_dreamingShouldStart() { - settings.putBool(RestartDozeListener.RESTART_NAP_KEY, true) + fun testRestoreDozeState_dozingShouldStart() { + settings.putBool(RestartDozeListener.RESTART_SLEEP_KEY, true) restartDozeListener.maybeRestartSleep() executor.advanceClockToLast() executor.runAllReady() - verify(powerManager).wakeUp(clock.uptimeMillis()) + verify(powerManager) + .wakeUp(eq(clock.uptimeMillis()), eq(PowerManager.WAKE_REASON_APPLICATION), anyString()) verify(powerManager).goToSleep(clock.uptimeMillis()) } @Test - fun testRestoreDreamState_dreamingShouldNot() { - settings.putBool(RestartDozeListener.RESTART_NAP_KEY, false) + fun testRestoreDozeState_dozingShouldNotStart() { + settings.putBool(RestartDozeListener.RESTART_SLEEP_KEY, false) restartDozeListener.maybeRestartSleep() executor.advanceClockToLast() executor.runAllReady() - verify(powerManager, never()).wakeUp(anyLong()) + verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString()) verify(powerManager, never()).goToSleep(anyLong()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt index 5ec6283f3de0..bdc33f45c717 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -50,7 +51,6 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -90,9 +90,9 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { keyguardUpdateMonitor, keyguardBypassController, ) - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) - `when`(repository.primaryBouncerShow.value).thenReturn(false) - `when`(bouncerView.delegate).thenReturn(bouncerViewDelegate) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) + whenever(repository.primaryBouncerShow.value).thenReturn(false) + whenever(bouncerView.delegate).thenReturn(bouncerViewDelegate) resources = context.orCreateTestableResources } @@ -118,7 +118,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testShow_keyguardIsDone() { - `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true) + whenever(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true) verify(keyguardStateController, never()).notifyPrimaryBouncerShowing(true) verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow() } @@ -135,7 +135,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion() { - `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) + whenever(repository.panelExpansionAmount.value).thenReturn(0.5f) underTest.setPanelExpansion(0.6f) verify(repository).setPanelExpansion(0.6f) verify(mPrimaryBouncerCallbackInteractor).dispatchExpansionChanged(0.6f) @@ -143,8 +143,8 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion_fullyShown() { - `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) + whenever(repository.panelExpansionAmount.value).thenReturn(0.5f) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) underTest.setPanelExpansion(EXPANSION_VISIBLE) verify(falsingCollector).onBouncerShown() verify(mPrimaryBouncerCallbackInteractor).dispatchFullyShown() @@ -152,8 +152,8 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion_fullyHidden() { - `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) + whenever(repository.panelExpansionAmount.value).thenReturn(0.5f) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) underTest.setPanelExpansion(EXPANSION_HIDDEN) verify(repository).setPrimaryShow(false) verify(falsingCollector).onBouncerHidden() @@ -163,7 +163,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion_startingToHide() { - `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) + whenever(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) underTest.setPanelExpansion(0.1f) verify(repository).setPrimaryStartingToHide(true) verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToHide() @@ -228,7 +228,21 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { } @Test - fun testStartDisappearAnimation() { + fun testStartDisappearAnimation_willRunDismissFromKeyguard() { + whenever(bouncerViewDelegate.willRunDismissFromKeyguard()).thenReturn(true) + + val runnable = mock(Runnable::class.java) + underTest.startDisappearAnimation(runnable) + // End runnable should run immediately + verify(runnable).run() + // ... while the disappear animation should never be run + verify(repository, never()).setPrimaryStartDisappearAnimation(any(Runnable::class.java)) + } + + @Test + fun testStartDisappearAnimation_willNotRunDismissFromKeyguard_() { + whenever(bouncerViewDelegate.willRunDismissFromKeyguard()).thenReturn(false) + val runnable = mock(Runnable::class.java) underTest.startDisappearAnimation(runnable) verify(repository).setPrimaryStartDisappearAnimation(any(Runnable::class.java)) @@ -236,45 +250,45 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testIsFullShowing() { - `when`(repository.primaryBouncerShow.value).thenReturn(true) - `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) + whenever(repository.primaryBouncerShow.value).thenReturn(true) + whenever(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) assertThat(underTest.isFullyShowing()).isTrue() - `when`(repository.primaryBouncerShow.value).thenReturn(false) + whenever(repository.primaryBouncerShow.value).thenReturn(false) assertThat(underTest.isFullyShowing()).isFalse() } @Test fun testIsScrimmed() { - `when`(repository.primaryBouncerScrimmed.value).thenReturn(true) + whenever(repository.primaryBouncerScrimmed.value).thenReturn(true) assertThat(underTest.isScrimmed()).isTrue() - `when`(repository.primaryBouncerScrimmed.value).thenReturn(false) + whenever(repository.primaryBouncerScrimmed.value).thenReturn(false) assertThat(underTest.isScrimmed()).isFalse() } @Test fun testIsInTransit() { - `when`(repository.primaryBouncerShowingSoon.value).thenReturn(true) + whenever(repository.primaryBouncerShowingSoon.value).thenReturn(true) assertThat(underTest.isInTransit()).isTrue() - `when`(repository.primaryBouncerShowingSoon.value).thenReturn(false) + whenever(repository.primaryBouncerShowingSoon.value).thenReturn(false) assertThat(underTest.isInTransit()).isFalse() - `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) + whenever(repository.panelExpansionAmount.value).thenReturn(0.5f) assertThat(underTest.isInTransit()).isTrue() } @Test fun testIsAnimatingAway() { - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {}) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {}) assertThat(underTest.isAnimatingAway()).isTrue() - `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) + whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) assertThat(underTest.isAnimatingAway()).isFalse() } @Test fun testWillDismissWithAction() { - `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true) + whenever(bouncerViewDelegate.willDismissWithActions()).thenReturn(true) assertThat(underTest.willDismissWithAction()).isTrue() - `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false) + whenever(bouncerViewDelegate.willDismissWithActions()).thenReturn(false) assertThat(underTest.willDismissWithAction()).isFalse() } @@ -363,12 +377,13 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { isUnlockingWithFpAllowed: Boolean, isAnimatingAway: Boolean ) { - `when`(repository.primaryBouncerShow.value).thenReturn(isVisible) + whenever(repository.primaryBouncerShow.value).thenReturn(isVisible) resources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, sfpsEnabled) - `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(fpsDetectionRunning) - `when`(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) + .thenReturn(fpsDetectionRunning) + whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) .thenReturn(isUnlockingWithFpAllowed) - `when`(repository.primaryBouncerStartingDisappearAnimation.value) + whenever(repository.primaryBouncerStartingDisappearAnimation.value) .thenReturn(if (isAnimatingAway) Runnable {} else null) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 746f66881a88..98794fd4de0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -115,7 +115,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { repository.sendTransitionStep(step(1f)) assertThat(values.size).isEqualTo(4) - values.forEach { assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f)) } + values.forEach { assertThat(it).isEqualTo(ScrimAlpha()) } job.cancel() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt index e0ca90ec2c58..a72634bcb807 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.media.controls.ui import android.app.PendingIntent -import android.content.res.ColorStateList +import android.content.res.Configuration import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.util.MathUtils.abs @@ -26,9 +26,9 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -49,7 +49,7 @@ import com.android.systemui.qs.PageIndicator import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock @@ -89,6 +89,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Mock lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock lateinit var mediaHostState: MediaHostState @Mock lateinit var activityStarter: ActivityStarter + @Mock @Main private lateinit var executor: DelayableExecutor @Mock lateinit var mediaDataManager: MediaDataManager @Mock lateinit var configurationController: ConfigurationController @Mock lateinit var falsingCollector: FalsingCollector @@ -112,15 +113,11 @@ class MediaCarouselControllerTest : SysuiTestCase() { private val clock = FakeSystemClock() private lateinit var mediaCarouselController: MediaCarouselController - private lateinit var mainExecutor: FakeExecutor - private lateinit var backgroundExecutor: FakeExecutor @Before fun setup() { MockitoAnnotations.initMocks(this) transitionRepository = FakeKeyguardTransitionRepository() - mainExecutor = FakeExecutor(clock) - backgroundExecutor = FakeExecutor(clock) mediaCarouselController = MediaCarouselController( context, @@ -129,8 +126,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaHostStatesManager, activityStarter, clock, - mainExecutor, - backgroundExecutor, + executor, mediaDataManager, configurationController, falsingCollector, @@ -405,7 +401,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { resumption = true ) ) - runAllReady() assertEquals( MediaPlayerData.getMediaPlayerIndex("paused local"), @@ -515,8 +510,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { false ) mediaCarouselController.shouldScrollToKey = true - runAllReady() - // switching between media players. listener.value.onMediaDataLoaded( "playing local", @@ -538,7 +531,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { resumption = false ) ) - runAllReady() assertEquals( MediaPlayerData.getMediaPlayerIndex("paused local"), @@ -563,7 +555,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { resumption = false ) ) - runAllReady() var playerIndex = MediaPlayerData.getMediaPlayerIndex("playing local") assertEquals( @@ -586,8 +577,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { packageName = "PACKAGE_NAME" ) ) - runAllReady() - playerIndex = MediaPlayerData.getMediaPlayerIndex("playing local") assertEquals(playerIndex, 0) } @@ -684,38 +673,17 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @Test - fun testOnUiModeChanged_playersAreAddedBack() { - mediaCarouselController.pageIndicator = pageIndicator - + fun testOnConfigChanged_playersAreAddedBack() { listener.value.onMediaDataLoaded( - "paused local", + "playing local", null, DATA.copy( active = true, - isPlaying = false, + isPlaying = true, playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false ) ) - runAllReady() - - val playersSize = MediaPlayerData.players().size - configListener.value.onUiModeChanged() - runAllReady() - - verify(pageIndicator).tintList = - ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator)) - assertEquals(playersSize, MediaPlayerData.players().size) - assertEquals( - MediaPlayerData.getMediaPlayerIndex("paused local"), - mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex - ) - } - - @Test - fun testOnDensityOrFontScaleChanged_playersAreAddedBack() { - mediaCarouselController.pageIndicator = pageIndicator - listener.value.onMediaDataLoaded( "paused local", null, @@ -726,46 +694,14 @@ class MediaCarouselControllerTest : SysuiTestCase() { resumption = false ) ) - runAllReady() val playersSize = MediaPlayerData.players().size - configListener.value.onDensityOrFontScaleChanged() - runAllReady() - - verify(pageIndicator).tintList = - ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator)) - assertEquals(playersSize, MediaPlayerData.players().size) - assertEquals( - MediaPlayerData.getMediaPlayerIndex("paused local"), - mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex - ) - } - @Test - fun testOnThemeChanged_playersAreAddedBack() { - mediaCarouselController.pageIndicator = pageIndicator + configListener.value.onConfigChanged(Configuration()) - listener.value.onMediaDataLoaded( - "paused local", - null, - DATA.copy( - active = true, - isPlaying = false, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false - ) - ) - runAllReady() - - val playersSize = MediaPlayerData.players().size - configListener.value.onThemeChanged() - runAllReady() - - verify(pageIndicator).tintList = - ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator)) assertEquals(playersSize, MediaPlayerData.players().size) assertEquals( - MediaPlayerData.getMediaPlayerIndex("paused local"), + MediaPlayerData.getMediaPlayerIndex("playing local"), mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex ) } @@ -896,9 +832,4 @@ class MediaCarouselControllerTest : SysuiTestCase() { // Verify that seekbar listening attribute in media control panel is set to false. verify(panel, times(MediaPlayerData.players().size)).listening = false } - - private fun runAllReady() { - backgroundExecutor.runAllReady() - mainExecutor.runAllReady() - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index df13fddc5a28..eba059137511 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -2350,6 +2350,48 @@ public class MediaControlPanelTest : SysuiTestCase() { } } + @Test + fun outputSwitcher_hasCustomIntent_openOverLockscreen() { + // When the device for a media player has an intent that opens over lockscreen + val pendingIntent = mock(PendingIntent::class.java) + whenever(pendingIntent.isActivity).thenReturn(true) + whenever(keyguardStateController.isShowing).thenReturn(true) + whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true) + + val customDevice = device.copy(intent = pendingIntent) + val dataWithDevice = mediaData.copy(device = customDevice) + player.attachPlayer(viewHolder) + player.bindPlayer(dataWithDevice, KEY) + + // When the user taps on the output switcher, + seamless.callOnClick() + + // Then we send the pending intent as is, without modifying the original intent + verify(pendingIntent).send() + verify(pendingIntent, never()).getIntent() + } + + @Test + fun outputSwitcher_hasCustomIntent_requiresUnlock() { + // When the device for a media player has an intent that cannot open over lockscreen + val pendingIntent = mock(PendingIntent::class.java) + whenever(pendingIntent.isActivity).thenReturn(true) + whenever(keyguardStateController.isShowing).thenReturn(true) + whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())) + .thenReturn(false) + + val customDevice = device.copy(intent = pendingIntent) + val dataWithDevice = mediaData.copy(device = customDevice) + player.attachPlayer(viewHolder) + player.bindPlayer(dataWithDevice, KEY) + + // When the user taps on the output switcher, + seamless.callOnClick() + + // Then we request keyguard dismissal + verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent)) + } + private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener = withArgCaptor { verify(seekBarViewModel).setScrubbingChangeListener(capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt index 464acb68fb07..01ffdcd580c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt @@ -19,12 +19,14 @@ package com.android.systemui.mediaprojection.appselector.view import android.content.Context import android.content.res.Configuration import android.content.res.Resources +import android.graphics.Insets import android.graphics.Rect import android.util.DisplayMetrics.DENSITY_DEFAULT +import android.view.WindowInsets import android.view.WindowManager import android.view.WindowMetrics +import androidx.core.view.WindowInsetsCompat.Type import androidx.test.filters.SmallTest -import com.android.internal.R import com.android.systemui.SysuiTestCase import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.statusbar.policy.FakeConfigurationController @@ -94,7 +96,13 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { } private fun givenTaskbarSize(size: Int) { - whenever(resources.getDimensionPixelSize(eq(R.dimen.taskbar_frame_height))).thenReturn(size) + val windowInsets = + WindowInsets.Builder() + .setInsets(Type.tappableElement(), Insets.of(Rect(0, 0, 0, size))) + .build() + val windowMetrics = WindowMetrics(windowManager.maximumWindowMetrics.bounds, windowInsets) + whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics) + whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics) } private fun givenDisplay(width: Int, height: Int, isTablet: Boolean = false) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 99cf8d0ebe93..7087c0135998 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -24,7 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; @@ -184,6 +186,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock protected KeyguardBottomAreaView mKeyguardBottomArea; @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; + @Mock protected ViewPropertyAnimator mViewPropertyAnimator; @Mock protected KeyguardBottomAreaView mQsFrame; @Mock protected HeadsUpManagerPhone mHeadsUpManager; @Mock protected NotificationShelfController mNotificationShelfController; @@ -357,7 +360,14 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { .thenReturn(mHeadsUpCallback); when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); - when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(mKeyguardBottomArea.animate()).thenReturn(mViewPropertyAnimator); + when(mView.animate()).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.translationX(anyFloat())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setInterpolator(any())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setListener(any())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setUpdateListener(any())).thenReturn(mViewPropertyAnimator); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 4517596e8b02..b868018b7dda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -42,6 +42,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -674,6 +676,24 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @Test + public void testFoldToAodAnimationCleansupInAnimationEnd() { + ArgumentCaptor<Animator.AnimatorListener> animCaptor = + ArgumentCaptor.forClass(Animator.AnimatorListener.class); + ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> updateCaptor = + ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); + + // Start fold animation & Capture Listeners + mNotificationPanelViewController.startFoldToAodAnimation(() -> {}, () -> {}, () -> {}); + verify(mViewPropertyAnimator).setListener(animCaptor.capture()); + verify(mViewPropertyAnimator).setUpdateListener(updateCaptor.capture()); + + // End animation and validate listeners were unset + animCaptor.getValue().onAnimationEnd(null); + verify(mViewPropertyAnimator).setListener(null); + verify(mViewPropertyAnimator).setUpdateListener(null); + } + + @Test public void testExpandWithQsMethodIsUsingLockscreenTransitionController() { enableSplitShade(/* enabled= */ true); mStatusBarStateController.setState(KEYGUARD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 8476d0d45603..bf54d4297ad8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -16,6 +16,9 @@ package com.android.systemui.unfold.updates +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources import android.os.Handler import android.testing.AndroidTestingRunner import androidx.core.util.Consumer @@ -33,6 +36,7 @@ import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenLis import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor import org.junit.Before @@ -49,20 +53,19 @@ import org.mockito.MockitoAnnotations @SmallTest class DeviceFoldStateProviderTest : SysuiTestCase() { - @Mock - private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider + @Mock private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider - @Mock - private lateinit var handler: Handler + @Mock private lateinit var handler: Handler - @Mock - private lateinit var rotationChangeProvider: RotationChangeProvider + @Mock private lateinit var rotationChangeProvider: RotationChangeProvider - @Mock - private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider + @Mock private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider - @Captor - private lateinit var rotationListener: ArgumentCaptor<RotationListener> + @Mock private lateinit var resources: Resources + + @Mock private lateinit var context: Context + + @Captor private lateinit var rotationListener: ArgumentCaptor<RotationListener> private val foldProvider = TestFoldProvider() private val screenOnStatusProvider = TestScreenOnStatusProvider() @@ -81,10 +84,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { - override val halfFoldedTimeoutMillis: Int - get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() - } + val config = + object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { + override val halfFoldedTimeoutMillis: Int + get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() + } + whenever(context.resources).thenReturn(resources) + whenever(context.mainExecutor).thenReturn(mContext.mainExecutor) foldStateProvider = DeviceFoldStateProvider( @@ -95,6 +101,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { activityTypeProvider, unfoldKeyguardVisibilityProvider, rotationChangeProvider, + context, context.mainExecutor, handler ) @@ -112,7 +119,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onUnfoldedScreenAvailable() { unfoldedScreenAvailabilityUpdates.add(Unit) } - }) + } + ) foldStateProvider.start() verify(rotationChangeProvider).addCallback(capture(rotationListener)) @@ -134,6 +142,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { // By default, we're on launcher. setupForegroundActivityType(isHomeActivity = true) + setIsLargeScreen(true) } @Test @@ -181,7 +190,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(10) assertThat(foldUpdates) - .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @@ -386,8 +395,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent( - START_CLOSING_ON_APPS_THRESHOLD_DEGREES - - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - + 1 + ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -429,8 +440,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent( - START_CLOSING_ON_APPS_THRESHOLD_DEGREES - - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - + 1 + ) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -470,7 +483,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(130) sendHingeAngleEvent(120) assertThat(foldUpdates) - .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) } @Test @@ -531,8 +544,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { rotationListener.value.onRotationChanged(1) - assertThat(foldUpdates).containsExactly( - FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN) } @Test @@ -545,6 +558,45 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED) } + @Test + fun onFolding_onSmallScreen_tansitionDoesNotStart() { + setIsLargeScreen(false) + + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(100) + + assertThat(foldUpdates).isEmpty() + } + + @Test + fun onFolding_onLargeScreen_tansitionStarts() { + setIsLargeScreen(true) + + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(100) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun onUnfold_onSmallScreen_emitsStartOpening() { + // the new display state might arrive later, so it shouldn't be used to decide to send the + // start opening event, but only for the closing. + setFoldState(folded = true) + setIsLargeScreen(false) + foldUpdates.clear() + + setFoldState(folded = false) + screenOnStatusProvider.notifyScreenTurningOn() + sendHingeAngleEvent(10) + sendHingeAngleEvent(20) + screenOnStatusProvider.notifyScreenTurnedOn() + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING) + } + private fun setupForegroundActivityType(isHomeActivity: Boolean?) { whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity) } @@ -566,6 +618,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { foldProvider.notifyFolded(folded) } + private fun setIsLargeScreen(isLargeScreen: Boolean) { + val smallestScreenWidth = if (isLargeScreen) { 601 } else { 10 } + val configuration = Configuration() + configuration.smallestScreenWidthDp = smallestScreenWidth + whenever(resources.configuration).thenReturn(configuration) + } + private fun fireScreenOnEvent() { screenOnStatusProvider.notifyScreenTurnedOn() } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt index 2044f05664d0..380c1fcbf732 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt @@ -53,4 +53,4 @@ class ScreenSizeFoldProvider(private val context: Context) : FoldProvider { } } -private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 +internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index d653fc7beff2..a633a5e41882 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -15,12 +15,14 @@ */ package com.android.systemui.unfold.updates +import android.content.Context import android.os.Handler import android.os.Trace import android.util.Log import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.core.util.Consumer +import com.android.systemui.unfold.compat.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate @@ -45,6 +47,7 @@ constructor( private val activityTypeProvider: CurrentActivityTypeProvider, private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider, private val rotationChangeProvider: RotationChangeProvider, + private val context: Context, @UnfoldMain private val mainExecutor: Executor, @UnfoldMain private val handler: Handler ) : FoldStateProvider { @@ -119,7 +122,7 @@ constructor( "lastHingeAngle: $lastHingeAngle, " + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" ) - Trace.setCounter( "hinge_angle", angle.toLong()) + Trace.setCounter("hinge_angle", angle.toLong()) } val currentDirection = @@ -136,6 +139,7 @@ constructor( val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val screenAvailableEventSent = isUnfoldHandled + val isOnLargeScreen = isOnLargeScreen() if ( angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle @@ -144,7 +148,9 @@ constructor( // angle range as closing threshold could overlap this range screenAvailableEventSent && // do not send transition event if we are still in the // process of turning on the inner display - isClosingThresholdMet(angle) // hinge angle is below certain threshold. + isClosingThresholdMet(angle) && // hinge angle is below certain threshold. + isOnLargeScreen // Avoids sending closing event when on small screen. + // Start event is sent regardless due to hall sensor. ) { notifyFoldUpdate(transitionUpdate, lastHingeAngle) } @@ -233,7 +239,7 @@ constructor( } private fun cancelAnimation(): Unit = - notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) + notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener { @@ -261,6 +267,11 @@ constructor( } } + private fun isOnLargeScreen(): Boolean { + return context.resources.configuration.smallestScreenWidthDp > + INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + } + /** While the screen is off or the device is folded, hinge angle updates are not needed. */ private fun updateHingeAngleProviderState() { if (isScreenOn && !isFolded) { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index ff1a495edcbb..802574828d15 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -168,6 +168,7 @@ public final class ContentCaptureManagerService extends @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs; @GuardedBy("mLock") int mDevCfgLogHistorySize; @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs; + @GuardedBy("mLock") boolean mDevCfgDisableFlushForViewTreeAppearing; private final Executor mDataShareExecutor = Executors.newCachedThreadPool(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -358,6 +359,8 @@ public final class ContentCaptureManagerService extends case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING: setFineTuneParamsFromDeviceConfig(); return; default: @@ -387,13 +390,20 @@ public final class ContentCaptureManagerService extends DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + mDevCfgDisableFlushForViewTreeAppearing = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + false); if (verbose) { Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): " + "bufferSize=" + mDevCfgMaxBufferSize + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs + ", logHistory=" + mDevCfgLogHistorySize - + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs); + + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs + + ", disableFlushForViewTreeAppearing=" + + mDevCfgDisableFlushForViewTreeAppearing); } } } @@ -628,6 +638,7 @@ public final class ContentCaptureManagerService extends } @Override // from AbstractMasterSystemService + @GuardedBy("mLock") protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); @@ -645,6 +656,8 @@ public final class ContentCaptureManagerService extends pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize); pw.print(prefix2); pw.print("idleUnbindTimeoutMs: "); pw.println(mDevCfgIdleUnbindTimeoutMs); + pw.print(prefix2); pw.print("disableFlushForViewTreeAppearing: "); + pw.println(mDevCfgDisableFlushForViewTreeAppearing); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); } @@ -1003,12 +1016,15 @@ public final class ContentCaptureManagerService extends return null; } - final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, - mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, - mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, - whitelistedComponents); - if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); - return options; + synchronized (mLock) { + final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, + mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, + mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, + mDevCfgDisableFlushForViewTreeAppearing, + whitelistedComponents); + if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); + return options; + } } @Override diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 425158195940..1dc0942ceac5 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4893,10 +4893,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); final long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4942,7 +4938,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d11413937f8d..4df34cafc38b 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6751,7 +6751,8 @@ public class NotificationManagerService extends SystemService { } // Ensure MediaStyle has correct permissions for remote device extras - if (notification.isStyle(Notification.MediaStyle.class)) { + if (notification.isStyle(Notification.MediaStyle.class) + || notification.isStyle(Notification.DecoratedMediaCustomViewStyle.class)) { int hasMediaContentControlPermission = mPackageManager.checkPermission( android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId); if (hasMediaContentControlPermission != PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index 4b8a5b7fcf56..26f781f62078 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -861,7 +861,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { private static void getLetterBoxBounds(WindowState windowState, Region outRegion) { final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets(); - final Rect nonLetterboxRect = windowState.getBounds(); + final Rect nonLetterboxRect = new Rect(windowState.getBounds()); nonLetterboxRect.inset(letterboxInsets); outRegion.set(windowState.getBounds()); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 7ada2135212c..a8626dfe043a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -8071,6 +8071,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mSizeCompatScale = 1f; mSizeCompatBounds = null; mCompatDisplayInsets = null; + mLetterboxUiController.clearInheritedCompatDisplayInsets(); } @VisibleForTesting @@ -8386,6 +8387,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @NonNull Rect getScreenResolvedBounds() { + final Configuration resolvedConfig = getResolvedOverrideConfiguration(); + final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); + return mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds; + } + void recomputeConfiguration() { // We check if the current activity is transparent. In that case we need to // recomputeConfiguration of the first opaque activity beneath, to allow a @@ -8585,7 +8592,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolvedBounds.set(containingBounds); final float letterboxAspectRatioOverride = - mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(); + mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig); final float desiredAspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 41130812d658..aedd2c594b9d 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -200,6 +200,7 @@ public class DisplayPolicy { private final boolean mCarDockEnablesAccelerometer; private final boolean mDeskDockEnablesAccelerometer; + private final boolean mDeskDockRespectsNoSensorAndLockedWithoutAccelerometer; private final AccessibilityManager mAccessibilityManager; private final ImmersiveModeConfirmation mImmersiveModeConfirmation; private final ScreenshotHelper mScreenshotHelper; @@ -435,6 +436,8 @@ public class DisplayPolicy { final Resources r = mContext.getResources(); mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer); + mDeskDockRespectsNoSensorAndLockedWithoutAccelerometer = + r.getBoolean(R.bool.config_deskRespectsNoSensorAndLockedWithoutAccelerometer); mCanSystemBarsBeShownByUser = !r.getBoolean( R.bool.config_remoteInsetsControllerControlsSystemBars) || r.getBoolean( R.bool.config_remoteInsetsControllerSystemBarsCanBeShownByUserAction); @@ -755,6 +758,10 @@ public class DisplayPolicy { return mDeskDockEnablesAccelerometer; } + boolean isDeskDockRespectsNoSensorAndLockedWithoutAccelerometer() { + return mDeskDockRespectsNoSensorAndLockedWithoutAccelerometer; + } + public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) { mPersistentVrModeEnabled = persistentVrModeEnabled; } @@ -2662,6 +2669,8 @@ public class DisplayPolicy { pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); pw.print(" mDeskDockEnablesAccelerometer="); pw.println(mDeskDockEnablesAccelerometer); + pw.print(" mDeskDockRespectsNoSensorAndLockedWithoutAccelerometer="); + pw.println(mDeskDockRespectsNoSensorAndLockedWithoutAccelerometer); pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode)); pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState)); pw.print(prefix); pw.print("mAwake="); pw.print(mAwake); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index c0ed08d2607d..e9569d8cc064 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -87,6 +87,8 @@ import java.util.Set; */ public class DisplayRotation { private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM; + // Delay to avoid race between fold update and orientation update. + private static final int ORIENTATION_UPDATE_DELAY_MS = 800; // Delay in milliseconds when updating config due to folding events. This prevents // config changes and unexpected jumps while folding the device to closed state. @@ -1170,6 +1172,10 @@ public class DisplayRotation { mDisplayPolicy.isCarDockEnablesAccelerometer(); final boolean deskDockEnablesAccelerometer = mDisplayPolicy.isDeskDockEnablesAccelerometer(); + final boolean deskDockRespectsNoSensorAndLockedWithoutAccelerometer = + mDisplayPolicy.isDeskDockRespectsNoSensorAndLockedWithoutAccelerometer() + && (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED + || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); final int preferredRotation; if (!isDefaultDisplay) { @@ -1188,7 +1194,8 @@ public class DisplayRotation { } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) - && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { + && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0) + && !deskDockRespectsNoSensorAndLockedWithoutAccelerometer) { // Ignore sensor when in desk dock unless explicitly enabled. // This case can override the behavior of NOSENSOR, and can also // enable 180 degree rotation while docked. @@ -1738,15 +1745,15 @@ public class DisplayRotation { mDeviceState = newState; // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will // return true, so rotation is unlocked. - mService.updateRotation(false /* alwaysSendConfiguration */, - false /* forceRelayout */); } else { mInHalfFoldTransition = true; mDeviceState = newState; - // Tell the device to update its orientation. - mService.updateRotation(false /* alwaysSendConfiguration */, - false /* forceRelayout */); } + UiThread.getHandler().postDelayed( + () -> { + mService.updateRotation(false /* alwaysSendConfiguration */, + false /* forceRelayout */); + }, ORIENTATION_UPDATE_DELAY_MS); // Alert the activity of possible new bounds. UiThread.getHandler().removeCallbacks(mActivityBoundsUpdateCallback); UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback, diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 37cf5bc95a23..ec04894b1d42 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -184,6 +184,10 @@ final class LetterboxConfiguration { // portrait device orientation. private boolean mIsVerticalReachabilityEnabled; + // Whether book mode automatic horizontal reachability positioning is allowed for letterboxed + // fullscreen apps in landscape device orientation. + private boolean mIsAutomaticReachabilityInBookModeEnabled; + // Whether education is allowed for letterboxed fullscreen apps. private boolean mIsEducationEnabled; @@ -277,6 +281,8 @@ final class LetterboxConfiguration { R.bool.config_letterboxIsHorizontalReachabilityEnabled); mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsVerticalReachabilityEnabled); + mIsAutomaticReachabilityInBookModeEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled); mDefaultPositionForHorizontalReachability = readLetterboxHorizontalReachabilityPositionFromConfig(mContext, false); mDefaultPositionForVerticalReachability = @@ -681,6 +687,14 @@ final class LetterboxConfiguration { return mIsVerticalReachabilityEnabled; } + /* + * Whether automatic horizontal reachability repositioning in book mode is allowed for + * letterboxed fullscreen apps in landscape device orientation. + */ + boolean getIsAutomaticReachabilityInBookModeEnabled() { + return mIsAutomaticReachabilityInBookModeEnabled; + } + /** * Overrides whether horizontal reachability repositioning is allowed for letterboxed fullscreen * apps in landscape device orientation. @@ -698,6 +712,14 @@ final class LetterboxConfiguration { } /** + * Overrides whether automatic horizontal reachability repositioning in book mode is allowed for + * letterboxed fullscreen apps in landscape device orientation. + */ + void setIsAutomaticReachabilityInBookModeEnabled(boolean enabled) { + mIsAutomaticReachabilityInBookModeEnabled = enabled; + } + + /** * Resets whether horizontal reachability repositioning is allowed for letterboxed fullscreen * apps in landscape device orientation to * {@link R.bool.config_letterboxIsHorizontalReachabilityEnabled}. @@ -717,6 +739,16 @@ final class LetterboxConfiguration { R.bool.config_letterboxIsVerticalReachabilityEnabled); } + /** + * Resets whether automatic horizontal reachability repositioning in book mode is + * allowed for letterboxed fullscreen apps in landscape device orientation to + * {@link R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled}. + */ + void resetEnabledAutomaticReachabilityInBookMode() { + mIsAutomaticReachabilityInBookModeEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled); + } + /* * Gets default horizontal position of the letterboxed app window when horizontal reachability * is enabled. diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 6bffc4c95e64..001b0ca15997 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -38,6 +38,10 @@ import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.screenOrientationToString; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; +import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; +import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; @@ -188,7 +192,7 @@ final class LetterboxUiController { private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; @Configuration.Orientation - private int mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED; + private int mInheritedOrientation = ORIENTATION_UNDEFINED; // The app compat state for the opaque activity if any private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; @@ -323,6 +327,10 @@ final class LetterboxUiController { mLetterbox.destroy(); mLetterbox = null; } + if (mLetterboxConfigListener != null) { + mLetterboxConfigListener.onRemoved(); + mLetterboxConfigListener = null; + } } void onMovedToDisplay(int displayId) { @@ -749,6 +757,8 @@ final class LetterboxUiController { final Rect innerFrame = hasInheritedLetterboxBehavior() ? mActivityRecord.getBounds() : w.getFrame(); mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint); + // We need to notify Shell that letterbox position has changed. + mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); } else if (mLetterbox != null) { mLetterbox.hide(); } @@ -797,13 +807,18 @@ final class LetterboxUiController { float getHorizontalPositionMultiplier(Configuration parentConfiguration) { // Don't check resolved configuration because it may not be updated yet during // configuration change. - boolean bookMode = isDisplayFullScreenAndInPosture( - DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */); + boolean bookModeEnabled = isFullScreenAndBookModeEnabled(); return isHorizontalReachabilityEnabled(parentConfiguration) // Using the last global dynamic position to avoid "jumps" when moving // between apps or activities. - ? mLetterboxConfiguration.getHorizontalMultiplierForReachability(bookMode) - : mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier(bookMode); + ? mLetterboxConfiguration.getHorizontalMultiplierForReachability(bookModeEnabled) + : mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier(bookModeEnabled); + } + + private boolean isFullScreenAndBookModeEnabled() { + return isDisplayFullScreenAndInPosture( + DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */) + && mLetterboxConfiguration.getIsAutomaticReachabilityInBookModeEnabled(); } float getVerticalPositionMultiplier(Configuration parentConfiguration) { @@ -818,12 +833,14 @@ final class LetterboxUiController { : mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode); } - float getFixedOrientationLetterboxAspectRatio() { + float getFixedOrientationLetterboxAspectRatio(@NonNull Configuration parentConfiguration) { + // Don't resize to split screen size when half folded if letterbox position is centered return isDisplayFullScreenAndSeparatingHinge() - ? getSplitScreenAspectRatio() - : mActivityRecord.shouldCreateCompatDisplayInsets() - ? getDefaultMinAspectRatioForUnresizableApps() - : getDefaultMinAspectRatio(); + && getHorizontalPositionMultiplier(parentConfiguration) != 0.5f + ? getSplitScreenAspectRatio() + : mActivityRecord.shouldCreateCompatDisplayInsets() + ? getDefaultMinAspectRatioForUnresizableApps() + : getDefaultMinAspectRatio(); } private float getDefaultMinAspectRatioForUnresizableApps() { @@ -874,6 +891,20 @@ final class LetterboxUiController { return mActivityRecord.mWmService.mContext.getResources(); } + @LetterboxConfiguration.LetterboxVerticalReachabilityPosition + int getLetterboxPositionForVerticalReachability() { + final boolean isInFullScreenTabletopMode = isDisplayFullScreenAndSeparatingHinge(); + return mLetterboxConfiguration.getLetterboxPositionForVerticalReachability( + isInFullScreenTabletopMode); + } + + @LetterboxConfiguration.LetterboxHorizontalReachabilityPosition + int getLetterboxPositionForHorizontalReachability() { + final boolean isInFullScreenBookMode = isDisplayFullScreenAndSeparatingHinge(); + return mLetterboxConfiguration.getLetterboxPositionForHorizontalReachability( + isInFullScreenBookMode); + } + @VisibleForTesting void handleHorizontalDoubleTap(int x) { if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) { @@ -885,7 +916,8 @@ final class LetterboxUiController { return; } - boolean isInFullScreenBookMode = isDisplayFullScreenAndSeparatingHinge(); + boolean isInFullScreenBookMode = isDisplayFullScreenAndSeparatingHinge() + && mLetterboxConfiguration.getIsAutomaticReachabilityInBookModeEnabled(); int letterboxPositionForHorizontalReachability = mLetterboxConfiguration .getLetterboxPositionForHorizontalReachability(isInFullScreenBookMode); if (mLetterbox.getInnerFrame().left > x) { @@ -965,6 +997,8 @@ final class LetterboxUiController { * </ul> */ private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) { + // Use screen resolved bounds which uses resolved bounds or size compat bounds + // as activity bounds can sometimes be empty return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN @@ -972,7 +1006,7 @@ final class LetterboxUiController { && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT) // Check whether the activity fills the parent vertically. && parentConfiguration.windowConfiguration.getAppBounds().height() - <= mActivityRecord.getBounds().height(); + <= mActivityRecord.getScreenResolvedBounds().height(); } @VisibleForTesting @@ -980,6 +1014,10 @@ final class LetterboxUiController { return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); } + boolean isLetterboxDoubleTapEducationEnabled() { + return isHorizontalReachabilityEnabled() || isVerticalReachabilityEnabled(); + } + /** * Whether vertical reachability is enabled for an activity in the current configuration. * @@ -992,6 +1030,8 @@ final class LetterboxUiController { * </ul> */ private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) { + // Use screen resolved bounds which uses resolved bounds or size compat bounds + // as activity bounds can sometimes be empty return mLetterboxConfiguration.getIsVerticalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN @@ -999,7 +1039,7 @@ final class LetterboxUiController { && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE) // Check whether the activity fills the parent horizontally. && parentConfiguration.windowConfiguration.getBounds().width() - == mActivityRecord.getBounds().width(); + == mActivityRecord.getScreenResolvedBounds().width(); } @VisibleForTesting @@ -1415,7 +1455,8 @@ final class LetterboxUiController { mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( mActivityRecord, firstOpaqueActivityBeneath, (opaqueConfig, transparentConfig) -> { - final Configuration mutatedConfiguration = new Configuration(); + final Configuration mutatedConfiguration = + fromOriginalTranslucentConfig(transparentConfig); final Rect parentBounds = parent.getWindowConfiguration().getBounds(); final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds(); final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); @@ -1477,6 +1518,10 @@ final class LetterboxUiController { return mInheritedCompatDisplayInsets; } + void clearInheritedCompatDisplayInsets() { + mInheritedCompatDisplayInsets = null; + } + /** * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque * activity beneath using the given consumer and returns {@code true}. @@ -1503,6 +1548,22 @@ final class LetterboxUiController { true /* traverseTopToBottom */)); } + // When overriding translucent activities configuration we need to keep some of the + // original properties + private Configuration fromOriginalTranslucentConfig(Configuration translucentConfig) { + final Configuration configuration = new Configuration(translucentConfig); + // The values for the following properties will be defined during the configuration + // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the + // properties inherited from the first not finishing opaque activity beneath. + configuration.orientation = ORIENTATION_UNDEFINED; + configuration.screenWidthDp = configuration.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; + configuration.screenHeightDp = + configuration.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; + configuration.smallestScreenWidthDp = + configuration.compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; + return configuration; + } + private void inheritConfiguration(ActivityRecord firstOpaque) { // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities // which are not already providing one (e.g. permission dialogs) and presumably also @@ -1522,7 +1583,7 @@ final class LetterboxUiController { mLetterboxConfigListener = null; mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; - mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED; + mInheritedOrientation = ORIENTATION_UNDEFINED; mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; mInheritedCompatDisplayInsets = null; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 18d6dea927c1..aed876d4a5b3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3479,6 +3479,25 @@ class Task extends TaskFragment { info.isFocused = isFocused(); info.isVisible = hasVisibleChildren(); info.isSleeping = shouldSleepActivities(); + info.isLetterboxDoubleTapEnabled = top != null + && top.mLetterboxUiController.isLetterboxDoubleTapEducationEnabled(); + info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET; + info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET; + info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET; + info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET; + if (info.isLetterboxDoubleTapEnabled) { + info.topActivityLetterboxWidth = top.getBounds().width(); + info.topActivityLetterboxHeight = top.getBounds().height(); + if (info.topActivityLetterboxWidth < info.topActivityLetterboxHeight) { + // Pillarboxed + info.topActivityLetterboxHorizontalPosition = + top.mLetterboxUiController.getLetterboxPositionForHorizontalReachability(); + } else { + // Letterboxed + info.topActivityLetterboxVerticalPosition = + top.mLetterboxUiController.getLetterboxPositionForVerticalReachability(); + } + } } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index a06d84c44f17..adf5310bb97c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -942,6 +942,10 @@ public class WindowManagerShellCommand extends ShellCommand { runSetBooleanFlag(pw, mLetterboxConfiguration ::setIsVerticalReachabilityEnabled); break; + case "--isAutomaticReachabilityInBookModeEnabled": + runSetBooleanFlag(pw, mLetterboxConfiguration + ::setIsAutomaticReachabilityInBookModeEnabled); + break; case "--defaultPositionForHorizontalReachability": runSetLetterboxDefaultPositionForHorizontalReachability(pw); break; @@ -1144,6 +1148,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); + mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode(); mLetterboxConfiguration.resetDefaultPositionForHorizontalReachability(); mLetterboxConfiguration.resetDefaultPositionForVerticalReachability(); mLetterboxConfiguration.resetIsEducationEnabled(); @@ -1179,6 +1184,8 @@ public class WindowManagerShellCommand extends ShellCommand { + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()); pw.println("Is vertical reachability enabled: " + mLetterboxConfiguration.getIsVerticalReachabilityEnabled()); + pw.println("Is automatic reachability in book mode enabled: " + + mLetterboxConfiguration.getIsAutomaticReachabilityInBookModeEnabled()); pw.println("Default position for horizontal reachability: " + LetterboxConfiguration.letterboxHorizontalReachabilityPositionToString( mLetterboxConfiguration.getDefaultPositionForHorizontalReachability())); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 30ec1632a622..881d1b3d581c 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -708,6 +709,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { } @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); Bundle options = createOptionsWithAccountName( diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 99a361c03a2a..874846d333cf 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4359,6 +4359,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); + assertFalse(posted.getNotification().extras + .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); + assertFalse(posted.getNotification().extras + .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); + } + + @Test + public void testCustomMediaStyleRemote_noPermission() throws RemoteException { + String deviceName = "device"; + when(mPackageManager.checkPermission( + eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt())) + .thenReturn(PERMISSION_DENIED); + Notification.DecoratedMediaCustomViewStyle style = + new Notification.DecoratedMediaCustomViewStyle(); + style.setRemotePlaybackInfo(deviceName, 0, null); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, + "testCustomMediaStyleRemoteNoPermission", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + NotificationRecord posted = mService.findNotificationLocked( + PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + + assertFalse(posted.getNotification().extras + .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); + assertFalse(posted.getNotification().extras + .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); + assertFalse(posted.getNotification().extras + .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index ed2b0a36cd5c..26d2a46f768a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -748,7 +750,7 @@ public class DisplayRotationTests { // ... until half-fold mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED); assertTrue(waitForUiHandler()); - verify(sMockWm).updateRotation(false, false); + verify(sMockWm).updateRotation(anyBoolean(), anyBoolean()); assertTrue(waitForUiHandler()); assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation( SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); @@ -756,7 +758,7 @@ public class DisplayRotationTests { // ... then transition back to flat mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN); assertTrue(waitForUiHandler()); - verify(sMockWm, atLeast(1)).updateRotation(false, false); + verify(sMockWm, atLeast(1)).updateRotation(anyBoolean(), anyBoolean()); assertTrue(waitForUiHandler()); assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); @@ -824,6 +826,23 @@ public class DisplayRotationTests { } @Test + public void testIgnoresDeskDockRotation_whenNoSensorAndLockedRespected() throws Exception { + mBuilder.setDeskDockRotation(Surface.ROTATION_270).build(); + when(mMockDisplayPolicy.isDeskDockRespectsNoSensorAndLockedWithoutAccelerometer()) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_LOCKED, Surface.ROTATION_90)); + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_NOSENSOR, Surface.ROTATION_90)); + } + + @Test public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest() throws Exception { mBuilder.build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 75d65b3a3f8f..4a3f46a567d1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; @@ -51,6 +52,7 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -63,6 +65,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -170,6 +174,26 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + + translucentActivity.setState(DESTROYED, "testing"); + translucentActivity.removeImmediately(); + + assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); + } + + @Test public void testHorizontalReachabilityEnabledForTranslucentActivities() { setUpDisplaySizeWithApp(2500, 1000); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -349,6 +373,33 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testApplyStrategyToTranslucentActivitiesRetainsWindowConfigurationProperties() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2000, 1000); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + // Translucent Activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + WindowConfiguration translucentWinConf = translucentActivity.getWindowConfiguration(); + translucentActivity.setActivityType(ACTIVITY_TYPE_STANDARD); + translucentActivity.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + translucentActivity.setDisplayWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + translucentActivity.setAlwaysOnTop(true); + + mTask.addChild(translucentActivity); + + // We check the WIndowConfiguration properties + translucentWinConf = translucentActivity.getWindowConfiguration(); + assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getDisplayWindowingMode()); + assertTrue(translucentWinConf.isAlwaysOnTop()); + } + + @Test public void testApplyStrategyToMultipleTranslucentActivities() { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2000, 1000); @@ -397,7 +448,7 @@ public class SizeCompatTests extends WindowTestsBase { .setLaunchedFromUid(mActivity.getUid()) .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .build(); - doReturn(true).when(translucentActivity).fillsParent(); + doReturn(false).when(translucentActivity).fillsParent(); mTask.addChild(translucentActivity); // It should not be in SCM assertFalse(translucentActivity.inSizeCompatMode()); @@ -458,6 +509,33 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() { + mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + // Rotate to put activity in size compat mode. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + assertTrue(mActivity.inSizeCompatMode()); + + // We launch a transparent activity + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setLaunchedFromUid(mActivity.getUid()) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .build(); + doReturn(false).when(translucentActivity).fillsParent(); + mTask.addChild(translucentActivity); + + // The transparent activity inherits the compat display insets of the opaque activity + // beneath it + assertNotNull(translucentActivity.getCompatDisplayInsets()); + + // Clearing SCM should also clear the inherited compat display insets + translucentActivity.clearSizeCompatMode(); + assertNull(translucentActivity.getCompatDisplayInsets()); + } + + @Test public void testRestartProcessIfVisible() { setUpDisplaySizeWithApp(1000, 2500); doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity); @@ -3049,12 +3127,44 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Vertical reachability is disabled because the app does not match parent width - assertNotEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds() - .width()); + assertNotEquals(mActivity.getScreenResolvedBounds().width(), + mActivity.mDisplayContent.getBounds().width()); assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled()); } @Test + public void testIsVerticalReachabilityEnabled_emptyBounds_true() { + setUpDisplaySizeWithApp(/* dw */ 1000, /* dh */ 2800); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + + // Set up activity with empty bounds to mock loading of app + mActivity.getWindowConfiguration().setBounds(null); + assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds()); + + // Vertical reachability is still enabled as resolved bounds is not empty + assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled()); + } + + @Test + public void testIsHorizontalReachabilityEnabled_emptyBounds_true() { + setUpDisplaySizeWithApp(/* dw */ 2800, /* dh */ 1000); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + + // Set up activity with empty bounds to mock loading of app + mActivity.getWindowConfiguration().setBounds(null); + assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds()); + + // Horizontal reachability is still enabled as resolved bounds is not empty + assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled()); + } + + @Test public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() { setUpDisplaySizeWithApp(2800, 1000); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -3070,8 +3180,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Horizontal reachability is disabled because the app does not match parent height - assertNotEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds() - .height()); + assertNotEquals(mActivity.getScreenResolvedBounds().height(), + mActivity.mDisplayContent.getBounds().height()); assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled()); } @@ -3091,8 +3201,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Horizontal reachability is enabled because the app matches parent height - assertEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds() - .height()); + assertEquals(mActivity.getScreenResolvedBounds().height(), + mActivity.mDisplayContent.getBounds().height()); assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled()); } @@ -3112,7 +3222,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Vertical reachability is enabled because the app matches parent width - assertEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds().width()); + assertEquals(mActivity.getScreenResolvedBounds().width(), + mActivity.mDisplayContent.getBounds().width()); assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled()); } @@ -3636,7 +3747,6 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testUpdateResolvedBoundsVerticalPosition_tabletop() { - // Set up a display in portrait with a fixed-orientation LANDSCAPE app setUpDisplaySizeWithApp(1400, 2800); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -3658,16 +3768,15 @@ public class SizeCompatTests extends WindowTestsBase { setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */); assertEquals(letterboxNoFold, mActivity.getBounds()); - } @Test - public void testUpdateResolvedBoundsHorizontalPosition_book() { - + public void testUpdateResolvedBoundsHorizontalPosition_bookModeEnabled() { // Set up a display in landscape with a fixed-orientation PORTRAIT app setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( + mWm.mLetterboxConfiguration.setIsAutomaticReachabilityInBookModeEnabled(true); + mWm.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( 1.0f /*letterboxVerticalPositionMultiplier*/); prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); @@ -3685,7 +3794,28 @@ public class SizeCompatTests extends WindowTestsBase { setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */); assertEquals(letterboxNoFold, mActivity.getBounds()); + } + @Test + public void testUpdateResolvedBoundsHorizontalPosition_bookModeDisabled_centered() { + // Set up a display in landscape with a fixed-orientation PORTRAIT app + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f); + prepareUnresizable(mActivity, 1.75f, SCREEN_ORIENTATION_PORTRAIT); + + Rect letterboxNoFold = new Rect(1000, 0, 1800, 1400); + assertEquals(letterboxNoFold, mActivity.getBounds()); + + // Make the activity full-screen + mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + // Stay centered and bounds don't change + setFoldablePosture(true /* isHalfFolded */, false /* isTabletop */); + assertEquals(letterboxNoFold, mActivity.getBounds()); + + setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */); + assertEquals(letterboxNoFold, mActivity.getBounds()); } private void setFoldablePosture(ActivityRecord activity, boolean isHalfFolded, |